Issue #3591726: Compare component inputs order-independently in tests and enforce it via PHPStan
Closes #3591726
What this MR does
Makes component inputs comparisons in tests order-independent, since input key order is database-backend-dependent (MySQL json / PostgreSQL jsonb reorder object keys; MariaDB / SQLite preserve them) and not semantically meaningful.
- Adds
CanvasKernelTestBase::assertSameInputs()— canonicalizes key order withksort()then compares strictly, so key→value binding andassertSame()type strictness are preserved (unlikeassertEqualsCanonicalizing(), which sorts by value). - Adds a PHPStan rule,
ComponentInputsComparisonMustIgnoreKeyOrderRule, that flags order-sensitiveassertSame()comparisons ofComponentTreeItem::getInputs()and points to the helper — modeled on the existingKernelTestsMustUseAssertEntityIsValidRule. Detection is type-based (so the unrelatedAdapterInterface::getInputs()doesn't trigger it) and matches only agetInputs()call that is directly anassertSame()argument (sogetInputs()['key']extraction andinputsvsinputs_resolvedcomparisons are not flagged). - Converts the input comparisons in
ComponentTreeFieldSymmetricalTranslationSynchronizerTestto the helper.
Scope is kernel tests, matching the existing rule's precedent.
Testing steps
-
ComponentTreeFieldSymmetricalTranslationSynchronizerTestpasses on all four backends; the order-sensitivity that caused MySQL/PostgreSQL failures is removed. -
composer phpstanpasses — the new rule is registered and has no pre-existing violations. - Temporarily change one
assertSameInputs(...)back toassertSame(...)→composer phpstanreportscanvas.componentInputsComparisonMustIgnoreKeyOrderon that line; revert.
Notes for reviewers
- No production code changes — test infrastructure and dev tooling only.
- Two alternatives were considered and rejected: normalizing key order on read (couples a low-level getter to config-schema resolution and adds cost to a hot path) and changing the storage column away from native JSON (storage migration, not viable here).
- Known limitations (intentional, matching the sibling rules' scope): the rule covers kernel tests only — functional tests, e.g.
CollapseComponentInputsUpdateTest, are out of scope — and it catches the direct comparison shape, not indirect ones (inputs stashed in a variable, or returned from anarray_map()closure).
AI usage disclosure
This change was produced with AI assistance (Claude). Per Drupal's policy on the use of AI, all code and prose were reviewed by a human contributor before submission.