mirror of
https://github.com/nikic/PHP-Parser.git
synced 2025-04-23 07:28:36 +02:00
Add support for new PHP 8.1 modifiers (#796)
Implement support for readonly properties (https://wiki.php.net/rfc/readonly_properties_v2) and final class contstants (https://wiki.php.net/rfc/final_class_const).
This commit is contained in:
parent
c4304c76bd
commit
55c4269232
@ -38,7 +38,7 @@ reserved_non_modifiers:
|
||||
|
||||
semi_reserved:
|
||||
reserved_non_modifiers
|
||||
| T_STATIC | T_ABSTRACT | T_FINAL | T_PRIVATE | T_PROTECTED | T_PUBLIC
|
||||
| T_STATIC | T_ABSTRACT | T_FINAL | T_PRIVATE | T_PROTECTED | T_PUBLIC | T_READONLY
|
||||
;
|
||||
|
||||
identifier_ex:
|
||||
@ -535,6 +535,7 @@ optional_visibility_modifier:
|
||||
| T_PUBLIC { $$ = Stmt\Class_::MODIFIER_PUBLIC; }
|
||||
| T_PROTECTED { $$ = Stmt\Class_::MODIFIER_PROTECTED; }
|
||||
| T_PRIVATE { $$ = Stmt\Class_::MODIFIER_PRIVATE; }
|
||||
| T_READONLY { $$ = Stmt\Class_::MODIFIER_READONLY; }
|
||||
;
|
||||
|
||||
parameter:
|
||||
@ -725,6 +726,7 @@ member_modifier:
|
||||
| T_STATIC { $$ = Stmt\Class_::MODIFIER_STATIC; }
|
||||
| T_ABSTRACT { $$ = Stmt\Class_::MODIFIER_ABSTRACT; }
|
||||
| T_FINAL { $$ = Stmt\Class_::MODIFIER_FINAL; }
|
||||
| T_READONLY { $$ = Stmt\Class_::MODIFIER_READONLY; }
|
||||
;
|
||||
|
||||
property_declaration_list:
|
||||
|
@ -74,7 +74,7 @@
|
||||
%token T_USE
|
||||
%token T_INSTEADOF
|
||||
%token T_GLOBAL
|
||||
%right T_STATIC T_ABSTRACT T_FINAL T_PRIVATE T_PROTECTED T_PUBLIC
|
||||
%right T_STATIC T_ABSTRACT T_FINAL T_PRIVATE T_PROTECTED T_PUBLIC T_READONLY
|
||||
%token T_VAR
|
||||
%token T_UNSET
|
||||
%token T_ISSET
|
||||
|
@ -77,6 +77,17 @@ class ClassConst implements PhpParser\Builder
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the constant final.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeFinal() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_FINAL);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets doc comment for the constant.
|
||||
*
|
||||
|
@ -77,6 +77,17 @@ class Property implements PhpParser\Builder
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the property readonly.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeReadonly() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_READONLY);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets default value for the property.
|
||||
*
|
||||
|
@ -443,6 +443,7 @@ class Lexer
|
||||
'T_ENUM',
|
||||
'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG',
|
||||
'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG',
|
||||
'T_READONLY',
|
||||
];
|
||||
|
||||
// PHP-Parser might be used together with another library that also emulates some or all
|
||||
@ -536,6 +537,7 @@ class Lexer
|
||||
$tokenMap[\T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG] = Tokens::T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG;
|
||||
$tokenMap[\T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG] = Tokens::T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG;
|
||||
$tokenMap[\T_ENUM] = Tokens::T_ENUM;
|
||||
$tokenMap[\T_READONLY] = Tokens::T_READONLY;
|
||||
|
||||
return $tokenMap;
|
||||
}
|
||||
@ -544,7 +546,7 @@ class Lexer
|
||||
// Based on semi_reserved production.
|
||||
return array_fill_keys([
|
||||
\T_STRING,
|
||||
\T_STATIC, \T_ABSTRACT, \T_FINAL, \T_PRIVATE, \T_PROTECTED, \T_PUBLIC,
|
||||
\T_STATIC, \T_ABSTRACT, \T_FINAL, \T_PRIVATE, \T_PROTECTED, \T_PUBLIC, \T_READONLY,
|
||||
\T_INCLUDE, \T_INCLUDE_ONCE, \T_EVAL, \T_REQUIRE, \T_REQUIRE_ONCE, \T_LOGICAL_OR, \T_LOGICAL_XOR, \T_LOGICAL_AND,
|
||||
\T_INSTANCEOF, \T_NEW, \T_CLONE, \T_EXIT, \T_IF, \T_ELSEIF, \T_ELSE, \T_ENDIF, \T_ECHO, \T_DO, \T_WHILE,
|
||||
\T_ENDWHILE, \T_FOR, \T_ENDFOR, \T_FOREACH, \T_ENDFOREACH, \T_DECLARE, \T_ENDDECLARE, \T_AS, \T_TRY, \T_CATCH,
|
||||
|
@ -13,6 +13,7 @@ use PhpParser\Lexer\TokenEmulator\FnTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\MatchTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\NullsafeTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\NumericLiteralSeparatorEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\ReadonlyTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\ReverseEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\TokenEmulator;
|
||||
|
||||
@ -53,6 +54,7 @@ class Emulative extends Lexer
|
||||
new NullsafeTokenEmulator(),
|
||||
new AttributeEmulator(),
|
||||
new EnumTokenEmulator(),
|
||||
new ReadonlyTokenEmulator(),
|
||||
];
|
||||
|
||||
// Collect emulators that are relevant for the PHP version we're running
|
||||
|
23
lib/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php
Normal file
23
lib/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
use PhpParser\Lexer\Emulative;
|
||||
|
||||
final class ReadonlyTokenEmulator extends KeywordEmulator
|
||||
{
|
||||
public function getPhpVersion(): string
|
||||
{
|
||||
return Emulative::PHP_8_1;
|
||||
}
|
||||
|
||||
public function getKeywordString(): string
|
||||
{
|
||||
return 'readonly';
|
||||
}
|
||||
|
||||
public function getKeywordToken(): int
|
||||
{
|
||||
return \T_READONLY;
|
||||
}
|
||||
}
|
@ -65,6 +65,15 @@ class ClassConst extends Node\Stmt
|
||||
return (bool) ($this->flags & Class_::MODIFIER_PRIVATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether constant is final.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isFinal() : bool {
|
||||
return (bool) ($this->flags & Class_::MODIFIER_FINAL);
|
||||
}
|
||||
|
||||
public function getType() : string {
|
||||
return 'Stmt_ClassConst';
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ class Class_ extends ClassLike
|
||||
const MODIFIER_STATIC = 8;
|
||||
const MODIFIER_ABSTRACT = 16;
|
||||
const MODIFIER_FINAL = 32;
|
||||
const MODIFIER_READONLY = 64;
|
||||
|
||||
const VISIBILITY_MODIFIER_MASK = 7; // 1 | 2 | 4
|
||||
|
||||
@ -96,6 +97,10 @@ class Class_ extends ClassLike
|
||||
throw new Error('Multiple final modifiers are not allowed');
|
||||
}
|
||||
|
||||
if ($a & self::MODIFIER_READONLY && $b & self::MODIFIER_READONLY) {
|
||||
throw new Error('Multiple readonly modifiers are not allowed');
|
||||
}
|
||||
|
||||
if ($a & 48 && $b & 48) {
|
||||
throw new Error('Cannot use the final modifier on an abstract class member');
|
||||
}
|
||||
|
@ -77,6 +77,15 @@ class Property extends Node\Stmt
|
||||
return (bool) ($this->flags & Class_::MODIFIER_STATIC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the property is readonly.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isReadonly() : bool {
|
||||
return (bool) ($this->flags & Class_::MODIFIER_READONLY);
|
||||
}
|
||||
|
||||
public function getType() : string {
|
||||
return 'Stmt_Property';
|
||||
}
|
||||
|
@ -128,6 +128,9 @@ class NodeDumper
|
||||
if ($flags & Class_::MODIFIER_FINAL) {
|
||||
$strs[] = 'MODIFIER_FINAL';
|
||||
}
|
||||
if ($flags & Class_::MODIFIER_READONLY) {
|
||||
$strs[] = 'MODIFIER_READONLY';
|
||||
}
|
||||
|
||||
if ($strs) {
|
||||
return implode(' | ', $strs) . ' (' . $flags . ')';
|
||||
|
@ -17,11 +17,11 @@ use PhpParser\Node\Stmt;
|
||||
*/
|
||||
class Php5 extends \PhpParser\ParserAbstract
|
||||
{
|
||||
protected $tokenToSymbolMapSize = 395;
|
||||
protected $tokenToSymbolMapSize = 396;
|
||||
protected $actionTableSize = 1093;
|
||||
protected $gotoTableSize = 643;
|
||||
|
||||
protected $invalidSymbol = 167;
|
||||
protected $invalidSymbol = 168;
|
||||
protected $errorSymbol = 1;
|
||||
protected $defaultAction = -32766;
|
||||
protected $unexpectedTokenRule = 32767;
|
||||
@ -194,38 +194,39 @@ class Php5 extends \PhpParser\ParserAbstract
|
||||
"'`'",
|
||||
"']'",
|
||||
"'\"'",
|
||||
"T_READONLY",
|
||||
"T_ENUM",
|
||||
"T_NULLSAFE_OBJECT_OPERATOR",
|
||||
"T_ATTRIBUTE"
|
||||
);
|
||||
|
||||
protected $tokenToSymbol = array(
|
||||
0, 167, 167, 167, 167, 167, 167, 167, 167, 167,
|
||||
167, 167, 167, 167, 167, 167, 167, 167, 167, 167,
|
||||
167, 167, 167, 167, 167, 167, 167, 167, 167, 167,
|
||||
167, 167, 167, 56, 163, 167, 160, 55, 167, 167,
|
||||
158, 159, 53, 50, 8, 51, 52, 54, 167, 167,
|
||||
167, 167, 167, 167, 167, 167, 167, 167, 31, 155,
|
||||
44, 16, 46, 30, 68, 167, 167, 167, 167, 167,
|
||||
167, 167, 167, 167, 167, 167, 167, 167, 167, 167,
|
||||
167, 167, 167, 167, 167, 167, 167, 167, 167, 167,
|
||||
167, 70, 167, 162, 36, 167, 161, 167, 167, 167,
|
||||
167, 167, 167, 167, 167, 167, 167, 167, 167, 167,
|
||||
167, 167, 167, 167, 167, 167, 167, 167, 167, 167,
|
||||
167, 167, 167, 156, 35, 157, 58, 167, 167, 167,
|
||||
167, 167, 167, 167, 167, 167, 167, 167, 167, 167,
|
||||
167, 167, 167, 167, 167, 167, 167, 167, 167, 167,
|
||||
167, 167, 167, 167, 167, 167, 167, 167, 167, 167,
|
||||
167, 167, 167, 167, 167, 167, 167, 167, 167, 167,
|
||||
167, 167, 167, 167, 167, 167, 167, 167, 167, 167,
|
||||
167, 167, 167, 167, 167, 167, 167, 167, 167, 167,
|
||||
167, 167, 167, 167, 167, 167, 167, 167, 167, 167,
|
||||
167, 167, 167, 167, 167, 167, 167, 167, 167, 167,
|
||||
167, 167, 167, 167, 167, 167, 167, 167, 167, 167,
|
||||
167, 167, 167, 167, 167, 167, 167, 167, 167, 167,
|
||||
167, 167, 167, 167, 167, 167, 167, 167, 167, 167,
|
||||
167, 167, 167, 167, 167, 167, 167, 167, 167, 167,
|
||||
167, 167, 167, 167, 167, 167, 1, 2, 3, 4,
|
||||
0, 168, 168, 168, 168, 168, 168, 168, 168, 168,
|
||||
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
|
||||
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
|
||||
168, 168, 168, 56, 163, 168, 160, 55, 168, 168,
|
||||
158, 159, 53, 50, 8, 51, 52, 54, 168, 168,
|
||||
168, 168, 168, 168, 168, 168, 168, 168, 31, 155,
|
||||
44, 16, 46, 30, 68, 168, 168, 168, 168, 168,
|
||||
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
|
||||
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
|
||||
168, 70, 168, 162, 36, 168, 161, 168, 168, 168,
|
||||
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
|
||||
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
|
||||
168, 168, 168, 156, 35, 157, 58, 168, 168, 168,
|
||||
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
|
||||
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
|
||||
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
|
||||
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
|
||||
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
|
||||
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
|
||||
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
|
||||
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
|
||||
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
|
||||
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
|
||||
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
|
||||
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
|
||||
168, 168, 168, 168, 168, 168, 1, 2, 3, 4,
|
||||
5, 6, 7, 9, 10, 11, 12, 13, 14, 15,
|
||||
17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
|
||||
27, 28, 29, 32, 33, 34, 37, 38, 39, 40,
|
||||
@ -235,11 +236,11 @@ class Php5 extends \PhpParser\ParserAbstract
|
||||
83, 84, 85, 86, 87, 88, 89, 90, 91, 92,
|
||||
93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
|
||||
103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
|
||||
113, 114, 115, 116, 117, 118, 119, 120, 121, 122,
|
||||
123, 124, 125, 126, 127, 128, 129, 164, 130, 131,
|
||||
132, 165, 133, 134, 135, 136, 137, 138, 139, 140,
|
||||
141, 142, 143, 144, 145, 146, 147, 148, 149, 150,
|
||||
151, 152, 153, 154, 166
|
||||
113, 114, 115, 116, 117, 118, 119, 120, 121, 164,
|
||||
122, 123, 124, 125, 126, 127, 128, 129, 165, 130,
|
||||
131, 132, 166, 133, 134, 135, 136, 137, 138, 139,
|
||||
140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
|
||||
150, 151, 152, 153, 154, 167
|
||||
);
|
||||
|
||||
protected $action = array(
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -108,40 +108,41 @@ final class Tokens
|
||||
const T_PRIVATE = 356;
|
||||
const T_PROTECTED = 357;
|
||||
const T_PUBLIC = 358;
|
||||
const T_VAR = 359;
|
||||
const T_UNSET = 360;
|
||||
const T_ISSET = 361;
|
||||
const T_EMPTY = 362;
|
||||
const T_HALT_COMPILER = 363;
|
||||
const T_CLASS = 364;
|
||||
const T_TRAIT = 365;
|
||||
const T_INTERFACE = 366;
|
||||
const T_ENUM = 367;
|
||||
const T_EXTENDS = 368;
|
||||
const T_IMPLEMENTS = 369;
|
||||
const T_OBJECT_OPERATOR = 370;
|
||||
const T_NULLSAFE_OBJECT_OPERATOR = 371;
|
||||
const T_LIST = 372;
|
||||
const T_ARRAY = 373;
|
||||
const T_CALLABLE = 374;
|
||||
const T_CLASS_C = 375;
|
||||
const T_TRAIT_C = 376;
|
||||
const T_METHOD_C = 377;
|
||||
const T_FUNC_C = 378;
|
||||
const T_LINE = 379;
|
||||
const T_FILE = 380;
|
||||
const T_START_HEREDOC = 381;
|
||||
const T_END_HEREDOC = 382;
|
||||
const T_DOLLAR_OPEN_CURLY_BRACES = 383;
|
||||
const T_CURLY_OPEN = 384;
|
||||
const T_PAAMAYIM_NEKUDOTAYIM = 385;
|
||||
const T_NAMESPACE = 386;
|
||||
const T_NS_C = 387;
|
||||
const T_DIR = 388;
|
||||
const T_NS_SEPARATOR = 389;
|
||||
const T_ELLIPSIS = 390;
|
||||
const T_NAME_FULLY_QUALIFIED = 391;
|
||||
const T_NAME_QUALIFIED = 392;
|
||||
const T_NAME_RELATIVE = 393;
|
||||
const T_ATTRIBUTE = 394;
|
||||
const T_READONLY = 359;
|
||||
const T_VAR = 360;
|
||||
const T_UNSET = 361;
|
||||
const T_ISSET = 362;
|
||||
const T_EMPTY = 363;
|
||||
const T_HALT_COMPILER = 364;
|
||||
const T_CLASS = 365;
|
||||
const T_TRAIT = 366;
|
||||
const T_INTERFACE = 367;
|
||||
const T_ENUM = 368;
|
||||
const T_EXTENDS = 369;
|
||||
const T_IMPLEMENTS = 370;
|
||||
const T_OBJECT_OPERATOR = 371;
|
||||
const T_NULLSAFE_OBJECT_OPERATOR = 372;
|
||||
const T_LIST = 373;
|
||||
const T_ARRAY = 374;
|
||||
const T_CALLABLE = 375;
|
||||
const T_CLASS_C = 376;
|
||||
const T_TRAIT_C = 377;
|
||||
const T_METHOD_C = 378;
|
||||
const T_FUNC_C = 379;
|
||||
const T_LINE = 380;
|
||||
const T_FILE = 381;
|
||||
const T_START_HEREDOC = 382;
|
||||
const T_END_HEREDOC = 383;
|
||||
const T_DOLLAR_OPEN_CURLY_BRACES = 384;
|
||||
const T_CURLY_OPEN = 385;
|
||||
const T_PAAMAYIM_NEKUDOTAYIM = 386;
|
||||
const T_NAMESPACE = 387;
|
||||
const T_NS_C = 388;
|
||||
const T_DIR = 389;
|
||||
const T_NS_SEPARATOR = 390;
|
||||
const T_ELLIPSIS = 391;
|
||||
const T_NAME_FULLY_QUALIFIED = 392;
|
||||
const T_NAME_QUALIFIED = 393;
|
||||
const T_NAME_RELATIVE = 394;
|
||||
const T_ATTRIBUTE = 395;
|
||||
}
|
||||
|
@ -977,6 +977,12 @@ abstract class ParserAbstract implements Parser
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($node->flags & Class_::MODIFIER_READONLY) {
|
||||
$this->emitError(new Error(
|
||||
sprintf('Method %s() cannot be readonly', $node->name),
|
||||
$this->getAttributesAt($modifierPos)));
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkClassConst(ClassConst $node, $modifierPos) {
|
||||
@ -990,9 +996,9 @@ abstract class ParserAbstract implements Parser
|
||||
"Cannot use 'abstract' as constant modifier",
|
||||
$this->getAttributesAt($modifierPos)));
|
||||
}
|
||||
if ($node->flags & Class_::MODIFIER_FINAL) {
|
||||
if ($node->flags & Class_::MODIFIER_READONLY) {
|
||||
$this->emitError(new Error(
|
||||
"Cannot use 'final' as constant modifier",
|
||||
"Cannot use 'readonly' as constant modifier",
|
||||
$this->getAttributesAt($modifierPos)));
|
||||
}
|
||||
}
|
||||
|
@ -1074,7 +1074,8 @@ abstract class PrettyPrinterAbstract
|
||||
. ($modifiers & Stmt\Class_::MODIFIER_PRIVATE ? 'private ' : '')
|
||||
. ($modifiers & Stmt\Class_::MODIFIER_STATIC ? 'static ' : '')
|
||||
. ($modifiers & Stmt\Class_::MODIFIER_ABSTRACT ? 'abstract ' : '')
|
||||
. ($modifiers & Stmt\Class_::MODIFIER_FINAL ? 'final ' : '');
|
||||
. ($modifiers & Stmt\Class_::MODIFIER_FINAL ? 'final ' : '')
|
||||
. ($modifiers & Stmt\Class_::MODIFIER_READONLY ? 'readonly ' : '');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -65,6 +65,21 @@ class ClassConstTest extends \PHPUnit\Framework\TestCase
|
||||
),
|
||||
$node
|
||||
);
|
||||
|
||||
$node = $this->createClassConstBuilder("TEST", 1)
|
||||
->makeFinal()
|
||||
->getNode()
|
||||
;
|
||||
|
||||
$this->assertEquals(
|
||||
new Stmt\ClassConst(
|
||||
[
|
||||
new Const_("TEST", new LNumber(1) )
|
||||
],
|
||||
Stmt\Class_::MODIFIER_FINAL
|
||||
),
|
||||
$node
|
||||
);
|
||||
}
|
||||
|
||||
public function testDocComment() {
|
||||
|
@ -66,6 +66,21 @@ class PropertyTest extends \PHPUnit\Framework\TestCase
|
||||
),
|
||||
$node
|
||||
);
|
||||
|
||||
$node = $this->createPropertyBuilder('test')
|
||||
->makeReadonly()
|
||||
->getNode()
|
||||
;
|
||||
|
||||
$this->assertEquals(
|
||||
new Stmt\Property(
|
||||
Stmt\Class_::MODIFIER_READONLY,
|
||||
[
|
||||
new Stmt\PropertyProperty('test')
|
||||
]
|
||||
),
|
||||
$node
|
||||
);
|
||||
}
|
||||
|
||||
public function testDocComment() {
|
||||
|
@ -22,6 +22,7 @@ class ClassConstTest extends \PHPUnit\Framework\TestCase
|
||||
$this->assertTrue($node->isPublic());
|
||||
$this->assertFalse($node->isProtected());
|
||||
$this->assertFalse($node->isPrivate());
|
||||
$this->assertFalse($node->isFinal());
|
||||
}
|
||||
|
||||
public function provideModifiers() {
|
||||
@ -29,6 +30,7 @@ class ClassConstTest extends \PHPUnit\Framework\TestCase
|
||||
['public'],
|
||||
['protected'],
|
||||
['private'],
|
||||
['final'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ class PropertyTest extends \PHPUnit\Framework\TestCase
|
||||
$this->assertFalse($node->isProtected());
|
||||
$this->assertFalse($node->isPrivate());
|
||||
$this->assertFalse($node->isStatic());
|
||||
$this->assertFalse($node->isReadonly());
|
||||
}
|
||||
|
||||
public function testStaticImplicitlyPublic() {
|
||||
@ -31,6 +32,12 @@ class PropertyTest extends \PHPUnit\Framework\TestCase
|
||||
$this->assertFalse($node->isProtected());
|
||||
$this->assertFalse($node->isPrivate());
|
||||
$this->assertTrue($node->isStatic());
|
||||
$this->assertFalse($node->isReadonly());
|
||||
}
|
||||
|
||||
public function testReadonly() {
|
||||
$node = new Property(Class_::MODIFIER_READONLY, []);
|
||||
$this->assertTrue($node->isReadonly());
|
||||
}
|
||||
|
||||
public function provideModifiers() {
|
||||
|
@ -78,11 +78,11 @@ array(
|
||||
-----
|
||||
<?php
|
||||
class A {
|
||||
final const X = 1;
|
||||
readonly const X = 1;
|
||||
}
|
||||
-----
|
||||
!!php7
|
||||
Cannot use 'final' as constant modifier from 3:5 to 3:9
|
||||
Cannot use 'readonly' as constant modifier from 3:5 to 3:12
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
@ -98,7 +98,7 @@ array(
|
||||
0: Stmt_ClassConst(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_FINAL (32)
|
||||
flags: MODIFIER_READONLY (64)
|
||||
consts: array(
|
||||
0: Const(
|
||||
name: Identifier(
|
||||
@ -150,4 +150,4 @@ array(
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -7,6 +7,7 @@ class Foo {
|
||||
public const B = 2;
|
||||
protected const C = 3;
|
||||
private const D = 4;
|
||||
final const E = 5;
|
||||
}
|
||||
-----
|
||||
!!php7
|
||||
@ -82,6 +83,21 @@ array(
|
||||
)
|
||||
)
|
||||
)
|
||||
4: Stmt_ClassConst(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_FINAL (32)
|
||||
consts: array(
|
||||
0: Const(
|
||||
name: Identifier(
|
||||
name: E
|
||||
)
|
||||
value: Scalar_LNumber(
|
||||
value: 5
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
@ -66,6 +66,40 @@ array(
|
||||
)
|
||||
)
|
||||
-----
|
||||
<?php class A { readonly readonly $a; }
|
||||
-----
|
||||
!!php7
|
||||
Multiple readonly modifiers are not allowed from 1:26 to 1:33
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
)
|
||||
extends: null
|
||||
implements: array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_Property(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_READONLY (64)
|
||||
type: null
|
||||
props: array(
|
||||
0: Stmt_PropertyProperty(
|
||||
name: VarLikeIdentifier(
|
||||
name: a
|
||||
)
|
||||
default: null
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
-----
|
||||
<?php class A { abstract abstract function a(); }
|
||||
-----
|
||||
Multiple abstract modifiers are not allowed from 1:26 to 1:33
|
||||
@ -221,6 +255,31 @@ array(
|
||||
)
|
||||
)
|
||||
-----
|
||||
<?php readonly class A { }
|
||||
// Type in the partial parse could conceivably be any of 0, 16 or 32
|
||||
-----
|
||||
Syntax error, unexpected T_READONLY from 1:7 to 1:14
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
)
|
||||
extends: null
|
||||
implements: array(
|
||||
)
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
1: Stmt_Nop(
|
||||
comments: array(
|
||||
0: // Type in the partial parse could conceivably be any of 0, 16 or 32
|
||||
)
|
||||
)
|
||||
)
|
||||
-----
|
||||
<?php class A { abstract $a; }
|
||||
-----
|
||||
Properties cannot be declared abstract from 1:17 to 1:24
|
||||
|
@ -6,6 +6,7 @@ class A {
|
||||
public string $a;
|
||||
protected static D $b;
|
||||
private ?float $c;
|
||||
readonly static public ?int $d;
|
||||
}
|
||||
-----
|
||||
!!php7
|
||||
@ -73,6 +74,24 @@ array(
|
||||
)
|
||||
)
|
||||
)
|
||||
3: Stmt_Property(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC | MODIFIER_STATIC | MODIFIER_READONLY (73)
|
||||
type: NullableType(
|
||||
type: Identifier(
|
||||
name: int
|
||||
)
|
||||
)
|
||||
props: array(
|
||||
0: Stmt_PropertyProperty(
|
||||
name: VarLikeIdentifier(
|
||||
name: d
|
||||
)
|
||||
default: null
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
35
test/code/parser/stmt/class/readonlyMethod.test
Normal file
35
test/code/parser/stmt/class/readonlyMethod.test
Normal file
@ -0,0 +1,35 @@
|
||||
Methods cannot be readonly
|
||||
-----
|
||||
<?php class A { readonly function foo() {} }
|
||||
-----
|
||||
!!php7
|
||||
Method foo() cannot be readonly from 1:17 to 1:24
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
)
|
||||
extends: null
|
||||
implements: array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassMethod(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_READONLY (64)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
name: foo
|
||||
)
|
||||
params: array(
|
||||
)
|
||||
returnType: null
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
@ -8,6 +8,7 @@ class A
|
||||
public string $b;
|
||||
protected static ?float $c = 5.0;
|
||||
private static ?self $d;
|
||||
public readonly int|float $e;
|
||||
}
|
||||
-----
|
||||
!!php7
|
||||
@ -17,4 +18,5 @@ class A
|
||||
public string $b;
|
||||
protected static ?float $c = 5.0;
|
||||
private static ?self $d;
|
||||
public readonly int|float $e;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user