Report constraint plugins that break on Drupal 12 because they have no named-arguments constructor
Maintainers usually reply within 1 day
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 35/100
- Issue type
- Feature
- Clarity
- Clearly specified
- Activity status
- Stale
- Tech stack
- drupal, php
- Domain
- devtools, testing-qa
Research direction
Start by reading the existing PHPStan rule implementations and tests for Drupal constraint plugins, then inspect how they identify constraint classes and constructors. Add coverage for classes with public option properties and no constructor carrying HasNamedArguments, including the allowed inherited constructor case. Done when the rule reports the reproducing plugin and its tests pass; an open pull request is already linked to this issue.
Written by the indexing model from the issue text.
Description
Summary
Drupal 11.4 started moving constraint plugins to named arguments, because Symfony deprecated the options array. See https://www.drupal.org/node/3554746.
- Drupal 11. For a plugin with options and no constructor with
#[HasNamedArguments],ConstraintFactory::createInstance()passes the options as an array:new $plugin_class($configuration). Symfony 7.4 triggers at runtime: "Support for evaluating options in the base Constraint class is deprecated. Initialize properties in the constructor of ProbeConstraint instead." - Drupal 12. It uses Symfony 8.1, and
ConstraintFactory::createInstance()spreads the options as named arguments:new $plugin_class(...$configuration). A plugin without its own constructor then fails withError: Unknown named parameter $max.
PHPStan doesn't see either one, because the deprecation is triggered with data, not by a deprecated method.
Steps to reproduce
On Drupal 11.5.x-dev, with PHPStan 2.3.5, phpstan-drupal 2.2.2 and phpstan-deprecation-rules, at level 2:
<?php
declare(strict_types=1);
namespace Drupal\pst_probe\Plugin\Validation\Constraint;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Validation\Attribute\Constraint;
use Symfony\Component\Validator\Constraint as SymfonyConstraint;
#[Constraint(
id: 'PstProbe',
label: new TranslatableMarkup('Probe', [], ['context' => 'Validation']),
)]
final class ProbeConstraint extends SymfonyConstraint {
public string $message = 'The value is not valid.';
public int $max = 10;
}
And a use of it:
BaseFieldDefinition::create('string')->addConstraint('PstProbe', ['max' => 5]);
new ProbeConstraint(['message' => 'Too long.']);
Result: [OK] No errors.
The same class with Symfony 8.1, as Drupal 12 creates it:
$configuration = ['max' => 5];
new ProbeConstraint(...$configuration);
// Error: Unknown named parameter $max
How often this happens
In unpatched copies of 75 contributed modules and themes from the Drupal CMS stack, 52 constraint classes have public option properties and no constructor: canvas 34, ai_validations 7, scheduler_content_moderation_integration 3, ai 3, trash 2, crop 2 and eca 1. Each one breaks on Drupal 12 when a field definition, config schema or code passes options to it.
Proposed resolution
A rule should be added for classes that extend Symfony\Component\Validator\Constraint and are Drupal constraint plugins, with the Constraint attribute or annotation:
- If the class declares public properties (its options), and its constructor is the one of the base
Constraintclass, report it. A constructor with the#[HasNamedArguments]attribute in the class or in a parent, like Symfony'sChoiceorCollection, is fine. For example: "Constraint plugin ProbeConstraint has options but no constructor with the HasNamedArguments attribute. Drupal 12 passes the options as named arguments, so this constraint fails when it is configured. Add a constructor with named arguments. See https://www.drupal.org/node/3554746." - Optionally, report
new SomeConstraint([...])with an array as the first argument, when the constructor ofSomeConstrainthas no#[HasNamedArguments].
In Drupal 11.5, 73 of core's 95 constraint plugins have a constructor with #[HasNamedArguments]. They are examples of the fix.
LLM disclosure
LLM was used to find and resolve this issue. With human review.
- Dominant language
- PHP
- Stars
- 208
- Forks
- 88
- Avg merge
- 2d 17h
- Merged PRs (30d)
- 15
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from mglaman/phpstan-drupal
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
mglaman/phpstan-drupal#1078 ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 55/100
mglaman/phpstan-drupal#1079 ·
Maintainers usually reply within 1 day
-
Magic properties: original on entities, name on UserSession and password on User are not reported as deprecatedPossibly taken @goba claimed this 1 day ago. Open
Difficulty 3/5 1-2 days Newbie friendliness 35/100
mglaman/phpstan-drupal#1074 ·
Maintainers usually reply within 1 day
-
enhancement
Difficulty 5/5 Over a week Newbie friendliness 25/100
mglaman/phpstan-drupal#1051 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 4/5 3-5 days Newbie friendliness 48/100
mglaman/phpstan-drupal#1047 ·
Maintainers usually reply within 1 day
All issues in mglaman/phpstan-drupal
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
getgrav/grav-plugin-sitemap#125 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
nikic/PHP-Parser#1184 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
api-platform/core#8835 ·
Maintainers usually reply within 2 days
-
14.3.x
Difficulty 1/5 1-3 hours Newbie friendliness 82/100