Convert Constraint plugin discovery to attributes

Created on 13 February 2024, 10 months ago
Updated 17 March 2024, 9 months ago

Problem/Motivation

In πŸ“Œ Use PHP attributes instead of doctrine annotations Fixed we added support for attribute based plugin discovery.
As part of that issue we converted block and action plugins.

This issue is to convert \Drupal\Core\Validation\Annotation\Constraintplugins to use Attributes.

Proposed resolution

  1. Add a class to represent the new Attribute - Example
  2. Update the plugin manager constructor to include both the attribute and annotation class names - example
  3. Convert all plugins that use the annotation to use the new attribute - example

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

πŸ“Œ Task
Status

Fixed

Version

10.3 ✨

Component
BaseΒ  β†’

Last updated about 14 hours ago

Created by

πŸ‡¦πŸ‡ΊAustralia larowlan πŸ‡¦πŸ‡ΊπŸ.au GMT+10

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @larowlan
  • First commit to issue fork.
  • Pipeline finished with Failed
    9 months ago
    Total: 184s
    #102731
  • Pipeline finished with Failed
    9 months ago
    Total: 184s
    #102735
  • Pipeline finished with Failed
    9 months ago
    Total: 595s
    #102741
  • Pipeline finished with Failed
    9 months ago
    Total: 184s
    #102796
  • Pipeline finished with Failed
    9 months ago
    Total: 603s
    #102799
  • Pipeline finished with Failed
    9 months ago
    Total: 569s
    #102815
  • Pipeline finished with Success
    9 months ago
    Total: 557s
    #102826
  • Status changed to Needs review 9 months ago
  • I was implementing a custom validation constraint and noticed that Constraint plugins hadn't yet been converted to attributes, so I picked this up. I underestimated level of effort by quite a bit, first in the number of plugins there are, and second the Symfony deprecation notices. Anyway, tests are green, so I assume I caught everything.

  • Pipeline finished with Failed
    9 months ago
    Total: 163s
    #103093
  • Pipeline finished with Success
    9 months ago
    Total: 536s
    #103098
  • Decided I didn't like aliasing Drupal\Core\Validation\Attribute\Constraint as ConstraintAttribute, so switched to aliasing Symfony\Component\Validator\Constraint as SymfonyConstraint.

  • Pipeline finished with Success
    9 months ago
    Total: 478s
    #103746
  • Status changed to Needs work 9 months ago
  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Only did a partial review but tagged a few out of scope changes.

  • Status changed to Needs review 9 months ago
  • Method return typehints were added to address tests failing for Symfony deprecations, so not OOS.

  • Status changed to RTBC 9 months ago
  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Gotcha,

    Then in that case searched for @Constraint and all instances appear to be replaced.

  • πŸ‡¬πŸ‡§United Kingdom longwave UK

    To backport this to 10.3.x I am not sure we can add the return types, we might have to add them to the baseline or ignore them instead.

  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Could it be a 11.x only?

  • Not sure if this suggests whether more typehints can be backported, but there are existing typehints on some constraint plugins: Drupal\Core\Entity\Plugin\Validation\Constraint\BundleConstraint
    Drupal\Core\Plugin\Plugin\Validation\Constraint\PluginExistsConstraint

    https://git.drupalcode.org/project/drupal/-/blob/11.x/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/BundleConstraint.php?ref_type=heads#L51
    https://git.drupalcode.org/project/drupal/-/blob/11.x/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/BundleConstraint.php?ref_type=heads#L58

  • The alternate approach could be to do something like this:
    ```
    /**
    * {@inheritdoc}
    *
    * phpcs:ignore Drupal.Commenting.FunctionComment.MissingReturnComment
    * @return string
    */
    public function validatedBy() {
    ```

  • Status changed to Needs work 9 months ago
  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    Why is this issue fixing deprecations as well. I feel this should only be doing the attribute conversion. The rest of the changes belong in a separate issue. - no?

  • Status changed to Needs review 9 months ago
  • @alexpott - Tests were failing without fixing the typehints. https://git.drupalcode.org/issue/drupal-3420990/-/jobs/893451

    ---- Drupal\Tests\Core\TypedData\RecursiveContextualValidatorTest ----
    Status Group Filename Line Function
    --------------------------------------------------------------------------------
    Fail Other phpunit-727.xml 0 Drupal\Tests\Core\TypedData\Recursi
    PHPUnit Test failed to complete; Error: PHPUnit 9.6.15 by Sebastian
    Bergmann and contributors.

    Testing Drupal\Tests\Core\TypedData\RecursiveContextualValidatorTest
    ............... 15 / 15
    (100%)

    Time: 00:00.179, Memory: 6.00 MB

    OK (15 tests, 40 assertions)

    Remaining self deprecation notices (11)

    1x: Method "Symfony\Component\Validator\Constraint::getDefaultOption()"
    might add "?string" as a native return type declaration in the future. Do
    the same in child class
    "Drupal\Core\Validation\Plugin\Validation\Constraint\ValidKeysConstraint"
    now to avoid errors or add an explicit @return annotation to suppress this
    message.
    1x in
    RecursiveContextualValidatorTest::testBasicValidateWithoutConstraints from
    Drupal\Tests\Core\TypedData

    1x: Method "Symfony\Component\Validator\Constraint::getRequiredOptions()"
    might add "array" as a native return type declaration in the future. Do the
    same in child class
    "Drupal\Core\Validation\Plugin\Validation\Constraint\ValidKeysConstraint"
    now to avoid errors or add an explicit @return annotation to suppress this
    message.
    1x in
    RecursiveContextualValidatorTest::testBasicValidateWithoutConstraints from
    Drupal\Tests\Core\TypedData

    1x: Method "Symfony\Component\Validator\Constraint::validatedBy()" might
    add "string" as a native return type declaration in the future. Do the same
    in child class
    "Drupal\Core\Validation\Plugin\Validation\Constraint\UuidConstraint" now to
    avoid errors or add an explicit @return annotation to suppress this
    message.
    1x in
    RecursiveContextualValidatorTest::testBasicValidateWithoutConstraints from
    Drupal\Tests\Core\TypedData

    1x: Method "Symfony\Component\Validator\Constraint::validatedBy()" might
    add "string" as a native return type declaration in the future. Do the same
    in child class
    "Drupal\Core\Validation\Plugin\Validation\Constraint\RegexConstraint" now
    to avoid errors or add an explicit @return annotation to suppress this
    message.
    1x in
    RecursiveContextualValidatorTest::testBasicValidateWithoutConstraints from
    Drupal\Tests\Core\TypedData

    1x: Method "Symfony\Component\Validator\Constraint::validatedBy()" might
    add "string" as a native return type declaration in the future. Do the same
    in child class
    "Drupal\Core\Validation\Plugin\Validation\Constraint\LengthConstraint" now
    to avoid errors or add an explicit @return annotation to suppress this
    message.
    1x in
    RecursiveContextualValidatorTest::testBasicValidateWithoutConstraints from
    Drupal\Tests\Core\TypedData

    1x: Method "Symfony\Component\Validator\Constraint::getDefaultOption()"
    might add "?string" as a native return type declaration in the future. Do
    the same in child class
    "Drupal\Core\Validation\Plugin\Validation\Constraint\EntityBundleExistsConstraint"
    now to avoid errors or add an explicit @return annotation to suppress this
    message.
    1x in
    RecursiveContextualValidatorTest::testBasicValidateWithoutConstraints from
    Drupal\Tests\Core\TypedData

    1x: Method "Symfony\Component\Validator\Constraint::getRequiredOptions()"
    might add "array" as a native return type declaration in the future. Do the
    same in child class
    "Drupal\Core\Validation\Plugin\Validation\Constraint\EntityBundleExistsConstraint"
    now to avoid errors or add an explicit @return annotation to suppress this
    message.
    1x in
    RecursiveContextualValidatorTest::testBasicValidateWithoutConstraints from
    Drupal\Tests\Core\TypedData
    1x: Method "Symfony\Component\Validator\Constraint::validatedBy()" might
    add "string" as a native return type declaration in the future. Do the same
    in child class
    "Drupal\Core\Validation\Plugin\Validation\Constraint\EmailConstraint" now
    to avoid errors or add an explicit @return annotation to suppress this
    message.
    1x in
    RecursiveContextualValidatorTest::testBasicValidateWithoutConstraints from
    Drupal\Tests\Core\TypedData
    1x: Method "Symfony\Component\Validator\Constraint::validatedBy()" might
    add "string" as a native return type declaration in the future. Do the same
    in child class
    "Drupal\Core\Validation\Plugin\Validation\Constraint\CountConstraint" now
    to avoid errors or add an explicit @return annotation to suppress this
    message.
    1x in
    RecursiveContextualValidatorTest::testBasicValidateWithoutConstraints from
    Drupal\Tests\Core\TypedData
    1x: Method "Symfony\Component\Validator\Constraint::getDefaultOption()"
    might add "?string" as a native return type declaration in the future. Do
    the same in child class
    "Drupal\Core\Validation\Plugin\Validation\Constraint\ComplexDataConstraint"
    now to avoid errors or add an explicit @return annotation to suppress this
    message.
    1x in
    RecursiveContextualValidatorTest::testBasicValidateWithoutConstraints from
    Drupal\Tests\Core\TypedData
    1x: Method "Symfony\Component\Validator\Constraint::getRequiredOptions()"
    might add "array" as a native return type declaration in the future. Do the
    same in child class
    "Drupal\Core\Validation\Plugin\Validation\Constraint\ComplexDataConstraint"
    now to avoid errors or add an explicit @return annotation to suppress this
    message.
    1x in
    RecursiveContextualValidatorTest::testBasicValidateWithoutConstraints from
    Drupal\Tests\Core\TypedData
    Remaining direct deprecation notices (10)
    1x: The "Symfony\Component\Validator\Constraints\Regex::$message"
    property is considered final. You should not override it in
    "Drupal\Core\Validation\Plugin\Validation\Constraint\RegexConstraint".
    1x in
    RecursiveContextualValidatorTest::testBasicValidateWithoutConstraints from
    Drupal\Tests\Core\TypedData
    1x: The "Symfony\Component\Validator\Constraints\Length::$maxMessage"
    property is considered final. You should not override it in
    "Drupal\Core\Validation\Plugin\Validation\Constraint\LengthConstraint".
    1x in
    RecursiveContextualValidatorTest::testBasicValidateWithoutConstraints from
    Drupal\Tests\Core\TypedData
    1x: The "Symfony\Component\Validator\Constraints\Length::$minMessage"
    property is considered final. You should not override it in
    "Drupal\Core\Validation\Plugin\Validation\Constraint\LengthConstraint".
    1x in
    RecursiveContextualValidatorTest::testBasicValidateWithoutConstraints from
    Drupal\Tests\Core\TypedData
    1x: The "Symfony\Component\Validator\Constraints\Length::$exactMessage"
    property is considered final. You should not override it in
    "Drupal\Core\Validation\Plugin\Validation\Constraint\LengthConstraint".
    1x in
    RecursiveContextualValidatorTest::testBasicValidateWithoutConstraints from
    Drupal\Tests\Core\TypedData
    1x: The "Symfony\Component\Validator\Constraints\Count::$minMessage"
    property is considered final. You should not override it in
    "Drupal\Core\Validation\Plugin\Validation\Constraint\CountConstraint".
    1x in
    RecursiveContextualValidatorTest::testBasicValidateWithoutConstraints from
    Drupal\Tests\Core\TypedData
    1x: The "Symfony\Component\Validator\Constraints\Count::$maxMessage"
    property is considered final. You should not override it in
    "Drupal\Core\Validation\Plugin\Validation\Constraint\CountConstraint".
    1x in
    RecursiveContextualValidatorTest::testBasicValidateWithoutConstraints from
    Drupal\Tests\Core\TypedData
    1x: The "Symfony\Component\Validator\Constraints\Count::$exactMessage"
    property is considered final. You should not override it in
    "Drupal\Core\Validation\Plugin\Validation\Constraint\CountConstraint".
    1x in
    RecursiveContextualValidatorTest::testBasicValidateWithoutConstraints from
    Drupal\Tests\Core\TypedData
    1x: The "Symfony\Component\Validator\Constraints\Choice::$strict"
    property is considered final. You should not override it in
    "Drupal\Core\Validation\Plugin\Validation\Constraint\AllowedValuesConstraint".
    1x in
    RecursiveContextualValidatorTest::testBasicValidateWithoutConstraints from
    Drupal\Tests\Core\TypedData
    1x: The "Symfony\Component\Validator\Constraints\Choice::$minMessage"
    property is considered final. You should not override it in
    "Drupal\Core\Validation\Plugin\Validation\Constraint\AllowedValuesConstraint".
    1x in
    RecursiveContextualValidatorTest::testBasicValidateWithoutConstraints from
    Drupal\Tests\Core\TypedData
    1x: The "Symfony\Component\Validator\Constraints\Choice::$maxMessage"
    property is considered final. You should not override it in
    "Drupal\Core\Validation\Plugin\Validation\Constraint\AllowedValuesConstraint".
    1x in
    RecursiveContextualValidatorTest::testBasicValidateWithoutConstraints from
    Drupal\Tests\Core\TypedData

  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    @godotislate why are we suddenly hitting this when we move to attributes? This is already the case. My guess is the conversion is breaking how we are already skipping it so we should fix that here.

  • Pipeline finished with Failed
    9 months ago
    Total: 493s
    #109045
  • @alexpott I ran the failing test locally, and the issue is that AttributeClassDiscovery instantiates a \ReflectionClass when getting definitions, at which point the Symfony DebugClassLoader is flagging the deprecations. AnnotatedClassDiscovery does not instantiate \ReflectionClass, so that's apparently why it hasn't come up before now.

    Stack trace

    DebugClassLoader.php:653, Symfony\Component\ErrorHandler\DebugClassLoader->checkAnnotations()
    DebugClassLoader.php:334, Symfony\Component\ErrorHandler\DebugClassLoader->checkClass()
    DebugClassLoader.php:307, Symfony\Component\ErrorHandler\DebugClassLoader->loadClass()
    AttributeClassDiscovery.php:125, ReflectionClass->__construct()
    AttributeClassDiscovery.php:125, Drupal\Component\Plugin\Discovery\AttributeClassDiscovery->parseClass()
    AttributeDiscoveryWithAnnotations.php:98, Drupal\Core\Plugin\Discovery\AttributeDiscoveryWithAnnotations->parseClass()
    AttributeClassDiscovery.php:84, Drupal\Component\Plugin\Discovery\AttributeClassDiscovery->getDefinitions()
    AttributeDiscoveryWithAnnotations.php:67, Drupal\Core\Plugin\Discovery\AttributeDiscoveryWithAnnotations->getDefinitions()
    DerivativeDiscoveryDecorator.php:86, Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator->getDefinitions()
    StaticDiscoveryDecorator.php:56, Drupal\Component\Plugin\Discovery\StaticDiscoveryDecorator->getDefinitions()
    DefaultPluginManager.php:329, Drupal\Core\Plugin\DefaultPluginManager->findDefinitions()
    DefaultPluginManager.php:205, Drupal\Core\Plugin\DefaultPluginManager->getDefinitions()
    DiscoveryCachedTrait.php:22, Drupal\Core\Plugin\DefaultPluginManager->getDefinition()
    ConstraintFactory.php:21, Drupal\Core\Validation\ConstraintFactory->createInstance()
    PluginManagerBase.php:83, Drupal\Component\Plugin\PluginManagerBase->createInstance()
    ConstraintManager.php:85, Drupal\Core\Validation\ConstraintManager->create()
    ContextDefinition.php:366, Drupal\Core\Plugin\Context\ContextDefinition->getConstraintObjects()
    ContextDefinition.php:316, Drupal\Core\Plugin\Context\ContextDefinition->isSatisfiedBy()
    ContextHandler.php:76, Drupal\Core\Plugin\Context\ContextHandler->Drupal\Core\Plugin\Context\{closure:/var/www/html/web/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php:75-77}()
    ContextHandler.php:77, array_filter()
    ContextHandler.php:77, Drupal\Core\Plugin\Context\ContextHandler->getMatchingContexts()
    ContextHandler.php:64, Drupal\Core\Plugin\Context\ContextHandler->checkRequirements()
    ContextHandlerTest.php:74, Drupal\Tests\Core\Plugin\ContextHandlerTest->testCheckRequirements()
    TestCase.php:1614, PHPUnit\Framework\TestCase->runTest()
    TestCase.php:1220, PHPUnit\Framework\TestCase->runBare()
    TestResult.php:728, PHPUnit\Framework\TestResult->run()
    TestCase.php:970, PHPUnit\Framework\TestCase->run()
    TestSuite.php:684, PHPUnit\Framework\TestSuite->run()
    TestSuite.php:684, PHPUnit\Framework\TestSuite->run()
    TestRunner.php:651, PHPUnit\TextUI\TestRunner->run()
    Command.php:145, PHPUnit\TextUI\Command->run()
    Command.php:98, PHPUnit\TextUI\Command::main()
    phpunit:107, include()
    phpunit:122, {main}()
    

    Should we suppress the deprecation messages in .deprecation-ignore.txt?

  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    Yes let's suppress and use documentation - i.e add @return where we can. This is another good thing about attributes - they get checked better by our tools.

  • Pipeline finished with Failed
    9 months ago
    Total: 506s
    #109139
  • Pipeline finished with Success
    9 months ago
    Total: 495s
    #109148
  • Added @return as needed and suppressed other deprecation warnings and tests are passing again.
    Follow up here: πŸ“Œ Symfony deprecations in Constraint plugins Fixed

  • Status changed to Needs work 9 months ago
  • πŸ‡¦πŸ‡ΊAustralia kim.pepper πŸ„β€β™‚οΈπŸ‡¦πŸ‡ΊSydney, Australia

    NW for a couple of minor nitpicks.

  • πŸ‡¦πŸ‡ΊAustralia kim.pepper πŸ„β€β™‚οΈπŸ‡¦πŸ‡ΊSydney, Australia

    We also need to update https://git.drupalcode.org/project/drupal/-/blob/11.x/core/core.api.php?... to replace @see \Drupal\Core\Validation\Annotation\Constraint with @see \Drupal\Core\Validation\Attribute\Constraint

  • Status changed to RTBC 9 months ago
  • πŸ‡¦πŸ‡ΊAustralia kim.pepper πŸ„β€β™‚οΈπŸ‡¦πŸ‡ΊSydney, Australia

    Searched for usages of @Constraint annotation and found none. All feedback addressed. (Are you able to resolve the comment threads?)

  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    @godotislate nice work and creating the follow-up πŸ“Œ Symfony deprecations in Constraint plugins Fixed super nice!

    Committed and pushed 045dfe1b96 to 11.x and b2971f464b to 10.3.x. Thanks!

  • Status changed to Fixed 9 months ago
  • Automatically closed - issue fixed for 2 weeks with no activity.

Production build 0.71.5 2024