[CodeQuality] Add LoadValidatorMetadataToAttributeRector, deprecate the annotation variant - #988
Merged
Merged
Conversation
…he annotation variant The validator loads constraints from PHP attributes since Symfony 5.2, and Doctrine annotations are deprecated since Symfony 6.4, so moving loadValidatorMetadata() to a docblock produces a form that is on its way out. The new rule writes attributes instead, converting the options array to named arguments. Each placement is checked against the constraint's #[Attribute] target flags first, as only 10 of the 70 core constraints allow TARGET_CLASS, and constraints written before 5.2 carry no #[Attribute] at all. Constraints that cannot be placed stay in loadValidatorMetadata(). The rule is bound to symfony/validator >= 5.2.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
LoadValidatorMetadataToAnnotationRectormovesloadValidatorMetadata()into a docblock. Per the vendoredsymfony/validatorCHANGELOG:AnnotationLoader→AttributeLoaderSo the rule emits a form Symfony deprecated two majors ago. This replaces it with an attribute-writing rule.
Before / after
final class SomeClass { - private $city; - - public static function loadValidatorMetadata(ClassMetadata $metadata): void - { - $metadata->addPropertyConstraint('city', new Assert\NotBlank([ - 'message' => 'City can\'t be blank.', - ])); - } + #[Assert\NotBlank(message: 'City can\'t be blank.')] + private $city; }Getter constraints land on the getter, class constraints on the class, and the
loadValidatorMetadata()method is dropped once every statement has moved.Placement is checked, not assumed
Presence of
#[Attribute]alone is not enough — the target flags differ per constraint, and the rule writes to three different places:Only 10 of the 70 constraints in
symfony/validatorallowTARGET_CLASS. Writing#[Assert\NotBlank]on a class would crash the validator on load, soConstraintAttributeTargetAnalyzerchecks the flags for each placement:The same check covers custom constraints written before 5.2, which carry no
#[Attribute]and would fail with "Attempting to use non-attribute class" once the loader instantiates them:Anything that cannot be placed stays in
loadValidatorMetadata(), so a partial move keeps the method:final class PartialMoveKeepsClassMethod { + #[\Symfony\Component\Validator\Constraints\NotBlank] private $city; public static function loadValidatorMetadata(ClassMetadata $metadata): void { - $metadata->addPropertyConstraint('city', new Assert\NotBlank()); $metadata->addPropertyConstraint('unknownProperty', new Assert\NotBlank()); } }Named arguments
The options array is emitted as named arguments, matching what
ConstraintOptionsToNamedArgumentsRector(symfony73-validator) produces fornewexpressions. That rule operates onNew_and cannot reach attribute args, so without this the new rule would emit a form the repo has another rule to eliminate.A non-string key or a list (
new Assert\Callback(['validateCity'])) keeps the array verbatim — there the array is the value, not options.Version binding
ComposerPackageConstraintInterface→symfony/validator >= 5.2. Registered insymfony-code-qualityonly, replacing the old rule. The constraint gates the rule at runtime wherever it is registered, socomposer-basedis left untouched.Deprecation
LoadValidatorMetadataToAnnotationRectorkeeps its class andRuleDefinition, implementsDeprecatedInterface,refactor()throws pointing at the new rule.Its support classes had no other callers and are removed:
ClassAnnotationAssertResolver,MethodCallAnnotationAssertResolver,PropertyAnnotationAssertResolver,DoctrineAnnotationFromNewFactory,DoctrineAnnotationKeyToValuesResolver,StringValueQuoteWrapper,ClassMethodAndAnnotation,PropertyAndAnnotation.StmtMethodCallMatcheris reused by the new resolver and moved toNodeAnalyzer/ValidatorAssert/.AnnotationOrAttributeValueResolverstays — still used byThisRenderFactory.