Skip to content

Add @arguments-by-name, @optional-arguments-by-name and @stable-parameter-names support - #6741

Open
nicolas-grekas wants to merge 4 commits into
phpstan:2.3.xfrom
nicolas-grekas:arguments-by-name
Open

nicolas-grekas wants to merge 4 commits into
phpstan:2.3.xfrom
nicolas-grekas:arguments-by-name

Conversation

@nicolas-grekas

@nicolas-grekas nicolas-grekas commented Oct 14, 2026 •

Copy link
Copy Markdown

This PR adds support for three PHPDoc tags. They tell callers how to pass arguments, and which part of a signature is covered by backward compatibility.

With @optional-arguments-by-name, the arguments of optional parameters must be passed by name. Their names are stable, their order is not. With @arguments-by-name, every argument must be passed by name. @stable-parameter-names allows both styles, because names and order are both stable.

final class Configuration
{
    /**
     * @optional-arguments-by-name
     */
    public function __construct(string $dialect, array $groups = [], bool $allowExtraAttributes = true)
    {
    }
}

new Configuration('openapi-3.1', groups: ['read']); // OK
new Configuration('openapi-3.1', ['read']);         // Argument for optional parameter $groups of class Configuration constructor must be passed by name.

Like @no-named-arguments, the tags can be put on a function, a method or a class. A class-level tag applies to the public and protected methods of the class, and to the methods that override or implement them. A method-level tag wins over a class-level one, including a class-level @no-named-arguments. Constructors don't inherit the tags of the parent constructor.

When a method overrides or implements a tagged method, renaming a covered parameter is reported too. All parameters are covered by @arguments-by-name and @stable-parameter-names, and optional ones by @optional-arguments-by-name.

@no-named-arguments wins when it's on the same docblock as one of the new tags. I didn't put this behind bleeding edge since nothing is reported until a library adds one of the tags. Calls through callables are not checked yet, because callable types don't know about the tags.

stof asked for this in phpstan/phpstan#14386, after the discussion in symfony/symfony-docs#14566 back in 2020. Ondřej suggested hardcoding the Symfony cases in phpstan-symfony first. Like stof, I'd rather have the tags in the code. They follow each version of the library, and the need isn't specific to Symfony. Psalm gets the same tags in vimeo/psalm#12389, which builds on GromNaN's @only-named-arguments from vimeo/psalm#11791 (our @arguments-by-name).

Closes phpstan/phpstan#14386

Symfony docs: symfony/symfony-docs#23350

…parameter-names` support

PHPStan now reports a positional argument when the PHPDoc of the called function, method or class requires it to be passed by name.
`@stable-parameter-names` is parsed but reports nothing at call sites.
…gs win

A tag on a method now wins over a class-level tag, including a class-level `@no-named-arguments`.
Constructors no longer inherit the tags of the parent constructor.
The new arguments of `FunctionCallParametersCheck::check()` are now optional and last, so extensions that call it keep working.
A private method is only called by its own class, so how it is called doesn't matter to other packages.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for requiring the usage of named arguments

1 participant