Skip to content
17 changes: 17 additions & 0 deletions PHPCSUtils/BackCompat/BCFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ public static function getDeclarationName(File $phpcsFile, $stackPtr)
* parameter would bleed through to the next (non-type hinted) parameter.
* - PHPCS 3.5.3: Added support for PHP 7.4 `T_FN` arrow functions.
* - PHPCS 3.5.7: Added support for namespace operators in type declarations. PHPCS#3066.
* - PHPCS 3.6.0: Added support for PHP 8.0 union types. PHPCS#3032.
*
* @see \PHP_CodeSniffer\Files\File::getMethodParameters() Original source.
* @see \PHPCSUtils\Utils\FunctionDeclarations::getParameters() PHPCSUtils native improved version.
Expand Down Expand Up @@ -422,6 +423,10 @@ public static function getMethodParameters(File $phpcsFile, $stackPtr)
break;
case 'T_NAMESPACE':
case 'T_NS_SEPARATOR':
case 'T_BITWISE_OR': // Union type separator PHPCS < 3.6.0.
case 'T_TYPE_UNION': // Union type separator PHPCS >= 3.6.0.
case 'T_FALSE':
case 'T_NULL':
// Part of a type hint or default value.
if ($defaultStart === null) {
if ($typeHintToken === false) {
Expand Down Expand Up @@ -542,6 +547,7 @@ public static function getMethodParameters(File $phpcsFile, $stackPtr)
* to `\PHP_CodeSniffer\Exceptions\RuntimeException`.
* - PHPCS 3.5.3: Added support for PHP 7.4 `T_FN` arrow functions.
* - PHPCS 3.5.7: Added support for namespace operators in type declarations. PHPCS#3066.
* - PHPCS 3.6.0: Added support for PHP 8.0 union types. PHPCS#3032.
*
* @see \PHP_CodeSniffer\Files\File::getMethodProperties() Original source.
* @see \PHPCSUtils\Utils\FunctionDeclarations::getProperties() PHPCSUtils native improved version.
Expand Down Expand Up @@ -659,6 +665,16 @@ public static function getMethodProperties(File $phpcsFile, $stackPtr)
break;
}

/*
* Work-around for a scope map tokenizer bug in PHPCS.
* {@link https://github.com/squizlabs/PHP_CodeSniffer/pull/3066}
*/
if ($scopeOpener === null && $tokens[$i]['code'] === \T_OPEN_CURLY_BRACKET) {
// End of function definition for which the scope opener is incorrectly not set.
$hasBody = true;
break;
}

if ($tokens[$i]['type'] === 'T_NULLABLE'
// Handle nullable tokens in PHPCS < 2.8.0.
|| (defined('T_NULLABLE') === false && $tokens[$i]['code'] === T_INLINE_THEN)
Expand Down Expand Up @@ -748,6 +764,7 @@ public static function getMethodProperties(File $phpcsFile, $stackPtr)
* - PHPCS 3.5.0: The Exception thrown changed from a `\PHP_CodeSniffer\Exceptions\TokenizerException`
* to `\PHP_CodeSniffer\Exceptions\RuntimeException`.
* - PHPCS 3.5.7: Added support for namespace operators in type declarations. PHPCS#3066.
* - PHPCS 3.6.0: Added support for PHP 8.0 union types. PHPCS#3032.
*
* @see \PHP_CodeSniffer\Files\File::getMemberProperties() Original source.
* @see \PHPCSUtils\Utils\Variables::getMemberProperties() PHPCSUtils native improved version.
Expand Down
30 changes: 27 additions & 3 deletions PHPCSUtils/Tokens/Collections.php
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,8 @@ public static function nullsafeObjectOperatorBC()
/**
* Token types which can be encountered in a parameter type declaration.
*
* Note: this is a method, not a property as the `T_TYPE_UNION` token for PHP 8.0 union types may not exist.
*
* Sister-method to the {@see Collections::parameterTypeTokensBC()} method.
* This method supports PHPCS 3.3.0 and up.
* The {@see Collections::parameterTypeTokensBC()} method supports PHPCS 2.6.0 and up.
Expand All @@ -773,6 +775,7 @@ public static function nullsafeObjectOperatorBC()
* @since 1.0.0-alpha4 This method replaces the {@see Collections::$parameterTypeTokens} property.
* @since 1.0.0-alpha4 Added support for PHP 8.0 union types.
* @since 1.0.0-alpha4 Added support for PHP 8.0 identifier name tokens.
* @since 1.0.0-alpha4 Added the T_TYPE_UNION token for union type support in PHPCS > 3.6.0.
*
* @return array <int|string> => <int|string>
*/
Expand All @@ -784,11 +787,16 @@ public static function parameterTypeTokens()
\T_PARENT => \T_PARENT,
\T_FALSE => \T_FALSE, // Union types only.
\T_NULL => \T_NULL, // Union types only.
\T_BITWISE_OR => \T_BITWISE_OR, // Union types.
\T_BITWISE_OR => \T_BITWISE_OR, // Union types for PHPCS < 3.6.0.
];

$tokens += self::namespacedNameTokens();

// PHPCS > 3.6.0: a new token was introduced for the union type separator.
if (\defined('T_TYPE_UNION') === true) {
$tokens[\T_TYPE_UNION] = \T_TYPE_UNION;
}

return $tokens;
}

Expand Down Expand Up @@ -829,6 +837,8 @@ public static function parameterTypeTokensBC()
/**
* Token types which can be encountered in a property type declaration.
*
* Note: this is a method, not a property as the `T_TYPE_UNION` token for PHP 8.0 union types may not exist.
*
* Sister-method to the {@see Collections::propertyTypeTokensBC()} method.
* This method supports PHPCS 3.3.0 and up.
* The {@see Collections::propertyTypeTokensBC()} method supports PHPCS 2.6.0 and up.
Expand All @@ -846,6 +856,7 @@ public static function parameterTypeTokensBC()
* @since 1.0.0-alpha4 This method replaces the {@see Collections::$propertyTypeTokens} property.
* @since 1.0.0-alpha4 Added support for PHP 8.0 union types.
* @since 1.0.0-alpha4 Added support for PHP 8.0 identifier name tokens.
* @since 1.0.0-alpha4 Added the T_TYPE_UNION token for union type support in PHPCS > 3.6.0.
*
* @return array <int|string> => <int|string>
*/
Expand All @@ -857,11 +868,16 @@ public static function propertyTypeTokens()
\T_PARENT => \T_PARENT,
\T_FALSE => \T_FALSE, // Union types only.
\T_NULL => \T_NULL, // Union types only.
\T_BITWISE_OR => \T_BITWISE_OR, // Union types.
\T_BITWISE_OR => \T_BITWISE_OR, // Union types for PHPCS < 3.6.0.
];

$tokens += self::namespacedNameTokens();

// PHPCS > 3.6.0: a new token was introduced for the union type separator.
if (\defined('T_TYPE_UNION') === true) {
$tokens[\T_TYPE_UNION] = \T_TYPE_UNION;
}

return $tokens;
}

Expand Down Expand Up @@ -902,6 +918,8 @@ public static function propertyTypeTokensBC()
/**
* Token types which can be encountered in a return type declaration.
*
* Note: this is a method, not a property as the `T_TYPE_UNION` token for PHP 8.0 union types may not exist.
*
* Sister-method to the {@see Collections::returnTypeTokensBC()} method.
* This method supports PHPCS 3.3.0 and up.
* The {@see Collections::returnTypeTokensBC()} method supports PHPCS 2.6.0 and up.
Expand All @@ -919,6 +937,7 @@ public static function propertyTypeTokensBC()
* @since 1.0.0-alpha4 This method replaces the {@see Collections::$returnTypeTokens} property.
* @since 1.0.0-alpha4 Added support for PHP 8.0 union types.
* @since 1.0.0-alpha4 Added support for PHP 8.0 identifier name tokens.
* @since 1.0.0-alpha4 Added the T_TYPE_UNION token for union type support in PHPCS > 3.6.0.
*
* @return array <int|string> => <int|string>
*/
Expand All @@ -932,11 +951,16 @@ public static function returnTypeTokens()
\T_FALSE => \T_FALSE, // Union types only.
\T_NULL => \T_NULL, // Union types only.
\T_ARRAY => \T_ARRAY, // Arrow functions PHPCS < 3.5.4 + union types.
\T_BITWISE_OR => \T_BITWISE_OR, // Union types.
\T_BITWISE_OR => \T_BITWISE_OR, // Union types for PHPCS < 3.6.0.
];

$tokens += self::namespacedNameTokens();

// PHPCS > 3.6.0: a new token was introduced for the union type separator.
if (\defined('T_TYPE_UNION') === true) {
$tokens[\T_TYPE_UNION] = \T_TYPE_UNION;
}

return $tokens;
}

Expand Down
5 changes: 2 additions & 3 deletions PHPCSUtils/Utils/FunctionDeclarations.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,6 @@ public static function getName(File $phpcsFile, $stackPtr)
* - New `"return_type_end_token"` (int|false) array index.
* - To allow for backward compatible handling of arrow functions, this method will also accept
* `T_STRING` tokens and examine them to check if these are arrow functions.
* - Support for PHP 8.0 union types.
* - Support for PHP 8.0 identifier name tokens in return types, cross-version PHP & PHPCS.
*
* @see \PHP_CodeSniffer\Files\File::getMethodProperties() Original source.
Expand Down Expand Up @@ -400,7 +399,6 @@ public static function getProperties(File $phpcsFile, $stackPtr)
* - Clearer exception message when a non-closure use token was passed to the function.
* - To allow for backward compatible handling of arrow functions, this method will also accept
* `T_STRING` tokens and examine them to check if these are arrow functions.
* - Support for PHP 8.0 union types.
* - Support for PHP 8.0 constructor property promotion.
* - Support for PHP 8.0 identifier name tokens in parameter types, cross-version PHP & PHPCS.
*
Expand Down Expand Up @@ -512,7 +510,8 @@ public static function getParameters(File $phpcsFile, $stackPtr)
case 'T_NAME_QUALIFIED':
case 'T_NAME_FULLY_QUALIFIED':
case 'T_NAME_RELATIVE':
case 'T_BITWISE_OR': // Union type separator.
case 'T_BITWISE_OR': // Union type separator PHPCS < 3.6.0.
case 'T_TYPE_UNION': // Union type separator PHPCS >= 3.6.0.
if ($typeHintToken === false) {
$typeHintToken = $i;
}
Expand Down
1 change: 0 additions & 1 deletion PHPCSUtils/Utils/Variables.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ class Variables
* This will now throw the same _"$stackPtr is not a class member var"_ runtime exception as
* other non-property variables passed to the method.
* - Defensive coding against incorrect calls to this method.
* - Support for PHP 8.0 union types.
* - Support PHP 8.0 identifier name tokens in property types, cross-version PHP & PHPCS.
*
* @see \PHP_CodeSniffer\Files\File::getMemberProperties() Original source.
Expand Down
47 changes: 47 additions & 0 deletions Tests/BackCompat/BCFile/GetMemberPropertiesTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,50 @@ class NSOperatorInType {
/* testNamespaceOperatorTypeHint */
public ?namespace\Name $prop;
}

$anon = class() {
/* testPHP8UnionTypesSimple */
public int|float $unionTypeSimple;

/* testPHP8UnionTypesTwoClasses */
private MyClassA|\Package\MyClassB $unionTypesTwoClasses;

/* testPHP8UnionTypesAllBaseTypes */
protected array|bool|int|float|NULL|object|string $unionTypesAllBaseTypes;

/* testPHP8UnionTypesAllPseudoTypes */
// Intentional fatal error - mixing types which cannot be combined, but that's not the concern of the method.
var false|mixed|self|parent|iterable|Resource $unionTypesAllPseudoTypes;

/* testPHP8UnionTypesIllegalTypes */
// Intentional fatal error - types which are not allowed for properties, but that's not the concern of the method.
public callable|static|void $unionTypesIllegalTypes;

/* testPHP8UnionTypesNullable */
// Intentional fatal error - nullability is not allowed with union types, but that's not the concern of the method.
public ?int|float $unionTypesNullable;

/* testPHP8PseudoTypeNull */
// Intentional fatal error - null pseudotype is only allowed in union types, but that's not the concern of the method.
public null $pseudoTypeNull;

/* testPHP8PseudoTypeFalse */
// Intentional fatal error - false pseudotype is only allowed in union types, but that's not the concern of the method.
public false $pseudoTypeFalse;

/* testPHP8PseudoTypeFalseAndBool */
// Intentional fatal error - false pseudotype is not allowed in combination with bool, but that's not the concern of the method.
public bool|FALSE $pseudoTypeFalseAndBool;

/* testPHP8ObjectAndClass */
// Intentional fatal error - object is not allowed in combination with class name, but that's not the concern of the method.
public object|ClassName $objectAndClass;

/* testPHP8PseudoTypeIterableAndArray */
// Intentional fatal error - iterable pseudotype is not allowed in combination with array or Traversable, but that's not the concern of the method.
public iterable|array|Traversable $pseudoTypeIterableAndArray;

/* testPHP8DuplicateTypeInUnionWhitespaceAndComment */
// 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;
};
144 changes: 144 additions & 0 deletions Tests/BackCompat/BCFile/GetMemberPropertiesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,150 @@ public function dataGetMemberProperties()
'nullable_type' => true,
],
],
'php8-union-types-simple' => [
'/* testPHP8UnionTypesSimple */',
[
'scope' => 'public',
'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' => false,
],
],
'php8-union-types-two-classes' => [
'/* testPHP8UnionTypesTwoClasses */',
[
'scope' => 'private',
'scope_specified' => true,
'is_static' => false,
'type' => 'MyClassA|\Package\MyClassB',
'type_token' => ($php8Names === true) ? -4 : -7, // Offset from the T_VARIABLE token.
'type_end_token' => -2, // Offset from the T_VARIABLE token.
'nullable_type' => false,
],
],
'php8-union-types-all-base-types' => [
'/* testPHP8UnionTypesAllBaseTypes */',
[
'scope' => 'protected',
'scope_specified' => true,
'is_static' => false,
'type' => 'array|bool|int|float|NULL|object|string',
'type_token' => -14, // Offset from the T_VARIABLE token.
'type_end_token' => -2, // Offset from the T_VARIABLE token.
'nullable_type' => false,
],
],
'php8-union-types-all-pseudo-types' => [
'/* testPHP8UnionTypesAllPseudoTypes */',
[
'scope' => 'public',
'scope_specified' => false,
'is_static' => false,
'type' => 'false|mixed|self|parent|iterable|Resource',
'type_token' => -12, // Offset from the T_VARIABLE token.
'type_end_token' => -2, // Offset from the T_VARIABLE token.
'nullable_type' => false,
],
],
'php8-union-types-illegal-types' => [
'/* testPHP8UnionTypesIllegalTypes */',
[
'scope' => 'public',
'scope_specified' => true,
'is_static' => false,
'type' => 'callable||void', // Missing static, but that's OK as not an allowed syntax.
'type_token' => -6, // Offset from the T_VARIABLE token.
'type_end_token' => -2, // Offset from the T_VARIABLE token.
'nullable_type' => false,
],
],
'php8-union-types-nullable' => [
'/* testPHP8UnionTypesNullable */',
[
'scope' => 'public',
'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-union-types-pseudo-type-null' => [
'/* testPHP8PseudoTypeNull */',
[
'scope' => 'public',
'scope_specified' => true,
'is_static' => false,
'type' => 'null',
'type_token' => -2, // Offset from the T_VARIABLE token.
'type_end_token' => -2, // Offset from the T_VARIABLE token.
'nullable_type' => false,
],
],
'php8-union-types-pseudo-type-false' => [
'/* testPHP8PseudoTypeFalse */',
[
'scope' => 'public',
'scope_specified' => true,
'is_static' => false,
'type' => 'false',
'type_token' => -2, // Offset from the T_VARIABLE token.
'type_end_token' => -2, // Offset from the T_VARIABLE token.
'nullable_type' => false,
],
],
'php8-union-types-pseudo-type-false-and-bool' => [
'/* testPHP8PseudoTypeFalseAndBool */',
[
'scope' => 'public',
'scope_specified' => true,
'is_static' => false,
'type' => 'bool|FALSE',
'type_token' => -4, // Offset from the T_VARIABLE token.
'type_end_token' => -2, // Offset from the T_VARIABLE token.
'nullable_type' => false,
],
],
'php8-union-types-object-and-class' => [
'/* testPHP8ObjectAndClass */',
[
'scope' => 'public',
'scope_specified' => true,
'is_static' => false,
'type' => 'object|ClassName',
'type_token' => -4, // Offset from the T_VARIABLE token.
'type_end_token' => -2, // Offset from the T_VARIABLE token.
'nullable_type' => false,
],
],
'php8-union-types-pseudo-type-iterable-and-array' => [
'/* testPHP8PseudoTypeIterableAndArray */',
[
'scope' => 'public',
'scope_specified' => true,
'is_static' => false,
'type' => 'iterable|array|Traversable',
'type_token' => -6, // Offset from the T_VARIABLE token.
'type_end_token' => -2, // Offset from the T_VARIABLE token.
'nullable_type' => false,
],
],
'php8-union-types-duplicate-type-with-whitespace-and-comments' => [
'/* testPHP8DuplicateTypeInUnionWhitespaceAndComment */',
[
'scope' => 'public',
'scope_specified' => true,
'is_static' => false,
'type' => 'int|string|INT',
'type_token' => -10, // Offset from the T_VARIABLE token.
'type_end_token' => -2, // Offset from the T_VARIABLE token.
'nullable_type' => false,
],
],
];
}

Expand Down
Loading