Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PassedParameters: add support for PHP 8.0 named arguments in function calls #235

Merged

Conversation

jrfnl
Copy link
Member

@jrfnl jrfnl commented Dec 1, 2020

PassedParameters: add support for PHP 8.0 named arguments in function calls

PHP 8.0 introduces named function call parameters:

array_fill(start_index: 0, count: 100, value: 50);

// Using reserved keywords as names is allowed.
array_foobar(array: $array, switch: $switch, class: $class);

Ref: https://wiki.php.net/rfc/named_params

This PR adds support for named arguments in function calls to PHPCSUtils.

This is done by:

  1. Adding support for named arguments to the getParameters() method.
    When a named parameter is encountered in a function call, the returned parameter information array for that parameter will contain the following additional keys: name_start, name_end, name.
    The existing start, end, raw and clean keys will contain the same content as before: the information on the parameter value (excluding the name part).
  2. A new $paramNames parameter has been added to the getParameter() method.
    It is STRONGLY recommended to always pass this parameter when examining function calls to allow for support for PHP 8 named arguments.
    When this parameter is not passed, but the function call being examined is a function call using named parameters, an exception will be thrown.
  3. This new $paramNames parameter allows for passing either the parameter name as a string or as an array of strings.
    While a parameter can only have one name, a lot of packages are reviewing and renaming parameters to more descriptive names to support named parameters in PHP 8, so, at times, to support multiple versions of packages, different names can be used for the same parameter in a PHP 8 function call and by allowing multiple names to be passed, the method supports this.
  4. Adding a new getParameterFromStack() method which takes the output of a previous call to getParameters(), an offset and a name/names to find a specific parameter.
    Finding a specific parameter in the stack returned by getParameters() used to be a case of just looking at the offset.
    With named parameter being allowed to be passed in any order and not all optional parameter before a specific parameter needing to be passed anymore, finding a specific parameter in the stack has become more complicated.
    As building the parameter stack can be "expensive", this method provides efficiency by allowing for retrieving multiple specific parameters from the same function call without needing to call getParameters() multiple times and without needing to handle positional versus named parameters within the sniff.

The implementation in this PR is based on the presumption that the upstream PR to add support for named parameters to PHPCS itself - squizlabs/PHP_CodeSniffer#3178 -, which is based on the proposal in issue squizlabs/PHP_CodeSniffer#3159, will be merged without changes.
The upstream PR is expected to go into PHPCS 3.6.0.

Includes extensive unit tests.

PassedParameters::getParameters(): fix backward compatibility with PHPCS < 3.6.0

To support named parameters in older PHPCS versions in which named parameters aren't supported by PHPCS itself yet, some fixes and tweaks are needed.

Depending on the specific code being used, in most cases, a parameter name label in older PHPCS versions would be tokenized as T_GOTO_LABEL with the T_COLON token being removed and the : being added to the "goto label" content.

This adds a BC support to the getParameters() method to allow for tokenization of the parameter name as the T_GOTO_LABEL token, as well as for the T_COLON being tokenized separately, but as the wrong token - T_STRING/T_INLINE_ELSE -.

As the T_GOTO_LABEL token swallows the T_COLON token, this also means that the token positions returned by PassedParameters::getParameter() will be different depending on whether sniffs are run on a PHPCS version supporting named parameters or not.

The unit tests have been adjusted to take this into account.

PassedParameters::getParameters(): fix compatibility with PHP 8.0 (tests only)

While the PHP 8.0 match control structure is not yet supported in PHPCS itself, in PHP 8.0, it will be recognized as a keyword, which will - again - throw off the token positions in the tests.

The PassedParameters::getParameters() method itself already handles "match as a keyword vs not a keyword" correctly.

This adds a tweak to the unit tests to work-around the different expected token positions.
Once PHPCS adds support for match control structures, this tweak will most likely need adjusting.

… calls

PHP 8.0 introduces named function call parameters:
```php
array_fill(start_index: 0, count: 100, value: 50);

// Using reserved keywords as names is allowed.
array_foobar(array: $array, switch: $switch, class: $class);
```

Ref: https://wiki.php.net/rfc/named_params

This PR adds support for named arguments in function calls to PHPCSUtils.

This is done by:
1. Adding support for named arguments to the `getParameters()` method.
    When a named parameter is encountered in a function call, the returned parameter information array for that parameter will contain the following additional keys: `name_start`, `name_end`, `name`.
    The existing `start`, `end`, `raw` and `clean` keys will contain the same content as before: the information on the parameter _value_ (excluding the name part).
2. A new `$paramNames` parameter has been added to the `getParameter()` method.
    It is STRONGLY recommended to always pass this parameter when examining function calls to allow for support for PHP 8 named arguments.
    When this parameter is **not** passed, but the function call being examined is a function call using named parameters, an exception will be thrown.
3. This new `$paramNames` parameter allows for passing either the parameter name as a string or as an array of strings.
    While a parameter can only have _one_ name, a lot of packages are reviewing and renaming parameters to more descriptive names to support named parameters in PHP 8, so, at times, to support multiple versions of packages, different names can be used for the same parameter in a PHP 8 function call and by allowing multiple names to be passed, the method supports this.
4. Adding a new `getParameterFromStack()` method which takes the output of a previous call to `getParameters()`, an offset and a name/names to find a specific parameter.
    Finding a specific parameter in the stack returned by `getParameters()` used to be a case of just looking at the offset.
    With named parameter being allowed to be passed in any order and not all optional parameter before a specific parameter needing to be passed anymore, finding a specific parameter in the stack has become more complicated.
    As building the parameter stack can be "expensive", this method provides efficiency by allowing for retrieving multiple specific parameters from the same function call without needing to call `getParameters()` multiple times and without needing to handle positional versus named parameters within the sniff.

The implementation in this PR is based on the presumption that the upstream PR to add support for named parameters to PHPCS itself - squizlabs/PHP_CodeSniffer 3178 -, which is based on the proposal in issue squizlabs/PHP_CodeSniffer 3159, will be merged without changes.
The upstream PR is expected to go into PHPCS 3.6.0.

Includes extensive unit tests.
…PCS < 3.6.0

To support named parameters in older PHPCS versions in which named parameters aren't supported by PHPCS itself yet, some fixes and tweaks are needed.

Depending on the specific code being used, in _most_ cases, a parameter name label in older PHPCS versions would be tokenized as `T_GOTO_LABEL` with the `T_COLON` token being removed and the `:` being added to the "goto label" content.

This adds a BC support to the `getParameters()` method to allow for tokenization of the parameter name as the `T_GOTO_LABEL` token, as well as for the `T_COLON` being tokenized separately, but as the wrong token - `T_STRING`/`T_INLINE_ELSE` -.

As the `T_GOTO_LABEL` token swallows the `T_COLON` token, this also means that the token positions returned by `PassedParameters::getParameter()` will be different depending on whether sniffs are run on a PHPCS version supporting named parameters or not.

The unit tests have been adjusted to take this into account.
…sts only)

While the PHP 8.0 `match` control structure is not yet supported in PHPCS itself, in PHP 8.0, it will be recognized as a keyword, which will - again - throw off the token positions in the tests.

The `PassedParameters::getParameters()` method itself already handles "`match` as a keyword vs not a keyword" correctly.

This adds a tweak to the unit tests to work-around the different expected token positions.
Once PHPCS adds support for `match` control structures, this tweak will most likely need adjusting.
@jrfnl jrfnl merged commit 8655503 into develop Dec 1, 2020
@jrfnl jrfnl deleted the feature/php-8-support-named-parameters-in-function-calls branch December 1, 2020 17:54
jrfnl added a commit to PHPCompatibility/PHPCompatibility that referenced this pull request Dec 2, 2020
PHPCSUtils `1.0.0-alpha4` will add support for named parameters.
Ref: PHPCSStandards/PHPCSUtils#235

To allow for this, any calls to `PassedParameters::getParameter()` to retrieve a parameter in a function call need to pass the name of the target parameter.

Within PHPCompatibility, this is the only sniff for which the tests would fail at the moment when used with PHPCSUtils alpha-4, so pre-eminently fixing this ahead of the release of PHPCSUtils 1.0.0-alpha4.

Adding unit tests for this change would break on alpha3 though, so I'm leaving those for a future PR once alpha4 has been released.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant