mirror of
https://github.com/nikic/PHP-Parser.git
synced 2025-07-15 03:16:32 +02:00
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
34bea19b6e | |||
678ccbe072 | |||
5d83adcc0e | |||
3bf0082455 | |||
d3eb10aca1 | |||
a6e34665fd | |||
f4b835f7d8 |
11
CHANGELOG.md
11
CHANGELOG.md
@ -1,8 +1,17 @@
|
||||
Version 4.13.3-dev
|
||||
Version 4.14.1-dev
|
||||
------------------
|
||||
|
||||
Nothing yet.
|
||||
|
||||
Version 4.14.0 (2022-05-31)
|
||||
---------------------------
|
||||
|
||||
### Added
|
||||
|
||||
* Added support for readonly classes.
|
||||
* Added `rawValue` attribute to `LNumber`, `DNumber` and `String_` nodes, which stores the unparsed
|
||||
value of the literal (e.g. `"1_000"` rather than `1000`).
|
||||
|
||||
Version 4.13.2 (2021-11-30)
|
||||
---------------------------
|
||||
|
||||
|
@ -3,10 +3,10 @@ PHP Parser
|
||||
|
||||
[](https://coveralls.io/github/nikic/PHP-Parser?branch=master)
|
||||
|
||||
This is a PHP 5.2 to PHP 8.0 parser written in PHP. Its purpose is to simplify static code analysis and
|
||||
This is a PHP 5.2 to PHP 8.1 parser written in PHP. Its purpose is to simplify static code analysis and
|
||||
manipulation.
|
||||
|
||||
[**Documentation for version 4.x**][doc_master] (stable; for running on PHP >= 7.0; for parsing PHP 5.2 to PHP 8.0).
|
||||
[**Documentation for version 4.x**][doc_master] (stable; for running on PHP >= 7.0; for parsing PHP 5.2 to PHP 8.1).
|
||||
|
||||
[Documentation for version 3.x][doc_3_x] (unsupported; for running on PHP >= 5.5; for parsing PHP 5.2 to PHP 7.2).
|
||||
|
||||
|
@ -689,9 +689,7 @@ array_expr:
|
||||
|
||||
scalar_dereference:
|
||||
array_expr '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
| T_CONSTANT_ENCAPSED_STRING '[' dim_offset ']'
|
||||
{ $attrs = attributes(); $attrs['kind'] = strKind($1);
|
||||
$$ = Expr\ArrayDimFetch[new Scalar\String_(Scalar\String_::parse($1), $attrs), $3]; }
|
||||
| T_CONSTANT_ENCAPSED_STRING '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[Scalar\String_::fromString($1, attributes()), $3]; }
|
||||
| constant '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
| scalar_dereference '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
/* alternative array syntax missing intentionally */
|
||||
@ -793,10 +791,8 @@ ctor_arguments:
|
||||
|
||||
common_scalar:
|
||||
T_LNUMBER { $$ = $this->parseLNumber($1, attributes(), true); }
|
||||
| T_DNUMBER { $$ = Scalar\DNumber[Scalar\DNumber::parse($1)]; }
|
||||
| T_CONSTANT_ENCAPSED_STRING
|
||||
{ $attrs = attributes(); $attrs['kind'] = strKind($1);
|
||||
$$ = new Scalar\String_(Scalar\String_::parse($1, false), $attrs); }
|
||||
| T_DNUMBER { $$ = Scalar\DNumber::fromString($1, attributes()); }
|
||||
| T_CONSTANT_ENCAPSED_STRING { $$ = Scalar\String_::fromString($1, attributes(), false); }
|
||||
| T_LINE { $$ = Scalar\MagicConst\Line[]; }
|
||||
| T_FILE { $$ = Scalar\MagicConst\File[]; }
|
||||
| T_DIR { $$ = Scalar\MagicConst\Dir[]; }
|
||||
|
@ -382,8 +382,18 @@ enum_case_expr:
|
||||
|
||||
class_entry_type:
|
||||
T_CLASS { $$ = 0; }
|
||||
| T_ABSTRACT T_CLASS { $$ = Stmt\Class_::MODIFIER_ABSTRACT; }
|
||||
| T_FINAL T_CLASS { $$ = Stmt\Class_::MODIFIER_FINAL; }
|
||||
| class_modifiers T_CLASS { $$ = $1; }
|
||||
;
|
||||
|
||||
class_modifiers:
|
||||
class_modifier { $$ = $1; }
|
||||
| class_modifiers class_modifier { $this->checkClassModifier($1, $2, #2); $$ = $1 | $2; }
|
||||
;
|
||||
|
||||
class_modifier:
|
||||
T_ABSTRACT { $$ = Stmt\Class_::MODIFIER_ABSTRACT; }
|
||||
| T_FINAL { $$ = Stmt\Class_::MODIFIER_FINAL; }
|
||||
| T_READONLY { $$ = Stmt\Class_::MODIFIER_READONLY; }
|
||||
;
|
||||
|
||||
extends_from:
|
||||
@ -1014,9 +1024,7 @@ dereferencable_scalar:
|
||||
{ $attrs = attributes(); $attrs['kind'] = Expr\Array_::KIND_LONG;
|
||||
$$ = new Expr\Array_($3, $attrs); }
|
||||
| array_short_syntax { $$ = $1; }
|
||||
| T_CONSTANT_ENCAPSED_STRING
|
||||
{ $attrs = attributes(); $attrs['kind'] = strKind($1);
|
||||
$$ = new Scalar\String_(Scalar\String_::parse($1), $attrs); }
|
||||
| T_CONSTANT_ENCAPSED_STRING { $$ = Scalar\String_::fromString($1, attributes()); }
|
||||
| '"' encaps_list '"'
|
||||
{ $attrs = attributes(); $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED;
|
||||
parseEncapsed($2, '"', true); $$ = new Scalar\Encapsed($2, $attrs); }
|
||||
@ -1024,7 +1032,7 @@ dereferencable_scalar:
|
||||
|
||||
scalar:
|
||||
T_LNUMBER { $$ = $this->parseLNumber($1, attributes()); }
|
||||
| T_DNUMBER { $$ = Scalar\DNumber[Scalar\DNumber::parse($1)]; }
|
||||
| T_DNUMBER { $$ = Scalar\DNumber::fromString($1, attributes()); }
|
||||
| dereferencable_scalar { $$ = $1; }
|
||||
| constant { $$ = $1; }
|
||||
| class_constant { $$ = $1; }
|
||||
|
@ -128,14 +128,6 @@ function resolveMacros($code) {
|
||||
. ' else { ' . $args[0] . ' = null; }';
|
||||
}
|
||||
|
||||
if ('strKind' === $name) {
|
||||
assertArgs(1, $args, $name);
|
||||
|
||||
return '(' . $args[0] . '[0] === "\'" || (' . $args[0] . '[1] === "\'" && '
|
||||
. '(' . $args[0] . '[0] === \'b\' || ' . $args[0] . '[0] === \'B\')) '
|
||||
. '? Scalar\String_::KIND_SINGLE_QUOTED : Scalar\String_::KIND_DOUBLE_QUOTED)';
|
||||
}
|
||||
|
||||
if ('prependLeadingComments' === $name) {
|
||||
assertArgs(1, $args, $name);
|
||||
|
||||
|
@ -67,7 +67,7 @@ class Class_ extends Declaration
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeAbstract() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_ABSTRACT);
|
||||
$this->flags = BuilderHelpers::addClassModifier($this->flags, Stmt\Class_::MODIFIER_ABSTRACT);
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -78,7 +78,13 @@ class Class_ extends Declaration
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeFinal() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_FINAL);
|
||||
$this->flags = BuilderHelpers::addClassModifier($this->flags, Stmt\Class_::MODIFIER_FINAL);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function makeReadonly() {
|
||||
$this->flags = BuilderHelpers::addClassModifier($this->flags, Stmt\Class_::MODIFIER_READONLY);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@ -310,4 +310,13 @@ final class BuilderHelpers
|
||||
Stmt\Class_::verifyModifier($modifiers, $modifier);
|
||||
return $modifiers | $modifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a modifier and returns new modifier bitmask.
|
||||
* @return int New modifiers
|
||||
*/
|
||||
public static function addClassModifier(int $existingModifiers, int $modifierToSet) : int {
|
||||
Stmt\Class_::verifyClassModifier($existingModifiers, $modifierToSet);
|
||||
return $existingModifiers | $modifierToSet;
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ class Const_ extends NodeAbstract
|
||||
/** @var Expr Value */
|
||||
public $value;
|
||||
|
||||
/** @var Name Namespaced name (if using NameResolver) */
|
||||
/** @var Name|null Namespaced name (if using NameResolver) */
|
||||
public $namespacedName;
|
||||
|
||||
/**
|
||||
@ -30,7 +30,7 @@ class Const_ extends NodeAbstract
|
||||
public function getSubNodeNames() : array {
|
||||
return ['name', 'value'];
|
||||
}
|
||||
|
||||
|
||||
public function getType() : string {
|
||||
return 'Const';
|
||||
}
|
||||
|
@ -24,6 +24,17 @@ class DNumber extends Scalar
|
||||
return ['value'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $attributes
|
||||
*/
|
||||
public static function fromString(string $str, array $attributes = []): DNumber
|
||||
{
|
||||
$attributes['rawValue'] = $str;
|
||||
$float = self::parse($str);
|
||||
|
||||
return new DNumber($float, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
@ -63,7 +74,7 @@ class DNumber extends Scalar
|
||||
// dec
|
||||
return (float) $str;
|
||||
}
|
||||
|
||||
|
||||
public function getType() : string {
|
||||
return 'Scalar_DNumber';
|
||||
}
|
||||
|
@ -41,6 +41,8 @@ class LNumber extends Scalar
|
||||
* @return LNumber The constructed LNumber, including kind attribute
|
||||
*/
|
||||
public static function fromString(string $str, array $attributes = [], bool $allowInvalidOctal = false) : LNumber {
|
||||
$attributes['rawValue'] = $str;
|
||||
|
||||
$str = str_replace('_', '', $str);
|
||||
|
||||
if ('0' !== $str[0] || '0' === $str) {
|
||||
@ -71,7 +73,7 @@ class LNumber extends Scalar
|
||||
$attributes['kind'] = LNumber::KIND_OCT;
|
||||
return new LNumber(intval($str, 8), $attributes);
|
||||
}
|
||||
|
||||
|
||||
public function getType() : string {
|
||||
return 'Scalar_LNumber';
|
||||
}
|
||||
|
@ -42,6 +42,22 @@ class String_ extends Scalar
|
||||
return ['value'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes
|
||||
*/
|
||||
public static function fromString(string $str, array $attributes = [], bool $parseUnicodeEscape = true): self
|
||||
{
|
||||
$attributes['kind'] = ($str[0] === "'" || ($str[1] === "'" && ($str[0] === 'b' || $str[0] === 'B')))
|
||||
? Scalar\String_::KIND_SINGLE_QUOTED
|
||||
: Scalar\String_::KIND_DOUBLE_QUOTED;
|
||||
|
||||
$attributes['rawValue'] = $str;
|
||||
|
||||
$string = self::parse($str, $parseUnicodeEscape);
|
||||
|
||||
return new self($string, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
|
@ -13,7 +13,7 @@ abstract class ClassLike extends Node\Stmt
|
||||
/** @var Node\AttributeGroup[] PHP attribute groups */
|
||||
public $attrGroups;
|
||||
|
||||
/** @var Node\Name Namespaced name (if using NameResolver) */
|
||||
/** @var Node\Name|null Namespaced name (if using NameResolver) */
|
||||
public $namespacedName;
|
||||
|
||||
/**
|
||||
|
@ -68,6 +68,10 @@ class Class_ extends ClassLike
|
||||
return (bool) ($this->flags & self::MODIFIER_FINAL);
|
||||
}
|
||||
|
||||
public function isReadonly() : bool {
|
||||
return (bool) ($this->flags & self::MODIFIER_READONLY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the class is anonymous.
|
||||
*
|
||||
@ -77,6 +81,27 @@ class Class_ extends ClassLike
|
||||
return null === $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public static function verifyClassModifier($a, $b) {
|
||||
if ($a & self::MODIFIER_ABSTRACT && $b & self::MODIFIER_ABSTRACT) {
|
||||
throw new Error('Multiple abstract modifiers are not allowed');
|
||||
}
|
||||
|
||||
if ($a & self::MODIFIER_FINAL && $b & self::MODIFIER_FINAL) {
|
||||
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');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
@ -20,7 +20,7 @@ class Function_ extends Node\Stmt implements FunctionLike
|
||||
/** @var Node\AttributeGroup[] PHP attribute groups */
|
||||
public $attrGroups;
|
||||
|
||||
/** @var Node\Name Namespaced name (if using NameResolver) */
|
||||
/** @var Node\Name|null Namespaced name (if using NameResolver) */
|
||||
public $namespacedName;
|
||||
|
||||
/**
|
||||
|
@ -2147,8 +2147,7 @@ class Php5 extends \PhpParser\ParserAbstract
|
||||
$this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes);
|
||||
},
|
||||
392 => function ($stackPos) {
|
||||
$attrs = $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes; $attrs['kind'] = ($this->semStack[$stackPos-(4-1)][0] === "'" || ($this->semStack[$stackPos-(4-1)][1] === "'" && ($this->semStack[$stackPos-(4-1)][0] === 'b' || $this->semStack[$stackPos-(4-1)][0] === 'B')) ? Scalar\String_::KIND_SINGLE_QUOTED : Scalar\String_::KIND_DOUBLE_QUOTED);
|
||||
$this->semValue = new Expr\ArrayDimFetch(new Scalar\String_(Scalar\String_::parse($this->semStack[$stackPos-(4-1)]), $attrs), $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes);
|
||||
$this->semValue = new Expr\ArrayDimFetch(Scalar\String_::fromString($this->semStack[$stackPos-(4-1)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes), $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes);
|
||||
},
|
||||
393 => function ($stackPos) {
|
||||
$this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes);
|
||||
@ -2275,11 +2274,10 @@ class Php5 extends \PhpParser\ParserAbstract
|
||||
$this->semValue = $this->parseLNumber($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes, true);
|
||||
},
|
||||
434 => function ($stackPos) {
|
||||
$this->semValue = new Scalar\DNumber(Scalar\DNumber::parse($this->semStack[$stackPos-(1-1)]), $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes);
|
||||
$this->semValue = Scalar\DNumber::fromString($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes);
|
||||
},
|
||||
435 => function ($stackPos) {
|
||||
$attrs = $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes; $attrs['kind'] = ($this->semStack[$stackPos-(1-1)][0] === "'" || ($this->semStack[$stackPos-(1-1)][1] === "'" && ($this->semStack[$stackPos-(1-1)][0] === 'b' || $this->semStack[$stackPos-(1-1)][0] === 'B')) ? Scalar\String_::KIND_SINGLE_QUOTED : Scalar\String_::KIND_DOUBLE_QUOTED);
|
||||
$this->semValue = new Scalar\String_(Scalar\String_::parse($this->semStack[$stackPos-(1-1)], false), $attrs);
|
||||
$this->semValue = Scalar\String_::fromString($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes, false);
|
||||
},
|
||||
436 => function ($stackPos) {
|
||||
$this->semValue = new Scalar\MagicConst\Line($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -875,6 +875,15 @@ abstract class ParserAbstract implements Parser
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
protected function checkClassModifier($a, $b, $modifierPos) {
|
||||
try {
|
||||
Class_::verifyClassModifier($a, $b);
|
||||
} catch (Error $error) {
|
||||
$error->setAttributes($this->getAttributesAt($modifierPos));
|
||||
$this->emitError($error);
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkModifier($a, $b, $modifierPos) {
|
||||
// Jumping through some hoops here because verifyModifier() is also used elsewhere
|
||||
try {
|
||||
|
@ -68,6 +68,20 @@ class ClassTest extends \PHPUnit\Framework\TestCase
|
||||
);
|
||||
}
|
||||
|
||||
public function testReadonly() {
|
||||
$node = $this->createClassBuilder('Test')
|
||||
->makeReadonly()
|
||||
->getNode()
|
||||
;
|
||||
|
||||
$this->assertEquals(
|
||||
new Stmt\Class_('Test', [
|
||||
'flags' => Stmt\Class_::MODIFIER_READONLY
|
||||
]),
|
||||
$node
|
||||
);
|
||||
}
|
||||
|
||||
public function testStatementOrder() {
|
||||
$method = new Stmt\ClassMethod('testMethod');
|
||||
$property = new Stmt\Property(
|
||||
|
27
test/PhpParser/Node/Scalar/DNumberTest.php
Normal file
27
test/PhpParser/Node/Scalar/DNumberTest.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Node\Scalar;
|
||||
|
||||
use PhpParser\Node\Stmt\Echo_;
|
||||
use PhpParser\ParserFactory;
|
||||
|
||||
class DNumberTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testRawValue()
|
||||
{
|
||||
$parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7);
|
||||
$nodes = $parser->parse('<?php echo 1_234.56;');
|
||||
|
||||
$echo = $nodes[0];
|
||||
$this->assertInstanceOf(Echo_::class, $echo);
|
||||
|
||||
/** @var Echo_ $echo */
|
||||
$lLumber = $echo->exprs[0];
|
||||
$this->assertInstanceOf(DNumber::class, $lLumber);
|
||||
|
||||
/** @var DNumber $dnumber */
|
||||
$this->assertSame(1234.56, $lLumber->value);
|
||||
$this->assertSame('1_234.56', $lLumber->getAttribute('rawValue'));
|
||||
}
|
||||
}
|
26
test/PhpParser/Node/Scalar/NumberTest.php
Normal file
26
test/PhpParser/Node/Scalar/NumberTest.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Node\Scalar;
|
||||
|
||||
use PhpParser\Node\Stmt\Echo_;
|
||||
use PhpParser\ParserFactory;
|
||||
|
||||
class NumberTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testRawValue()
|
||||
{
|
||||
$parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7);
|
||||
$nodes = $parser->parse('<?php echo 1_234;');
|
||||
|
||||
$echo = $nodes[0];
|
||||
$this->assertInstanceOf(Echo_::class, $echo);
|
||||
|
||||
/** @var Echo_ $echo */
|
||||
$lLumber = $echo->exprs[0];
|
||||
$this->assertInstanceOf(LNumber::class, $lLumber);
|
||||
|
||||
/** @var LNumber $lnumber */
|
||||
$this->assertSame(1234, $lLumber->value);
|
||||
$this->assertSame('1_234', $lLumber->getAttribute('rawValue'));
|
||||
}
|
||||
}
|
@ -2,8 +2,28 @@
|
||||
|
||||
namespace PhpParser\Node\Scalar;
|
||||
|
||||
use PhpParser\Node\Stmt\Echo_;
|
||||
use PhpParser\ParserFactory;
|
||||
|
||||
class StringTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testRawValue()
|
||||
{
|
||||
$parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7);
|
||||
$nodes = $parser->parse('<?php echo "sequence \x41";');
|
||||
|
||||
$echo = $nodes[0];
|
||||
$this->assertInstanceOf(Echo_::class, $echo);
|
||||
|
||||
/** @var Echo_ $echo */
|
||||
$string = $echo->exprs[0];
|
||||
$this->assertInstanceOf(String_::class, $string);
|
||||
|
||||
/** @var String_ $string */
|
||||
$this->assertSame('sequence A', $string->value);
|
||||
$this->assertSame('"sequence \\x41"', $string->getAttribute('rawValue'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideTestParseEscapeSequences
|
||||
*/
|
||||
|
@ -245,6 +245,7 @@ PHP;
|
||||
"attributes": {
|
||||
"startLine": 4,
|
||||
"endLine": 4,
|
||||
"rawValue": "0",
|
||||
"kind": 10
|
||||
}
|
||||
},
|
||||
@ -273,7 +274,8 @@ PHP;
|
||||
"value": 1,
|
||||
"attributes": {
|
||||
"startLine": 4,
|
||||
"endLine": 4
|
||||
"endLine": 4,
|
||||
"rawValue": "1.0"
|
||||
}
|
||||
},
|
||||
"flags": 0,
|
||||
@ -295,7 +297,8 @@ PHP;
|
||||
"attributes": {
|
||||
"startLine": 5,
|
||||
"endLine": 5,
|
||||
"kind": 1
|
||||
"kind": 1,
|
||||
"rawValue": "'Foo'"
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -398,6 +401,7 @@ JSON;
|
||||
"attributes": {
|
||||
"startLine": 4,
|
||||
"endLine": 4,
|
||||
"rawValue": "0",
|
||||
"kind": 10
|
||||
},
|
||||
"value": 0
|
||||
@ -426,7 +430,8 @@ JSON;
|
||||
"nodeType": "Scalar_DNumber",
|
||||
"attributes": {
|
||||
"startLine": 4,
|
||||
"endLine": 4
|
||||
"endLine": 4,
|
||||
"rawValue": "1.0"
|
||||
},
|
||||
"value": 1
|
||||
},
|
||||
@ -448,7 +453,8 @@ JSON;
|
||||
"attributes": {
|
||||
"startLine": 5,
|
||||
"endLine": 5,
|
||||
"kind": 1
|
||||
"kind": 1,
|
||||
"rawValue": "'Foo'"
|
||||
},
|
||||
"value": "Foo"
|
||||
}
|
||||
|
@ -1521,4 +1521,4 @@ array(
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
@ -150,4 +150,4 @@ array(
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
@ -66,7 +66,7 @@ array(
|
||||
)
|
||||
)
|
||||
-----
|
||||
<?php class A { readonly readonly $a; }
|
||||
<?php class C { readonly readonly $a; }
|
||||
-----
|
||||
!!php7
|
||||
Multiple readonly modifiers are not allowed from 1:26 to 1:33
|
||||
@ -76,7 +76,7 @@ array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
name: C
|
||||
)
|
||||
extends: null
|
||||
implements: array(
|
||||
@ -231,8 +231,29 @@ array(
|
||||
)
|
||||
-----
|
||||
<?php abstract final class A { }
|
||||
-----
|
||||
!!php7
|
||||
Cannot use the final modifier on an abstract class from 1:16 to 1:20
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_ABSTRACT | MODIFIER_FINAL (48)
|
||||
name: Identifier(
|
||||
name: A
|
||||
)
|
||||
extends: null
|
||||
implements: array(
|
||||
)
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
)
|
||||
-----
|
||||
<?php abstract final class A { }
|
||||
// Type in the partial parse could conceivably be any of 0, 16 or 32
|
||||
-----
|
||||
!!php5
|
||||
Syntax error, unexpected T_FINAL, expecting T_CLASS from 1:16 to 1:20
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
@ -258,6 +279,7 @@ array(
|
||||
<?php readonly class A { }
|
||||
// Type in the partial parse could conceivably be any of 0, 16 or 32
|
||||
-----
|
||||
!!php5
|
||||
Syntax error, unexpected T_READONLY from 1:7 to 1:14
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
@ -280,7 +302,7 @@ array(
|
||||
)
|
||||
)
|
||||
-----
|
||||
<?php class A { abstract $a; }
|
||||
<?php class B { abstract $b; }
|
||||
-----
|
||||
Properties cannot be declared abstract from 1:17 to 1:24
|
||||
array(
|
||||
@ -289,7 +311,7 @@ array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
name: B
|
||||
)
|
||||
extends: null
|
||||
implements: array(
|
||||
@ -303,7 +325,7 @@ array(
|
||||
props: array(
|
||||
0: Stmt_PropertyProperty(
|
||||
name: VarLikeIdentifier(
|
||||
name: a
|
||||
name: b
|
||||
)
|
||||
default: null
|
||||
)
|
||||
|
68
test/code/parser/stmt/class/readonly.test
Normal file
68
test/code/parser/stmt/class/readonly.test
Normal file
@ -0,0 +1,68 @@
|
||||
Readonly class
|
||||
-----
|
||||
<?php
|
||||
|
||||
readonly class A {
|
||||
}
|
||||
-----
|
||||
!!php7
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_READONLY (64)
|
||||
name: Identifier(
|
||||
name: A
|
||||
)
|
||||
extends: null
|
||||
implements: array(
|
||||
)
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
)
|
||||
-----
|
||||
<?php
|
||||
|
||||
readonly class A {
|
||||
}
|
||||
-----
|
||||
!!php5
|
||||
Syntax error, unexpected T_READONLY from 3:1 to 3:8
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: A
|
||||
)
|
||||
extends: null
|
||||
implements: array(
|
||||
)
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
)
|
||||
-----
|
||||
<?php
|
||||
|
||||
final readonly class A {
|
||||
}
|
||||
-----
|
||||
!!php7
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_FINAL | MODIFIER_READONLY (96)
|
||||
name: Identifier(
|
||||
name: A
|
||||
)
|
||||
extends: null
|
||||
implements: array(
|
||||
)
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
)
|
12
test/code/prettyPrinter/stmt/readonly_class.test
Normal file
12
test/code/prettyPrinter/stmt/readonly_class.test
Normal file
@ -0,0 +1,12 @@
|
||||
Readonly class
|
||||
-----
|
||||
<?php
|
||||
|
||||
readonly class Foo
|
||||
{
|
||||
}
|
||||
-----
|
||||
!!php7
|
||||
readonly class Foo
|
||||
{
|
||||
}
|
Reference in New Issue
Block a user