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 with ksort() then compares strictly, so key→value binding and assertSame() type strictness are preserved (unlike assertEqualsCanonicalizing(), which sorts by value).
  • Adds a PHPStan rule, ComponentInputsComparisonMustIgnoreKeyOrderRule, that flags order-sensitive assertSame() comparisons of ComponentTreeItem::getInputs() and points to the helper — modeled on the existing KernelTestsMustUseAssertEntityIsValidRule. Detection is type-based (so the unrelated AdapterInterface::getInputs() doesn't trigger it) and matches only a getInputs() call that is directly an assertSame() argument (so getInputs()['key'] extraction and inputs vs inputs_resolved comparisons are not flagged).
  • Converts the input comparisons in ComponentTreeFieldSymmetricalTranslationSynchronizerTest to the helper.

Scope is kernel tests, matching the existing rule's precedent.

Testing steps

  • ComponentTreeFieldSymmetricalTranslationSynchronizerTest passes on all four backends; the order-sensitivity that caused MySQL/PostgreSQL failures is removed.
  • composer phpstan passes — the new rule is registered and has no pre-existing violations.
  • Temporarily change one assertSameInputs(...) back to assertSame(...)composer phpstan reports canvas.componentInputsComparisonMustIgnoreKeyOrder on 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 an array_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.

Merge request reports

Loading