diff --git a/src/Files/File.php b/src/Files/File.php index 021949cb67..b0e67cec10 100644 --- a/src/Files/File.php +++ b/src/Files/File.php @@ -1759,6 +1759,7 @@ public function getMethodProperties($stackPtr) * 'scope' => string, // Public, private, or protected. * 'scope_specified' => boolean, // TRUE if the scope was explicitly specified. * 'is_static' => boolean, // TRUE if the static keyword was found. + * 'is_readonly' => boolean, // TRUE if the readonly keyword was found. * 'type' => string, // The type of the var (empty if no type specified). * 'type_token' => integer, // The stack pointer to the start of the type * // or FALSE if there is no type. diff --git a/tests/Core/File/GetMemberPropertiesTest.inc b/tests/Core/File/GetMemberPropertiesTest.inc index 3daf52c998..fe37230509 100644 --- a/tests/Core/File/GetMemberPropertiesTest.inc +++ b/tests/Core/File/GetMemberPropertiesTest.inc @@ -1,278 +1,282 @@ - 'a', 'b' => 'b' ), - /* testGroupPrivate 3 */ - $varQ = 'string', - /* testGroupPrivate 4 */ - $varR = 123, - /* testGroupPrivate 5 */ - $varS = ONE / self::THREE, - /* testGroupPrivate 6 */ - $varT = [ - 'a' => 'a', - 'b' => 'b' - ], - /* testGroupPrivate 7 */ - $varU = __DIR__ . "/base"; - - - /* testMethodParam */ - public function methodName($param) { - /* testImportedGlobal */ - global $importedGlobal = true; - - /* testLocalVariable */ - $localVariable = true; - } - - /* testPropertyAfterMethod */ - private static $varV = true; - - /* testMessyNullableType */ - public /* comment - */ ? //comment - array $foo = []; - - /* testNamespaceType */ - public \MyNamespace\MyClass $foo; - - /* testNullableNamespaceType 1 */ - private ?ClassName $nullableClassType; - - /* testNullableNamespaceType 2 */ - protected ?Folder\ClassName $nullableClassType2; - - /* testMultilineNamespaceType */ - public \MyNamespace /** comment *\/ comment */ - \MyClass /* comment */ - \Foo $foo; - -} - -interface Base -{ - /* testInterfaceProperty */ - protected $anonymous; -} - -/* testGlobalVariable */ -$globalVariable = true; - -/* testNotAVariable */ -return; - -$a = ( $foo == $bar ? new stdClass() : - new class() { - /* testNestedProperty 1 */ - public $var = true; - - /* testNestedMethodParam 1 */ - public function something($var = false) {} - } -); - -function_call( 'param', new class { - /* testNestedProperty 2 */ - public $year = 2017; - - /* testNestedMethodParam 2 */ - public function __construct( $open, $post_id ) {} -}, 10, 2 ); - -class PHP8Mixed { - /* testPHP8MixedTypeHint */ - public static miXed $mixed; - - /* testPHP8MixedTypeHintNullable */ - // Intentional fatal error - nullability is not allowed with mixed, but that's not the concern of the method. - private ?mixed $nullableMixed; -} - -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; - - /* testPHP81NotReadonly */ - private string $notReadonly; - /* testPHP81Readonly */ - public readonly int $readonly; -}; - -$anon = class { - /* testPHP8PropertySingleAttribute */ - #[PropertyWithAttribute] - public string $foo; - - /* testPHP8PropertyMultipleAttributes */ - #[PropertyWithAttribute(foo: 'bar'), MyAttribute] - protected ?int|float $bar; - - /* testPHP8PropertyMultilineAttribute */ - #[ - PropertyWithAttribute(/* comment */ 'baz') - ] - private mixed $baz; -}; - -enum Suit -{ - /* testEnumProperty */ - protected $anonymous; -} - -enum Direction implements ArrayAccess -{ - case Up; - case Down; - - /* testEnumMethodParamNotProperty */ - public function offsetGet($val) { ... } -} + 'a', 'b' => 'b' ), + /* testGroupPrivate 3 */ + $varQ = 'string', + /* testGroupPrivate 4 */ + $varR = 123, + /* testGroupPrivate 5 */ + $varS = ONE / self::THREE, + /* testGroupPrivate 6 */ + $varT = [ + 'a' => 'a', + 'b' => 'b' + ], + /* testGroupPrivate 7 */ + $varU = __DIR__ . "/base"; + + + /* testMethodParam */ + public function methodName($param) { + /* testImportedGlobal */ + global $importedGlobal = true; + + /* testLocalVariable */ + $localVariable = true; + } + + /* testPropertyAfterMethod */ + private static $varV = true; + + /* testMessyNullableType */ + public /* comment + */ ? //comment + array $foo = []; + + /* testNamespaceType */ + public \MyNamespace\MyClass $foo; + + /* testNullableNamespaceType 1 */ + private ?ClassName $nullableClassType; + + /* testNullableNamespaceType 2 */ + protected ?Folder\ClassName $nullableClassType2; + + /* testMultilineNamespaceType */ + public \MyNamespace /** comment *\/ comment */ + \MyClass /* comment */ + \Foo $foo; + +} + +interface Base +{ + /* testInterfaceProperty */ + protected $anonymous; +} + +/* testGlobalVariable */ +$globalVariable = true; + +/* testNotAVariable */ +return; + +$a = ( $foo == $bar ? new stdClass() : + new class() { + /* testNestedProperty 1 */ + public $var = true; + + /* testNestedMethodParam 1 */ + public function something($var = false) {} + } +); + +function_call( 'param', new class { + /* testNestedProperty 2 */ + public $year = 2017; + + /* testNestedMethodParam 2 */ + public function __construct( $open, $post_id ) {} +}, 10, 2 ); + +class PHP8Mixed { + /* testPHP8MixedTypeHint */ + public static miXed $mixed; + + /* testPHP8MixedTypeHintNullable */ + // Intentional fatal error - nullability is not allowed with mixed, but that's not the concern of the method. + private ?mixed $nullableMixed; +} + +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; + + /* testPHP81Readonly */ + public readonly int $readonly; + + /* testPHP81ReadonlyWithNullableType */ + public readonly ?array $array; + + /* testPHP81ReadonlyWithUnionType */ + protected ReadOnly string|null $array; +}; + +$anon = class { + /* testPHP8PropertySingleAttribute */ + #[PropertyWithAttribute] + public string $foo; + + /* testPHP8PropertyMultipleAttributes */ + #[PropertyWithAttribute(foo: 'bar'), MyAttribute] + protected ?int|float $bar; + + /* testPHP8PropertyMultilineAttribute */ + #[ + PropertyWithAttribute(/* comment */ 'baz') + ] + private mixed $baz; +}; + +enum Suit +{ + /* testEnumProperty */ + protected $anonymous; +} + +enum Direction implements ArrayAccess +{ + case Up; + case Down; + + /* testEnumMethodParamNotProperty */ + public function offsetGet($val) { ... } +} diff --git a/tests/Core/File/GetMemberPropertiesTest.php b/tests/Core/File/GetMemberPropertiesTest.php index d05814239d..cc709e69c7 100644 --- a/tests/Core/File/GetMemberPropertiesTest.php +++ b/tests/Core/File/GetMemberPropertiesTest.php @@ -51,6 +51,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => false, 'is_static' => false, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -61,6 +62,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => false, 'is_static' => false, + 'is_readonly' => false, 'type' => '?int', 'nullable_type' => true, ], @@ -71,6 +73,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -81,6 +84,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => 'string', 'nullable_type' => false, ], @@ -91,6 +95,7 @@ public function dataGetMemberProperties() 'scope' => 'protected', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -101,6 +106,7 @@ public function dataGetMemberProperties() 'scope' => 'protected', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => 'bool', 'nullable_type' => false, ], @@ -111,6 +117,7 @@ public function dataGetMemberProperties() 'scope' => 'private', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -121,6 +128,7 @@ public function dataGetMemberProperties() 'scope' => 'private', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => 'array', 'nullable_type' => false, ], @@ -131,6 +139,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => false, 'is_static' => true, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -141,6 +150,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => false, 'is_static' => true, + 'is_readonly' => false, 'type' => '?string', 'nullable_type' => true, ], @@ -151,6 +161,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => false, 'is_static' => true, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -161,6 +172,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => false, 'is_static' => true, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -171,6 +183,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => true, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -181,6 +194,7 @@ public function dataGetMemberProperties() 'scope' => 'protected', 'scope_specified' => true, 'is_static' => true, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -191,6 +205,7 @@ public function dataGetMemberProperties() 'scope' => 'private', 'scope_specified' => true, 'is_static' => true, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -201,6 +216,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => true, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -211,6 +227,7 @@ public function dataGetMemberProperties() 'scope' => 'protected', 'scope_specified' => true, 'is_static' => true, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -221,6 +238,7 @@ public function dataGetMemberProperties() 'scope' => 'private', 'scope_specified' => true, 'is_static' => true, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -231,6 +249,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => 'float', 'nullable_type' => false, ], @@ -241,6 +260,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => 'float', 'nullable_type' => false, ], @@ -251,6 +271,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => true, + 'is_readonly' => false, 'type' => '?string', 'nullable_type' => true, ], @@ -261,6 +282,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => true, + 'is_readonly' => false, 'type' => '?string', 'nullable_type' => true, ], @@ -271,6 +293,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => false, 'is_static' => false, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -281,6 +304,7 @@ public function dataGetMemberProperties() 'scope' => 'protected', 'scope_specified' => true, 'is_static' => true, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -291,6 +315,7 @@ public function dataGetMemberProperties() 'scope' => 'protected', 'scope_specified' => true, 'is_static' => true, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -301,6 +326,7 @@ public function dataGetMemberProperties() 'scope' => 'protected', 'scope_specified' => true, 'is_static' => true, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -311,6 +337,7 @@ public function dataGetMemberProperties() 'scope' => 'private', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -321,6 +348,7 @@ public function dataGetMemberProperties() 'scope' => 'private', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -331,6 +359,7 @@ public function dataGetMemberProperties() 'scope' => 'private', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -341,6 +370,7 @@ public function dataGetMemberProperties() 'scope' => 'private', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -351,6 +381,7 @@ public function dataGetMemberProperties() 'scope' => 'private', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -361,6 +392,7 @@ public function dataGetMemberProperties() 'scope' => 'private', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -371,6 +403,7 @@ public function dataGetMemberProperties() 'scope' => 'private', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -381,6 +414,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => '?array', 'nullable_type' => true, ], @@ -391,6 +425,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => '\MyNamespace\MyClass', 'nullable_type' => false, ], @@ -401,6 +436,7 @@ public function dataGetMemberProperties() 'scope' => 'private', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => '?ClassName', 'nullable_type' => true, ], @@ -411,6 +447,7 @@ public function dataGetMemberProperties() 'scope' => 'protected', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => '?Folder\ClassName', 'nullable_type' => true, ], @@ -421,6 +458,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => '\MyNamespace\MyClass\Foo', 'nullable_type' => false, ], @@ -431,6 +469,7 @@ public function dataGetMemberProperties() 'scope' => 'private', 'scope_specified' => true, 'is_static' => true, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -445,6 +484,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -455,6 +495,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => '', 'nullable_type' => false, ], @@ -465,6 +506,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => true, + 'is_readonly' => false, 'type' => 'miXed', 'nullable_type' => false, ], @@ -475,6 +517,7 @@ public function dataGetMemberProperties() 'scope' => 'private', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => '?mixed', 'nullable_type' => true, ], @@ -485,6 +528,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => '?namespace\Name', 'nullable_type' => true, ], @@ -495,6 +539,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => 'int|float', 'nullable_type' => false, ], @@ -505,6 +550,7 @@ public function dataGetMemberProperties() 'scope' => 'private', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => 'MyClassA|\Package\MyClassB', 'nullable_type' => false, ], @@ -515,6 +561,7 @@ public function dataGetMemberProperties() 'scope' => 'protected', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => 'array|bool|int|float|NULL|object|string', 'nullable_type' => false, ], @@ -525,6 +572,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => false, 'is_static' => false, + 'is_readonly' => false, 'type' => 'false|mixed|self|parent|iterable|Resource', 'nullable_type' => false, ], @@ -535,6 +583,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, // Missing static, but that's OK as not an allowed syntax. 'type' => 'callable||void', 'nullable_type' => false, @@ -546,6 +595,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => '?int|float', 'nullable_type' => true, ], @@ -556,6 +606,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => 'null', 'nullable_type' => false, ], @@ -566,6 +617,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => 'false', 'nullable_type' => false, ], @@ -576,6 +628,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => 'bool|FALSE', 'nullable_type' => false, ], @@ -586,6 +639,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => 'object|ClassName', 'nullable_type' => false, ], @@ -596,6 +650,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => 'iterable|array|Traversable', 'nullable_type' => false, ], @@ -606,29 +661,41 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => 'int|string|INT', 'nullable_type' => false, ], ], [ - '/* testPHP81NotReadonly */', + '/* testPHP81Readonly */', [ - 'scope' => 'private', + 'scope' => 'public', 'scope_specified' => true, 'is_static' => false, - 'is_readonly' => false, - 'type' => 'string', + 'is_readonly' => true, + 'type' => 'int', 'nullable_type' => false, ], ], [ - '/* testPHP81Readonly */', + '/* testPHP81ReadonlyWithNullableType */', [ 'scope' => 'public', 'scope_specified' => true, 'is_static' => false, 'is_readonly' => true, - 'type' => 'int', + 'type' => '?array', + 'nullable_type' => true, + ], + ], + [ + '/* testPHP81ReadonlyWithUnionType */', + [ + 'scope' => 'protected', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => true, + 'type' => 'string|null', 'nullable_type' => false, ], ], @@ -638,6 +705,7 @@ public function dataGetMemberProperties() 'scope' => 'public', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => 'string', 'nullable_type' => false, ], @@ -648,6 +716,7 @@ public function dataGetMemberProperties() 'scope' => 'protected', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => '?int|float', 'nullable_type' => true, ], @@ -658,6 +727,7 @@ public function dataGetMemberProperties() 'scope' => 'private', 'scope_specified' => true, 'is_static' => false, + 'is_readonly' => false, 'type' => 'mixed', 'nullable_type' => false, ],