diff --git a/src/Files/File.php b/src/Files/File.php index e63d09f57c..fe3b7b1c66 100644 --- a/src/Files/File.php +++ b/src/Files/File.php @@ -1354,6 +1354,10 @@ public function getDeclarationName($stackPtr) * 'readonly_token' => integer, // The stack pointer to the readonly modifier token. * // This index will only be set if the property is readonly. * + * ... and if the promoted property uses asymmetric visibility, these additional array indexes will also be available: + * 'set_visibility' => string, // The property set-visibility as declared. + * 'set_visibility_token' => integer, // The stack pointer to the set-visibility modifier token. + * * @param int $stackPtr The position in the stack of the function token * to acquire the parameters for. * @@ -1406,10 +1410,11 @@ public function getMethodParameters($stackPtr) $variadicToken = false; $typeHint = ''; $typeHintToken = false; - $typeHintEndToken = false; - $nullableType = false; - $visibilityToken = null; - $readonlyToken = null; + $typeHintEndToken = false; + $nullableType = false; + $visibilityToken = null; + $setVisibilityToken = null; + $readonlyToken = null; for ($i = $paramStart; $i <= $closer; $i++) { // Check to see if this token has a parenthesis or bracket opener. If it does @@ -1541,6 +1546,13 @@ public function getMethodParameters($stackPtr) $visibilityToken = $i; } break; + case T_PUBLIC_SET: + case T_PROTECTED_SET: + case T_PRIVATE_SET: + if ($defaultStart === null) { + $setVisibilityToken = $i; + } + break; case T_READONLY: if ($defaultStart === null) { $readonlyToken = $i; @@ -1575,16 +1587,21 @@ public function getMethodParameters($stackPtr) $vars[$paramCount]['type_hint_end_token'] = $typeHintEndToken; $vars[$paramCount]['nullable_type'] = $nullableType; - if ($visibilityToken !== null || $readonlyToken !== null) { + if ($visibilityToken !== null || $setVisibilityToken !== null || $readonlyToken !== null) { $vars[$paramCount]['property_visibility'] = 'public'; $vars[$paramCount]['visibility_token'] = false; - $vars[$paramCount]['property_readonly'] = false; if ($visibilityToken !== null) { $vars[$paramCount]['property_visibility'] = $this->tokens[$visibilityToken]['content']; $vars[$paramCount]['visibility_token'] = $visibilityToken; } + if ($setVisibilityToken !== null) { + $vars[$paramCount]['set_visibility'] = $this->tokens[$setVisibilityToken]['content']; + $vars[$paramCount]['set_visibility_token'] = $setVisibilityToken; + } + + $vars[$paramCount]['property_readonly'] = false; if ($readonlyToken !== null) { $vars[$paramCount]['property_readonly'] = true; $vars[$paramCount]['readonly_token'] = $readonlyToken; @@ -1598,21 +1615,22 @@ public function getMethodParameters($stackPtr) } // Reset the vars, as we are about to process the next parameter. - $currVar = null; - $paramStart = ($i + 1); - $defaultStart = null; - $equalToken = null; - $hasAttributes = false; - $passByReference = false; - $referenceToken = false; - $variableLength = false; - $variadicToken = false; - $typeHint = ''; - $typeHintToken = false; - $typeHintEndToken = false; - $nullableType = false; - $visibilityToken = null; - $readonlyToken = null; + $currVar = null; + $paramStart = ($i + 1); + $defaultStart = null; + $equalToken = null; + $hasAttributes = false; + $passByReference = false; + $referenceToken = false; + $variableLength = false; + $variadicToken = false; + $typeHint = ''; + $typeHintToken = false; + $typeHintEndToken = false; + $nullableType = false; + $visibilityToken = null; + $setVisibilityToken = null; + $readonlyToken = null; $paramCount++; break; @@ -1827,6 +1845,9 @@ public function getMethodProperties($stackPtr) * array( * 'scope' => string, // Public, private, or protected. * 'scope_specified' => boolean, // TRUE if the scope was explicitly specified. + * 'set_scope' => string|false, // Scope for asymmetric visibility. + * // Either public, private, or protected or + * // FALSE if no set scope is specified. * 'is_static' => boolean, // TRUE if the static keyword was found. * 'is_readonly' => boolean, // TRUE if the readonly keyword was found. * 'is_final' => boolean, // TRUE if the final keyword was found. @@ -1896,19 +1917,18 @@ public function getMemberProperties($stackPtr) } $valid = [ - T_PUBLIC => T_PUBLIC, - T_PRIVATE => T_PRIVATE, - T_PROTECTED => T_PROTECTED, - T_STATIC => T_STATIC, - T_VAR => T_VAR, - T_READONLY => T_READONLY, - T_FINAL => T_FINAL, + T_STATIC => T_STATIC, + T_VAR => T_VAR, + T_READONLY => T_READONLY, + T_FINAL => T_FINAL, ]; + $valid += Tokens::$scopeModifiers; $valid += Tokens::$emptyTokens; $scope = 'public'; $scopeSpecified = false; + $setScope = false; $isStatic = false; $isReadonly = false; $isFinal = false; @@ -1941,6 +1961,15 @@ public function getMemberProperties($stackPtr) $scope = 'protected'; $scopeSpecified = true; break; + case T_PUBLIC_SET: + $setScope = 'public'; + break; + case T_PROTECTED_SET: + $setScope = 'protected'; + break; + case T_PRIVATE_SET: + $setScope = 'private'; + break; case T_STATIC: $isStatic = true; break; @@ -2004,6 +2033,7 @@ public function getMemberProperties($stackPtr) return [ 'scope' => $scope, 'scope_specified' => $scopeSpecified, + 'set_scope' => $setScope, 'is_static' => $isStatic, 'is_readonly' => $isReadonly, 'is_final' => $isFinal, diff --git a/src/Standards/PSR2/Tests/Classes/PropertyDeclarationUnitTest.php b/src/Standards/PSR2/Tests/Classes/PropertyDeclarationUnitTest.php index 70abcec928..41b3626afb 100644 --- a/src/Standards/PSR2/Tests/Classes/PropertyDeclarationUnitTest.php +++ b/src/Standards/PSR2/Tests/Classes/PropertyDeclarationUnitTest.php @@ -64,8 +64,6 @@ public function getErrorList() 101 => 2, 105 => 1, 107 => 1, - 109 => 1, - 111 => 1, ]; }//end getErrorList() diff --git a/tests/Core/Files/File/GetMemberPropertiesTest.inc b/tests/Core/Files/File/GetMemberPropertiesTest.inc index c858826607..b5fcea9406 100644 --- a/tests/Core/Files/File/GetMemberPropertiesTest.inc +++ b/tests/Core/Files/File/GetMemberPropertiesTest.inc @@ -375,3 +375,29 @@ class WithFinalProperties { /* testPHP84FinalComplexTypedProp */ final public (Foo&\Bar)|bool $val9; } + +class AsymVisibility { + /* testPHP84AsymPublicSetProperty */ + public(set) mixed $prop1; + /* testPHP84AsymPublicPublicSetProperty */ + public public(set) (A&B)|null $prop2; + /* testPHP84AsymPublicSetPublicProperty */ + public(set) public bool $prop3; + + /* testPHP84AsymProtectedSetProperty */ + protected(set) readonly mixed $prop4; + /* testPHP84AsymPublicProtectedSetProperty */ + public protected(set) string $prop5; + /* testPHP84AsymProtectedSetPublicProperty */ + protected(set) public ?float $prop6; + + /* testPHP84AsymPrivateSetProperty */ + private(set) string|int $prop7; + /* testPHP84AsymProtectedPrivateSetProperty */ + final protected private(set) $prop8; + /* testPHP84AsymPrivateSetPublicProperty */ + private(set) public mixed $prop9; + + /* testPHP84IllegalAsymPublicProtectedSetStaticProperty */ + public protected(set) static mixed $prop10; +} diff --git a/tests/Core/Files/File/GetMemberPropertiesTest.php b/tests/Core/Files/File/GetMemberPropertiesTest.php index 8fa034d75b..3c218911b2 100644 --- a/tests/Core/Files/File/GetMemberPropertiesTest.php +++ b/tests/Core/Files/File/GetMemberPropertiesTest.php @@ -68,6 +68,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => false, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -82,6 +83,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => false, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -96,6 +98,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -110,6 +113,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -124,6 +128,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'protected', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -138,6 +143,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'protected', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -152,6 +158,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'private', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -166,6 +173,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'private', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -180,6 +188,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => false, + 'set_scope' => false, 'is_static' => true, 'is_readonly' => false, 'is_final' => false, @@ -194,6 +203,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => false, + 'set_scope' => false, 'is_static' => true, 'is_readonly' => false, 'is_final' => false, @@ -208,6 +218,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => false, + 'set_scope' => false, 'is_static' => true, 'is_readonly' => false, 'is_final' => false, @@ -222,6 +233,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => false, + 'set_scope' => false, 'is_static' => true, 'is_readonly' => false, 'is_final' => false, @@ -236,6 +248,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => true, 'is_readonly' => false, 'is_final' => false, @@ -250,6 +263,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'protected', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => true, 'is_readonly' => false, 'is_final' => false, @@ -264,6 +278,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'private', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => true, 'is_readonly' => false, 'is_final' => false, @@ -278,6 +293,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => false, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -292,6 +308,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => true, 'is_readonly' => false, 'is_final' => false, @@ -306,6 +323,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'protected', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => true, 'is_readonly' => false, 'is_final' => false, @@ -320,6 +338,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'private', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => true, 'is_readonly' => false, 'is_final' => false, @@ -334,6 +353,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -348,6 +368,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -362,6 +383,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => true, 'is_readonly' => false, 'is_final' => false, @@ -376,6 +398,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => true, 'is_readonly' => false, 'is_final' => false, @@ -390,6 +413,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'protected', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => true, 'is_readonly' => false, 'is_final' => false, @@ -404,6 +428,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'protected', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => true, 'is_readonly' => false, 'is_final' => false, @@ -418,6 +443,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'protected', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => true, 'is_readonly' => false, 'is_final' => false, @@ -432,6 +458,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'private', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -446,6 +473,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'private', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -460,6 +488,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'private', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -474,6 +503,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'private', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -488,6 +518,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'private', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -502,6 +533,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'private', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -516,6 +548,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'private', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -530,6 +563,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -544,6 +578,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -558,6 +593,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'private', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -572,6 +608,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'protected', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -586,6 +623,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -600,6 +638,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'private', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => true, 'is_readonly' => false, 'is_final' => false, @@ -618,6 +657,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -632,6 +672,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -646,6 +687,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => true, 'is_readonly' => false, 'is_final' => false, @@ -660,6 +702,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'private', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -674,6 +717,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -688,6 +732,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -702,6 +747,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'private', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -716,6 +762,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'protected', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -730,6 +777,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => false, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -744,6 +792,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -759,6 +808,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -773,6 +823,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -787,6 +838,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -801,6 +853,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -815,6 +868,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -829,6 +883,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -843,6 +898,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -857,6 +913,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => true, 'is_final' => false, @@ -871,6 +928,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => true, 'is_final' => false, @@ -885,6 +943,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => true, 'is_final' => false, @@ -899,6 +958,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'protected', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => true, 'is_final' => false, @@ -913,6 +973,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => false, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => true, 'is_final' => false, @@ -927,6 +988,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => false, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => true, 'is_final' => false, @@ -941,6 +1003,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'private', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => true, 'is_readonly' => true, 'is_final' => false, @@ -955,6 +1018,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => true, 'is_readonly' => true, 'is_final' => false, @@ -969,6 +1033,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -983,6 +1048,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'protected', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -997,6 +1063,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'private', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -1015,6 +1082,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -1029,6 +1097,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -1043,6 +1112,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -1057,6 +1127,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -1072,6 +1143,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -1086,6 +1158,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -1100,6 +1173,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -1114,6 +1188,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'protected', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => true, 'is_readonly' => false, 'is_final' => false, @@ -1128,6 +1203,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'private', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -1142,6 +1218,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => false, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => true, 'is_final' => false, @@ -1157,6 +1234,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => true, 'is_readonly' => false, 'is_final' => false, @@ -1171,6 +1249,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'protected', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => true, 'is_final' => false, @@ -1185,6 +1264,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'private', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => true, 'is_final' => false, @@ -1199,6 +1279,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => false, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => false, @@ -1213,6 +1294,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => true, @@ -1227,6 +1309,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'protected', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => true, @@ -1241,6 +1324,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => true, @@ -1255,6 +1339,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => true, 'is_readonly' => false, 'is_final' => true, @@ -1269,6 +1354,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => true, 'is_final' => true, @@ -1283,6 +1369,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => false, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => true, @@ -1297,6 +1384,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => false, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => true, @@ -1311,6 +1399,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => true, @@ -1325,6 +1414,7 @@ public static function dataGetMemberProperties() 'expected' => [ 'scope' => 'public', 'scope_specified' => true, + 'set_scope' => false, 'is_static' => false, 'is_readonly' => false, 'is_final' => true, @@ -1334,6 +1424,157 @@ public static function dataGetMemberProperties() 'nullable_type' => false, ], ], + + 'php8.4-asym-public-set' => [ + 'identifier' => '/* testPHP84AsymPublicSetProperty */', + 'expected' => [ + 'scope' => 'public', + 'scope_specified' => false, + 'set_scope' => 'public', + 'is_static' => false, + 'is_readonly' => false, + 'is_final' => false, + 'type' => 'mixed', + 'type_token' => -2, + 'type_end_token' => -2, + 'nullable_type' => false, + ], + ], + 'php8.4-asym-public-public-set-dnf-type' => [ + 'identifier' => '/* testPHP84AsymPublicPublicSetProperty */', + 'expected' => [ + 'scope' => 'public', + 'scope_specified' => true, + 'set_scope' => 'public', + 'is_static' => false, + 'is_readonly' => false, + 'is_final' => false, + 'type' => '(A&B)|null', + 'type_token' => -8, + 'type_end_token' => -2, + 'nullable_type' => false, + ], + ], + 'php8.4-asym-public-set-public' => [ + 'identifier' => '/* testPHP84AsymPublicSetPublicProperty */', + 'expected' => [ + 'scope' => 'public', + 'scope_specified' => true, + 'set_scope' => 'public', + 'is_static' => false, + 'is_readonly' => false, + 'is_final' => false, + 'type' => 'bool', + 'type_token' => -2, + 'type_end_token' => -2, + 'nullable_type' => false, + ], + ], + 'php8.4-asym-protected-set-readonly' => [ + 'identifier' => '/* testPHP84AsymProtectedSetProperty */', + 'expected' => [ + 'scope' => 'public', + 'scope_specified' => false, + 'set_scope' => 'protected', + 'is_static' => false, + 'is_readonly' => true, + 'is_final' => false, + 'type' => 'mixed', + 'type_token' => -2, + 'type_end_token' => -2, + 'nullable_type' => false, + ], + ], + 'php8.4-asym-public-protected-set' => [ + 'identifier' => '/* testPHP84AsymPublicProtectedSetProperty */', + 'expected' => [ + 'scope' => 'public', + 'scope_specified' => true, + 'set_scope' => 'protected', + 'is_static' => false, + 'is_readonly' => false, + 'is_final' => false, + 'type' => 'string', + 'type_token' => -2, + 'type_end_token' => -2, + 'nullable_type' => false, + ], + ], + 'php8.4-asym-protected-set-public-nullable-type' => [ + 'identifier' => '/* testPHP84AsymProtectedSetPublicProperty */', + 'expected' => [ + 'scope' => 'public', + 'scope_specified' => true, + 'set_scope' => 'protected', + 'is_static' => false, + 'is_readonly' => false, + 'is_final' => false, + 'type' => '?float', + 'type_token' => -2, + 'type_end_token' => -2, + 'nullable_type' => true, + ], + ], + 'php8.4-asym-private-set-union-type' => [ + 'identifier' => '/* testPHP84AsymPrivateSetProperty */', + 'expected' => [ + 'scope' => 'public', + 'scope_specified' => false, + 'set_scope' => 'private', + 'is_static' => false, + 'is_readonly' => false, + 'is_final' => false, + 'type' => 'string|int', + 'type_token' => -4, + 'type_end_token' => -2, + 'nullable_type' => false, + ], + ], + 'php8.4-asym-final-protected-private-set' => [ + 'identifier' => '/* testPHP84AsymProtectedPrivateSetProperty */', + 'expected' => [ + 'scope' => 'protected', + 'scope_specified' => true, + 'set_scope' => 'private', + 'is_static' => false, + 'is_readonly' => false, + 'is_final' => true, + 'type' => '', + 'type_token' => false, + 'type_end_token' => false, + 'nullable_type' => false, + ], + ], + 'php8.4-asym-private-set-public' => [ + 'identifier' => '/* testPHP84AsymPrivateSetPublicProperty */', + 'expected' => [ + 'scope' => 'public', + 'scope_specified' => true, + 'set_scope' => 'private', + 'is_static' => false, + 'is_readonly' => false, + 'is_final' => false, + 'type' => 'mixed', + 'type_token' => -2, + 'type_end_token' => -2, + 'nullable_type' => false, + ], + ], + 'php8.4-illegal-asym-public-protected-set-static' => [ + 'identifier' => '/* testPHP84IllegalAsymPublicProtectedSetStaticProperty */', + 'expected' => [ + 'scope' => 'public', + 'scope_specified' => true, + 'set_scope' => 'protected', + 'is_static' => true, + 'is_readonly' => false, + 'is_final' => false, + 'type' => 'mixed', + 'type_token' => -2, + 'type_end_token' => -2, + 'nullable_type' => false, + ], + ], ]; }//end dataGetMemberProperties() diff --git a/tests/Core/Files/File/GetMethodParametersTest.inc b/tests/Core/Files/File/GetMethodParametersTest.inc index a4d8767efa..3a12adeaef 100644 --- a/tests/Core/Files/File/GetMethodParametersTest.inc +++ b/tests/Core/Files/File/GetMethodParametersTest.inc @@ -217,6 +217,17 @@ class ConstructorPropertyPromotionWithOnlyReadOnly { public function __construct(readonly Foo&Bar $promotedProp, readonly ?bool $promotedToo,) {} } +class ConstructorPropertyPromotionWithAsymVisibility { + /* testPHP84ConstructorPropertyPromotionWithAsymVisibility */ + public function __construct( + protected(set) string|Book $book, + public private(set) ?Publisher $publisher, + Private(set) PROTECTED Author $author, + readonly public(set) int $pubYear, + protected(set) $illegalMissingType, + ) {} +} + /* testPHP8ConstructorPropertyPromotionGlobalFunction */ // Intentional fatal error. Property promotion not allowed in non-constructor, but that's not the concern of this method. function globalFunction(private $x) {} diff --git a/tests/Core/Files/File/GetMethodParametersTest.php b/tests/Core/Files/File/GetMethodParametersTest.php index f78f2a70b2..e660275fd1 100644 --- a/tests/Core/Files/File/GetMethodParametersTest.php +++ b/tests/Core/Files/File/GetMethodParametersTest.php @@ -2190,6 +2190,122 @@ public function testPHP81ConstructorPropertyPromotionWithOnlyReadOnly() }//end testPHP81ConstructorPropertyPromotionWithOnlyReadOnly() + /** + * Verify recognition of PHP8 constructor with property promotion using PHP 8.4 asymmetric visibility. + * + * @return void + */ + public function testPHP84ConstructorPropertyPromotionWithAsymVisibility() + { + // Offsets are relative to the T_FUNCTION token. + $expected = []; + $expected[0] = [ + 'token' => 12, + 'name' => '$book', + 'content' => 'protected(set) string|Book $book', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'string|Book', + 'type_hint_token' => 8, + 'type_hint_end_token' => 10, + 'nullable_type' => false, + 'property_visibility' => 'public', + 'visibility_token' => false, + 'set_visibility' => 'protected(set)', + 'set_visibility_token' => 6, + 'property_readonly' => false, + 'comma_token' => 13, + ]; + $expected[1] = [ + 'token' => 23, + 'name' => '$publisher', + 'content' => 'public private(set) ?Publisher $publisher', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '?Publisher', + 'type_hint_token' => 21, + 'type_hint_end_token' => 21, + 'nullable_type' => true, + 'property_visibility' => 'public', + 'visibility_token' => 16, + 'set_visibility' => 'private(set)', + 'set_visibility_token' => 18, + 'property_readonly' => false, + 'comma_token' => 24, + ]; + $expected[2] = [ + 'token' => 33, + 'name' => '$author', + 'content' => 'Private(set) PROTECTED Author $author', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'Author', + 'type_hint_token' => 31, + 'type_hint_end_token' => 31, + 'nullable_type' => false, + 'property_visibility' => 'PROTECTED', + 'visibility_token' => 29, + 'set_visibility' => 'Private(set)', + 'set_visibility_token' => 27, + 'property_readonly' => false, + 'comma_token' => 34, + ]; + $expected[3] = [ + 'token' => 43, + 'name' => '$pubYear', + 'content' => 'readonly public(set) int $pubYear', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'int', + 'type_hint_token' => 41, + 'type_hint_end_token' => 41, + 'nullable_type' => false, + 'property_visibility' => 'public', + 'visibility_token' => false, + 'set_visibility' => 'public(set)', + 'set_visibility_token' => 39, + 'property_readonly' => true, + 'readonly_token' => 37, + 'comma_token' => 44, + ]; + $expected[4] = [ + 'token' => 49, + 'name' => '$illegalMissingType', + 'content' => 'protected(set) $illegalMissingType', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'property_visibility' => 'public', + 'visibility_token' => false, + 'set_visibility' => 'protected(set)', + 'set_visibility_token' => 47, + 'property_readonly' => false, + 'comma_token' => 50, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP84ConstructorPropertyPromotionWithAsymVisibility() + + /** * Verify behaviour when a non-constructor function uses PHP 8 property promotion syntax. * @@ -3214,6 +3330,10 @@ private function getMethodParametersTestHelper($commentString, $expected, $targe $expected[$key]['visibility_token'] += $target; } + if (isset($param['set_visibility_token']) === true && is_int($param['set_visibility_token']) === true) { + $expected[$key]['set_visibility_token'] += $target; + } + if (isset($param['readonly_token']) === true) { $expected[$key]['readonly_token'] += $target; }