Add rector for Symfony ConstraintValidatorInterface type declarations (Drupal 12 / Symfony 8)
## Problem Symfony tightened the signatures of `ConstraintValidatorInterface` across the Symfony 7 → 8 bump that Drupal 11 → 12 rides along with: - **Symfony 7.0** (Drupal 11.0): `validate()`'s first parameter became `mixed $value`. - **Symfony 8.0** (Drupal 12.0): native `: void` return types were added to `validate()` and `initialize()`: ```php public function initialize(ExecutionContextInterface $context): void; public function validate(mixed $value, Constraint $constraint): void; ``` Contrib modules that implement the interface — directly, via the abstract `ConstraintValidator`, or via the legacy `extends Constraint implements ConstraintValidatorInterface` self-validating pattern — must add these declarations to be Drupal 12 compatible. This surfaced in redirect's automated Drupal 12 work ([project/redirect!200](https://git.drupalcode.org/project/redirect/-/merge_requests/200)), which changed exactly: ```php // UniqueHashValidator (extends ConstraintValidator) - public function validate($redirect, Constraint $constraint) { + public function validate(mixed $redirect, Constraint $constraint): void { // SourceLinkTypeConstraint (extends Constraint implements ConstraintValidatorInterface) - public function initialize(ExecutionContextInterface $context) { + public function initialize(ExecutionContextInterface $context): void { - public function validate($value, Constraint $constraint) { + public function validate(mixed $value, Constraint $constraint): void { ``` It affects many modules and is not yet handled by any automated tool. ## Backward compatibility The transform is **backward compatible on every supported Drupal version**, so no `DeprecationHelper` / version gate is needed: a child may declare types its interface does not (Drupal core itself ships `validate(mixed $value, Constraint $constraint): void` while running on a Symfony 7.4 interface that has no native `: void`). Widening a parameter to `mixed` is legal contravariance. ## Proposed rector `AddSymfonyConstraintValidatorTypeDeclarationsRector` (extends `AbstractRector`, no configuration, no version gate): - Matches `Class_` nodes that are a `Symfony\Component\Validator\ConstraintValidatorInterface`. - On `validate()`: adds `mixed` to param 0 (when untyped) and `: void` return (when absent). - On `initialize()`: adds `: void` return (when absent). - Idempotent — no-op when the types are already present. Deliberately **not** matching `Constraint` on its own: its methods (`validatedBy()`, `getTargets()`, `getDefaultOption()`, `getRequiredOptions()`) have been typed since Symfony 7.4, so any untyped override already fatals on Drupal 11 — there are no real targets there. ## Placement Registered in the Drupal 11 floor set (fires on `drupal/core ^11.0`, i.e. every D11 install, so modules can adopt the types while still supporting D11) **and** a new Drupal 12 set (fires on `^12.0`). Same rule in both; idempotent so double coverage is harmless.
issue