Skip to content
10 changes: 10 additions & 0 deletions PHPCSUtils/BackCompat/BCFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ public static function getDeclarationName(File $phpcsFile, $stackPtr)
* 'name' => '$var', // The variable name.
* 'token' => integer, // The stack pointer to the variable name.
* 'content' => string, // The full content of the variable definition.
* 'has_attributes' => boolean, // Does the parameter have one or more attributes attached ?
* 'pass_by_reference' => boolean, // Is the variable passed by reference?
* 'reference_token' => integer, // The stack pointer to the reference operator
* // or FALSE if the param is not passed by reference.
Expand Down Expand Up @@ -206,6 +207,7 @@ public static function getMethodParameters(File $phpcsFile, $stackPtr)
$defaultStart = null;
$equalToken = null;
$paramCount = 0;
$hasAttributes = false;
$passByReference = false;
$referenceToken = false;
$variableLength = false;
Expand Down Expand Up @@ -236,6 +238,12 @@ public static function getMethodParameters(File $phpcsFile, $stackPtr)
}

switch ($tokens[$i]['code']) {
case T_ATTRIBUTE:
$hasAttributes = true;

// Skip to the end of the attribute.
$i = $tokens[$i]['attribute_closer'];
break;
case T_BITWISE_AND:
if ($defaultStart === null) {
$passByReference = true;
Expand Down Expand Up @@ -356,6 +364,7 @@ public static function getMethodParameters(File $phpcsFile, $stackPtr)
$vars[$paramCount]['default_equal_token'] = $equalToken;
}

$vars[$paramCount]['has_attributes'] = $hasAttributes;
$vars[$paramCount]['pass_by_reference'] = $passByReference;
$vars[$paramCount]['reference_token'] = $referenceToken;
$vars[$paramCount]['variable_length'] = $variableLength;
Expand All @@ -381,6 +390,7 @@ public static function getMethodParameters(File $phpcsFile, $stackPtr)
$paramStart = ($i + 1);
$defaultStart = null;
$equalToken = null;
$hasAttributes = false;
$passByReference = false;
$referenceToken = false;
$variableLength = false;
Expand Down
12 changes: 11 additions & 1 deletion PHPCSUtils/Utils/Arrays.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ class Arrays
\T_ARRAY => \T_ARRAY,
\T_OPEN_SHORT_ARRAY => \T_OPEN_SHORT_ARRAY,

// Inline function and control structures to skip over.
// Inline function, control structures and other things to skip over.
\T_FN => \T_FN,
\T_MATCH => \T_MATCH,
\T_ATTRIBUTE => \T_ATTRIBUTE,
];

/**
Expand Down Expand Up @@ -297,6 +298,7 @@ public static function getOpenClose(File $phpcsFile, $stackPtr, $isShortArray =
* @since 1.0.0
* @since 1.0.0-alpha2 Now allows for arrow functions in arrays.
* @since 1.0.0-alpha4 Now allows for match expressions in arrays.
* @since 1.0.0-alpha4 Now allows for attributes in arrays.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being examined.
* @param int $start Stack pointer to the start of the array item.
Expand Down Expand Up @@ -354,6 +356,14 @@ public static function getDoubleArrowPtr(File $phpcsFile, $start, $end)
continue;
}

// Skip over attributes which may contain arrays as a passed parameters.
if ($tokens[$doubleArrow]['code'] === \T_ATTRIBUTE
&& isset($tokens[$doubleArrow]['attribute_closer'])
) {
$doubleArrow = $tokens[$doubleArrow]['attribute_closer'];
continue;
}

// Start of nested long/short array.
break;
} while ($doubleArrow < $end);
Expand Down
12 changes: 12 additions & 0 deletions PHPCSUtils/Utils/FunctionDeclarations.php
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ public static function getProperties(File $phpcsFile, $stackPtr)
* 'name' => '$var', // The variable name.
* 'token' => integer, // The stack pointer to the variable name.
* 'content' => string, // The full content of the variable definition.
* 'has_attributes' => boolean, // Does the parameter have one or more attributes attached ?
* 'pass_by_reference' => boolean, // Is the variable passed by reference?
* 'reference_token' => integer, // The stack pointer to the reference operator
* // or FALSE if the param is not passed by reference.
Expand Down Expand Up @@ -359,6 +360,7 @@ public static function getProperties(File $phpcsFile, $stackPtr)
* @since 1.0.0-alpha4 Added support for PHP 8.0 union types.
* @since 1.0.0-alpha4 Added support for PHP 8.0 constructor property promotion.
* @since 1.0.0-alpha4 Added support for PHP 8.0 identifier name tokenization.
* @since 1.0.0-alpha4 Added support for PHP 8.0 parameter attributes.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position in the stack of the function token
Expand Down Expand Up @@ -414,6 +416,7 @@ public static function getParameters(File $phpcsFile, $stackPtr)
$defaultStart = null;
$equalToken = null;
$paramCount = 0;
$hasAttributes = false;
$passByReference = false;
$referenceToken = false;
$variableLength = false;
Expand All @@ -439,6 +442,13 @@ public static function getParameters(File $phpcsFile, $stackPtr)
}

switch ($tokens[$i]['code']) {
case \T_ATTRIBUTE:
$hasAttributes = true;

// Skip to the end of the attribute.
$i = $tokens[$i]['attribute_closer'];
break;

case \T_BITWISE_AND:
$passByReference = true;
$referenceToken = $i;
Expand Down Expand Up @@ -488,6 +498,7 @@ public static function getParameters(File $phpcsFile, $stackPtr)
$vars[$paramCount]['default_equal_token'] = $equalToken;
}

$vars[$paramCount]['has_attributes'] = $hasAttributes;
$vars[$paramCount]['pass_by_reference'] = $passByReference;
$vars[$paramCount]['reference_token'] = $referenceToken;
$vars[$paramCount]['variable_length'] = $variableLength;
Expand All @@ -513,6 +524,7 @@ public static function getParameters(File $phpcsFile, $stackPtr)
$paramStart = ($i + 1);
$defaultStart = null;
$equalToken = null;
$hasAttributes = false;
$passByReference = false;
$referenceToken = false;
$variableLength = false;
Expand Down
7 changes: 7 additions & 0 deletions PHPCSUtils/Utils/Namespaces.php
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ public static function findNamespacePtr(File $phpcsFile, $stackPtr)
\T_CLOSE_SHORT_ARRAY,
\T_CLOSE_SQUARE_BRACKET,
\T_DOC_COMMENT_CLOSE_TAG,
\T_ATTRIBUTE_END,
];
$returnValue = false;

Expand Down Expand Up @@ -327,6 +328,12 @@ public static function findNamespacePtr(File $phpcsFile, $stackPtr)
continue;
}

// Skip over potentially large attributes.
if (isset($tokens[$prev]['attribute_opener'])) {
$prev = $tokens[$prev]['attribute_opener'];
continue;
}

// Skip over potentially large docblocks.
if (isset($tokens[$prev]['comment_opener'])) {
$prev = $tokens[$prev]['comment_opener'];
Expand Down
9 changes: 9 additions & 0 deletions PHPCSUtils/Utils/PassedParameters.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class PassedParameters
\T_OPEN_SQUARE_BRACKET => \T_OPEN_SQUARE_BRACKET,
\T_OPEN_PARENTHESIS => \T_OPEN_PARENTHESIS,
\T_DOC_COMMENT_OPEN_TAG => \T_DOC_COMMENT_OPEN_TAG,
\T_ATTRIBUTE => \T_ATTRIBUTE,
];

/**
Expand Down Expand Up @@ -270,6 +271,14 @@ public static function getParameters(File $phpcsFile, $stackPtr, $limit = 0, $is
continue;
}

// Skip over attributes.
if ($tokens[$nextComma]['code'] === \T_ATTRIBUTE
&& isset($tokens[$nextComma]['attribute_closer'])
) {
$nextComma = $tokens[$nextComma]['attribute_closer'];
continue;
}

if ($tokens[$nextComma]['code'] !== \T_COMMA
&& $tokens[$nextComma]['code'] !== $tokens[$closer]['code']
) {
Expand Down
2 changes: 2 additions & 0 deletions PHPCSUtils/Utils/Variables.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ class Variables
*
* @since 1.0.0
* @since 1.0.0-alpha4 Added support for PHP 8.0 union types.
* @since 1.0.0-alpha4 No longer gets confused by PHP 8.0 property attributes.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position in the stack of the `T_VARIABLE` token
Expand Down Expand Up @@ -138,6 +139,7 @@ public static function getMemberProperties(File $phpcsFile, $stackPtr)
\T_SEMICOLON,
\T_OPEN_CURLY_BRACKET,
\T_CLOSE_CURLY_BRACKET,
\T_ATTRIBUTE_END,
],
($stackPtr - 1)
);
Expand Down
16 changes: 16 additions & 0 deletions Tests/BackCompat/BCFile/GetMemberPropertiesTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,19 @@ $anon = class() {
// Intentional fatal error - duplicate types are not allowed in union types, but that's not the concern of the method.
public int |string| /*comment*/ INT $duplicateTypeInUnion;
};

$anon = class {
/* testPHP8PropertySingleAttribute */
#[PropertyWithAttribute]
public string $foo;

/* testPHP8PropertyMultipleAttributes */
#[PropertyWithAttribute(foo: 'bar'), MyAttribute]
protected ?int|float $bar;

/* testPHP8PropertyMultilineAttribute */
#[
PropertyWithAttribute(/* comment */ 'baz')
]
private mixed $baz;
};
36 changes: 36 additions & 0 deletions Tests/BackCompat/BCFile/GetMemberPropertiesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,42 @@ public function dataGetMemberProperties()
'nullable_type' => false,
],
],
'php8-property-with-single-attribute' => [
'/* testPHP8PropertySingleAttribute */',
[
'scope' => 'public',
'scope_specified' => true,
'is_static' => false,
'type' => 'string',
'type_token' => -2, // Offset from the T_VARIABLE token.
'type_end_token' => -2, // Offset from the T_VARIABLE token.
'nullable_type' => false,
],
],
'php8-property-with-multiple-attributes' => [
'/* testPHP8PropertyMultipleAttributes */',
[
'scope' => 'protected',
'scope_specified' => true,
'is_static' => false,
'type' => '?int|float',
'type_token' => -4, // Offset from the T_VARIABLE token.
'type_end_token' => -2, // Offset from the T_VARIABLE token.
'nullable_type' => true,
],
],
'php8-property-with-multiline-attribute' => [
'/* testPHP8PropertyMultilineAttribute */',
[
'scope' => 'private',
'scope_specified' => true,
'is_static' => false,
'type' => 'mixed',
'type_token' => -2, // Offset from the T_VARIABLE token.
'type_end_token' => -2, // Offset from the T_VARIABLE token.
'nullable_type' => false,
],
],
];
}

Expand Down
14 changes: 14 additions & 0 deletions Tests/BackCompat/BCFile/GetMethodParametersTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,20 @@ function commentsInParams(
?MyClass /*-*/ & /*-*/.../*-*/ $param /*-*/ = /*-*/ 'default value' . /*-*/ 'second part' // Trailing comment.
) {}

/* testParameterAttributesInFunctionDeclaration */
class ParametersWithAttributes(
public function __construct(
#[\MyExample\MyAttribute] private string $constructorPropPromTypedParamSingleAttribute,
#[MyAttr([1, 2])]
Type|false
$typedParamSingleAttribute,
#[MyAttribute(1234), MyAttribute(5678)] ?int $nullableTypedParamMultiAttribute,
#[WithoutArgument] #[SingleArgument(0)] $nonTypedParamTwoAttributes,
#[MyAttribute(array("key" => "value"))]
&...$otherParam,
) {}
}

/* testFunctionCallFnPHPCS353-354 */
$value = $obj->fn(true);

Expand Down
Loading