Skip to content

Commit 00faa60

Browse files
jrfnlgsherwood
authored andcommitted
File::getMethodParameters(): allow for readonly properties without visibility
Follow up on PR 3516 which was included in PHPCS 3.7.0. Turns out that constructor property promotion also allows for declaring properties with the `readonly` keyword, but without explicit visibility set. See: https://3v4l.org/nli62 Readonly properties without explicit visibility are already handled correctly in the `File::getMemberProperties()` method, but were not handled correctly in the `File::getMethodParameters()` method. Fixed now. Includes updated documentation and a unit test.
1 parent ce78f87 commit 00faa60

File tree

3 files changed

+60
-11
lines changed

3 files changed

+60
-11
lines changed

src/Files/File.php

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1261,10 +1261,12 @@ public function getDeclarationName($stackPtr)
12611261
* 'default_equal_token' => integer, // The stack pointer to the equals sign.
12621262
*
12631263
* Parameters declared using PHP 8 constructor property promotion, have these additional array indexes:
1264-
* 'property_visibility' => string, // The property visibility as declared.
1265-
* 'visibility_token' => integer, // The stack pointer to the visibility modifier token.
1266-
* 'property_readonly' => bool, // TRUE if the readonly keyword was found.
1267-
* 'readonly_token' => integer, // The stack pointer to the readonly modifier token.
1264+
* 'property_visibility' => string, // The property visibility as declared.
1265+
* 'visibility_token' => integer|false, // The stack pointer to the visibility modifier token
1266+
* // or FALSE if the visibility is not explicitly declared.
1267+
* 'property_readonly' => boolean, // TRUE if the readonly keyword was found.
1268+
* 'readonly_token' => integer, // The stack pointer to the readonly modifier token.
1269+
* // This index will only be set if the property is readonly.
12681270
*
12691271
* @param int $stackPtr The position in the stack of the function token
12701272
* to acquire the parameters for.
@@ -1476,15 +1478,20 @@ public function getMethodParameters($stackPtr)
14761478
$vars[$paramCount]['type_hint_end_token'] = $typeHintEndToken;
14771479
$vars[$paramCount]['nullable_type'] = $nullableType;
14781480

1479-
if ($visibilityToken !== null) {
1480-
$vars[$paramCount]['property_visibility'] = $this->tokens[$visibilityToken]['content'];
1481-
$vars[$paramCount]['visibility_token'] = $visibilityToken;
1481+
if ($visibilityToken !== null || $readonlyToken !== null) {
1482+
$vars[$paramCount]['property_visibility'] = 'public';
1483+
$vars[$paramCount]['visibility_token'] = false;
14821484
$vars[$paramCount]['property_readonly'] = false;
1483-
}
14841485

1485-
if ($readonlyToken !== null) {
1486-
$vars[$paramCount]['property_readonly'] = true;
1487-
$vars[$paramCount]['readonly_token'] = $readonlyToken;
1486+
if ($visibilityToken !== null) {
1487+
$vars[$paramCount]['property_visibility'] = $this->tokens[$visibilityToken]['content'];
1488+
$vars[$paramCount]['visibility_token'] = $visibilityToken;
1489+
}
1490+
1491+
if ($readonlyToken !== null) {
1492+
$vars[$paramCount]['property_readonly'] = true;
1493+
$vars[$paramCount]['readonly_token'] = $readonlyToken;
1494+
}
14881495
}
14891496

14901497
if ($this->tokens[$i]['code'] === T_COMMA) {

tests/Core/File/GetMethodParametersTest.inc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,11 @@ class ConstructorPropertyPromotionWithReadOnly {
119119
public function __construct(public readonly ?int $promotedProp, readonly private string|bool &$promotedToo) {}
120120
}
121121

122+
class ConstructorPropertyPromotionWithOnlyReadOnly {
123+
/* testPHP81ConstructorPropertyPromotionWithOnlyReadOnly */
124+
public function __construct(readonly Foo&Bar $promotedProp, readonly ?bool $promotedToo,) {}
125+
}
126+
122127
/* testPHP8ConstructorPropertyPromotionGlobalFunction */
123128
// Intentional fatal error. Property promotion not allowed in non-constructor, but that's not the concern of this method.
124129
function globalFunction(private $x) {}

tests/Core/File/GetMethodParametersTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,43 @@ public function testPHP81ConstructorPropertyPromotionWithReadOnly()
910910
}//end testPHP81ConstructorPropertyPromotionWithReadOnly()
911911

912912

913+
/**
914+
* Verify recognition of PHP8 constructor with property promotion using PHP 8.1 readonly
915+
* keyword without explicit visibility.
916+
*
917+
* @return void
918+
*/
919+
public function testPHP81ConstructorPropertyPromotionWithOnlyReadOnly()
920+
{
921+
$expected = [];
922+
$expected[0] = [
923+
'name' => '$promotedProp',
924+
'content' => 'readonly Foo&Bar $promotedProp',
925+
'has_attributes' => false,
926+
'pass_by_reference' => false,
927+
'variable_length' => false,
928+
'type_hint' => 'Foo&Bar',
929+
'nullable_type' => false,
930+
'property_visibility' => 'public',
931+
'property_readonly' => true,
932+
];
933+
$expected[1] = [
934+
'name' => '$promotedToo',
935+
'content' => 'readonly ?bool $promotedToo',
936+
'has_attributes' => false,
937+
'pass_by_reference' => false,
938+
'variable_length' => false,
939+
'type_hint' => '?bool',
940+
'nullable_type' => true,
941+
'property_visibility' => 'public',
942+
'property_readonly' => true,
943+
];
944+
945+
$this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected);
946+
947+
}//end testPHP81ConstructorPropertyPromotionWithOnlyReadOnly()
948+
949+
913950
/**
914951
* Verify behaviour when a non-constructor function uses PHP 8 property promotion syntax.
915952
*

0 commit comments

Comments
 (0)