Passing a $options array to constraint constructors is deprecated, use named arguments instead

Created on 2 May 2025, 29 days ago

Problem/Motivation

New deprecation in Symfony 7.3 via 📌 Update Composer dependencies for 11.2.0 Active

Since symfony/validator 7.3: Passing an array of options to configure the "..." constraint is deprecated, use named arguments instead.

Steps to reproduce

Proposed resolution

Use named arguments instead for Symfony constraints.

Add named argument support and a deprecation/BC layer for options arrays to custom Drupal constraints.

Remaining tasks

User interface changes

Introduced terminology

API changes

Data model changes

Release notes snippet

📌 Task
Status

Postponed

Version

11.0 🔥

Component

typed data system

Created by

🇬🇧United Kingdom longwave UK

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

Merge Requests

Comments & Activities

  • Issue created by @longwave
  • Note from @longwave about this on Slack:

    i see Symfony adds a HasNamedArguments attribute to constraints that have been converted.. so we could check for that at runtime although i hope reflection of that isn't too expensive

    I don't think reflection on the class will have much performance penalty, since the plugin class is being instantiated right after anyway, so it's getting loaded in memory regardless.

    But if it does turn out to be a performance issue, one idea I have is to add functionality to AttributeClassDiscovery::parseClass() to be able to pick up additional attributes like this and add them to the plugin definition, since the plugin class is already being reflected in discovery. The effort in Allow attribute-based plugins to discover supplemental attributes from other modules Active could be amended to include this.

    It would look something like:

    1. In parseClass(), call a new method, something like parseAdditionalAttributes() that takes the ReflectionClass object as one of its parameters
    2. In parseAdditionalAttributes(), check whether the plugin class implements a (new) Interface, maybe something like AdditionalAttributesPluginInterface (name TBD), that has its own public static function parseAdditionalAttributes() method
    3. Plugin classes implementing the interface and method can then use the reflection class object to look at other attributes on the class or its methods, properties, etc. specific to the plugin class
    4. For relevant constraint plugins, the implementation of parseAdditionalAttributes() would look at the constructor for the the HasNamedArguments attribute, and if so, that gets added back to the plugin definition

    Note that the above would have to account for attributes possibly being defined in other modules and the attribute discovery filecache, so there are complications.

    And I'm not completely sure this would be worth the effort for this use case, but just throwing it out there as a possibility.

  • 🇬🇧United Kingdom longwave UK

    Making this active and major, as this will likely require new deprecations and will be removed by Symfony 8/Drupal 12, we should aim to land this in 11.3.

  • Pipeline finished with Failed
    2 days ago
    Total: 245s
    #509565
  • Pipeline finished with Failed
    2 days ago
    Total: 111s
    #509586
  • Pipeline finished with Failed
    2 days ago
    Total: 95s
    #509667
  • Pipeline finished with Failed
    2 days ago
    #509686
  • Pipeline finished with Failed
    2 days ago
    Total: 101s
    #509710
  • Pipeline finished with Running
    2 days ago
    #509715
  • Pipeline finished with Failed
    2 days ago
    Total: 92s
    #509736
  • Pipeline finished with Running
    2 days ago
    #509740
  • Removed the entry in .deprecation-ignore.txt and finally got all tests passing.

    The deprecation layer still needs doing. It might be a bit complex and probably needs some thinking through. Among other things, even though all the docblocks the options passed to addConstraint($constraint_name, $options = NULL) typed @param array|null $options, there are cases such as this in EntityDataDefinition::setEntityType() where $options is a string:

      public function setEntityTypeId($entity_type_id) {
        return $this->addConstraint('EntityType', $entity_type_id);
      }
    

    The ConstraintManager even accounts for this by turning non-array $options into array in ::create():

      public function create($name, $options) {
        if (!is_array($options)) {
          // Plugins need an array as configuration, so make sure we have one.
          // The constraint classes support passing the options as part of the
          // 'value' key also.
          $options = isset($options) ? ['value' => $options] : [];
        }
        return $this->createInstance($name, $options);
      }
    

    Problem is that with named arguments, an exception is thrown with invalid argument names, and it can't be assumed that value will always be a valid argument name.

  • Pipeline finished with Success
    2 days ago
    Total: 758s
    #509784
  • Merge request !12291Draft: Resolve #3522497 "Deprecation poc" → (Open) created by godotislate
  • Pipeline finished with Failed
    about 1 hour ago
    #511084
  • Pipeline finished with Failed
    35 minutes ago
    #511105
  • Pipeline finished with Failed
    20 minutes ago
    #511106
Production build 0.71.5 2024