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

PHP 8.1 | Utils\PassedParameters: change named param implementation/named params after variadic #383

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 36 additions & 36 deletions PHPCSUtils/Utils/PassedParameters.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
use PHPCSUtils\Tokens\Collections;
use PHPCSUtils\Utils\Arrays;
use PHPCSUtils\Utils\GetTokensAsString;
use PHPCSUtils\Utils\NamingConventions;

/**
* Utility functions to retrieve information about parameters passed to function calls,
Expand Down Expand Up @@ -157,8 +156,9 @@ public static function hasParameters(File $phpcsFile, $stackPtr, $isShortArray =
* See {@see PassedParameters::hasParameters()} for information on the supported constructs.
*
* @since 1.0.0
* @since 1.0.0-alpha4 Added support for PHP 8.0 function calls with named arguments by
* introducing the new `'name_start'`, `'name_end'` and `'name'` index keys.
* @since 1.0.0-alpha4 Added support for PHP 8.0 function calls with named arguments by introducing
* the `'name'` and `'name_token'` index keys as well as using the name
* as the index for the top-level array for named parameters.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
* @param int $stackPtr The position of function call name,
Expand All @@ -170,7 +170,7 @@ public static function hasParameters(File $phpcsFile, $stackPtr, $isShortArray =
* Efficiency tweak for when this has already been established,
* Use with EXTREME care.
*
* @return array A multi-dimentional array information on each parameter/array item.
* @return array A multi-dimentional array with information on each parameter/array item.
* The information gathered about each parameter/array item is in the following format:
* ```php
* 1 => array(
Expand All @@ -180,22 +180,19 @@ public static function hasParameters(File $phpcsFile, $stackPtr, $isShortArray =
* 'clean' => string, // Same as `raw`, but all comment tokens have been stripped out.
* )
* ```
* For function calls passing named arguments, the format is as follows:
* If a named parameter is encountered in a function call, the top-level index will not be
* the parameter _position_, but the _parameter name_ and the array will include two extra keys:
* ```php
* 1 => array(
* 'name_start' => int, // The stack pointer to the first token in the parameter name.
* 'name_end' => int, // The stack pointer to the last token in the parameter name.
* // This will point to the colon.
* 'name' => string, // The parameter name as a string (without the colon).
* 'start' => int, // The stack pointer to the first token in the parameter value.
* 'end' => int, // The stack pointer to the last token in the parameter value.
* 'raw' => string, // A string with the contents of all tokens between `start` and `end`.
* 'clean' => string, // Same as `raw`, but all comment tokens have been stripped out.
* 'parameter_name' => array(
* 'name' => string, // The parameter name (without the colon).
* 'name_token' => int, // The stack pointer to the parameter name token.
* ...
* )
* ```
* The `'start'`, `'end'`, `'raw'` and `'clean'` indexes will always contain just and only
* information on the parameter value.
* _Note: The array starts at index 1._
* _Note: The array starts at index 1 for positional parameters._
* _The key for named parameters will be the parameter name._
* If no parameters/array items are found, an empty array will be returned.
*
* @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the token passed is not one of the
Expand Down Expand Up @@ -288,6 +285,7 @@ public static function getParameters(File $phpcsFile, $stackPtr, $limit = 0, $is

// Ok, we've reached the end of the parameter.
$paramEnd = ($nextComma - 1);
$key = $cnt;

if ($mayHaveNames === true) {
$firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $paramStart, ($paramEnd + 1), true);
Expand All @@ -302,18 +300,22 @@ public static function getParameters(File $phpcsFile, $stackPtr, $limit = 0, $is
if ($tokens[$secondNonEmpty]['code'] === \T_COLON
&& $tokens[$firstNonEmpty]['code'] === \T_PARAM_NAME
) {
$parameters[$cnt]['name_start'] = $paramStart;
$parameters[$cnt]['name_end'] = $secondNonEmpty;
$parameters[$cnt]['name'] = $tokens[$firstNonEmpty]['content'];
if (isset($parameters[$tokens[$firstNonEmpty]['content']]) === false) {
// Set the key to be the name, but only if we've not seen this name before.
$key = $tokens[$firstNonEmpty]['content'];
}

$parameters[$key]['name'] = $tokens[$firstNonEmpty]['content'];
$parameters[$key]['name_token'] = $firstNonEmpty;
$paramStart = ($secondNonEmpty + 1);
}
}
}

$parameters[$cnt]['start'] = $paramStart;
$parameters[$cnt]['end'] = $paramEnd;
$parameters[$cnt]['raw'] = \trim(GetTokensAsString::normal($phpcsFile, $paramStart, $paramEnd));
$parameters[$cnt]['clean'] = \trim(GetTokensAsString::noComments($phpcsFile, $paramStart, $paramEnd));
$parameters[$key]['start'] = $paramStart;
$parameters[$key]['end'] = $paramEnd;
$parameters[$key]['raw'] = \trim(GetTokensAsString::normal($phpcsFile, $paramStart, $paramEnd));
$parameters[$key]['clean'] = \trim(GetTokensAsString::noComments($phpcsFile, $paramStart, $paramEnd));

// Check if there are more tokens before the closing parenthesis.
// Prevents function calls with trailing comma's from setting an extra parameter:
Expand Down Expand Up @@ -476,7 +478,18 @@ public static function getParameterFromStack(array $parameters, $paramOffset, $p
return false;
}

// First check for positional parameters.
// First check for a named parameter.
if (empty($paramNames) === false) {
$paramNames = (array) $paramNames;
foreach ($paramNames as $name) {
// Note: parameter names are case-sensitive!.
if (isset($parameters[$name]) === true) {
return $parameters[$name];
}
}
}

// Next check for positional parameters.
if (isset($parameters[$paramOffset]) === true
&& isset($parameters[$paramOffset]['name']) === false
) {
Expand All @@ -489,19 +502,6 @@ public static function getParameterFromStack(array $parameters, $paramOffset, $p
);
}

$paramNames = \array_flip((array) $paramNames);

// Next check if a named parameter was passed with the specified name.
foreach ($parameters as $paramDetails) {
if (isset($paramDetails['name']) === false) {
continue;
}

if (isset($paramNames[$paramDetails['name']]) === true) {
return $paramDetails;
}
}

return false;
}
}
4 changes: 4 additions & 0 deletions Tests/Utils/PassedParameters/GetParameterCountTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ Partially\Qualified\myfunction( $a );
/* testFunctionCallNamespaceOperator */
namespace\myfunction( $a );

/* testFunctionCallNamedParamsDuplicateName */
// Error Exception, but not the concern of PHPCSUtils. Should still be handled.
test(param: 1, param: 2);

/* testLongArray1 */
$foo = array( 1, 2, 3, 4, 5, 6, true );

Expand Down
4 changes: 4 additions & 0 deletions Tests/Utils/PassedParameters/GetParameterCountTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,10 @@ public function dataGetParameterCount()
'expected' => 1,
'targetContent' => ($php8Names === true) ? null : 'myfunction',
],
'function-call-named-params-duplicate-name' => [
'testMarker' => '/* testFunctionCallNamedParamsDuplicateName */',
'expected' => 2,
],

// Long arrays.
'long-array-1' => [
Expand Down
4 changes: 4 additions & 0 deletions Tests/Utils/PassedParameters/GetParameterFromStackTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,7 @@ setcookie(
'name',
expires: time() + (60 * 60 * 24),
);

/* testPHP81NamedParamAfterVariadic */
// Prior to PHP 8.1, this was a compile error, but this is now supported.
test($positional, ...$variadic, namedA: $valueA, namedB: $valueB);
Loading