mirror of
https://github.com/nikic/PHP-Parser.git
synced 2025-06-16 21:00:19 +02:00
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
617d0220b9 | |||
a951e9e24d | |||
b30e7e73d5 | |||
ff24d1d61a | |||
e55f8c6b30 | |||
3ee592b6aa | |||
3fe2422e34 |
16
CHANGELOG.md
16
CHANGELOG.md
@ -1,7 +1,17 @@
|
||||
Version 4.14.1-dev
|
||||
------------------
|
||||
Version 4.15.0 (2022-09-03)
|
||||
---------------------------
|
||||
|
||||
Nothing yet.
|
||||
### Added
|
||||
|
||||
* PHP 8.2: Added support for `true` type.
|
||||
* PHP 8.2: Added support for DNF types.
|
||||
|
||||
### Fixed
|
||||
|
||||
* Support `readonly` as a function name.
|
||||
* Added `__serialize` and `__unserialize` to magic method list.
|
||||
* Fixed bounds check in `Name::slice()`.
|
||||
* Fixed formatting preservation when adding attributes to a class/method/etc that previously had none.
|
||||
|
||||
Version 4.14.0 (2022-05-31)
|
||||
---------------------------
|
||||
|
@ -3,10 +3,10 @@ PHP Parser
|
||||
|
||||
[](https://coveralls.io/github/nikic/PHP-Parser?branch=master)
|
||||
|
||||
This is a PHP 5.2 to PHP 8.1 parser written in PHP. Its purpose is to simplify static code analysis and
|
||||
This is a PHP 5.2 to PHP 8.2 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.1).
|
||||
[**Documentation for version 4.x**][doc_4_x] (stable; for running on PHP >= 7.0; for parsing PHP 5.2 to PHP 8.2).
|
||||
|
||||
[Documentation for version 3.x][doc_3_x] (unsupported; for running on PHP >= 5.5; for parsing PHP 5.2 to PHP 7.2).
|
||||
|
||||
@ -222,4 +222,4 @@ Component documentation:
|
||||
* Parent and sibling references
|
||||
|
||||
[doc_3_x]: https://github.com/nikic/PHP-Parser/tree/3.x/doc
|
||||
[doc_master]: https://github.com/nikic/PHP-Parser/tree/master/doc
|
||||
[doc_4_x]: https://github.com/nikic/PHP-Parser/tree/4.x/doc
|
||||
|
@ -571,7 +571,7 @@ type_expr:
|
||||
type { $$ = $1; }
|
||||
| '?' type { $$ = Node\NullableType[$2]; }
|
||||
| union_type { $$ = Node\UnionType[$1]; }
|
||||
| intersection_type { $$ = Node\IntersectionType[$1]; }
|
||||
| intersection_type { $$ = $1; }
|
||||
;
|
||||
|
||||
type:
|
||||
@ -585,34 +585,52 @@ type_without_static:
|
||||
| T_CALLABLE { $$ = Node\Identifier['callable']; }
|
||||
;
|
||||
|
||||
union_type_element:
|
||||
type { $$ = $1; }
|
||||
| '(' intersection_type ')' { $$ = $2; }
|
||||
;
|
||||
|
||||
union_type:
|
||||
type '|' type { init($1, $3); }
|
||||
| union_type '|' type { push($1, $3); }
|
||||
union_type_element '|' union_type_element { init($1, $3); }
|
||||
| union_type '|' union_type_element { push($1, $3); }
|
||||
;
|
||||
|
||||
union_type_without_static_element:
|
||||
type_without_static { $$ = $1; }
|
||||
| '(' intersection_type_without_static ')' { $$ = $2; }
|
||||
;
|
||||
|
||||
union_type_without_static:
|
||||
type_without_static '|' type_without_static { init($1, $3); }
|
||||
| union_type_without_static '|' type_without_static { push($1, $3); }
|
||||
union_type_without_static_element '|' union_type_without_static_element { init($1, $3); }
|
||||
| union_type_without_static '|' union_type_without_static_element { push($1, $3); }
|
||||
;
|
||||
|
||||
intersection_type_list:
|
||||
type T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type { init($1, $3); }
|
||||
| intersection_type_list T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type
|
||||
{ push($1, $3); }
|
||||
;
|
||||
|
||||
intersection_type:
|
||||
type T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type { init($1, $3); }
|
||||
| intersection_type T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type
|
||||
intersection_type_list { $$ = Node\IntersectionType[$1]; }
|
||||
;
|
||||
|
||||
intersection_type_without_static_list:
|
||||
type_without_static T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type_without_static
|
||||
{ init($1, $3); }
|
||||
| intersection_type_without_static_list T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type_without_static
|
||||
{ push($1, $3); }
|
||||
;
|
||||
|
||||
intersection_type_without_static:
|
||||
type_without_static T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type_without_static
|
||||
{ init($1, $3); }
|
||||
| intersection_type_without_static T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type_without_static
|
||||
{ push($1, $3); }
|
||||
intersection_type_without_static_list { $$ = Node\IntersectionType[$1]; }
|
||||
;
|
||||
|
||||
type_expr_without_static:
|
||||
type_without_static { $$ = $1; }
|
||||
| '?' type_without_static { $$ = Node\NullableType[$2]; }
|
||||
| union_type_without_static { $$ = Node\UnionType[$1]; }
|
||||
| intersection_type_without_static { $$ = Node\IntersectionType[$1]; }
|
||||
| intersection_type_without_static { $$ = $1; }
|
||||
;
|
||||
|
||||
optional_type_without_static:
|
||||
|
@ -178,7 +178,20 @@ final class BuilderHelpers
|
||||
}
|
||||
|
||||
$builtinTypes = [
|
||||
'array', 'callable', 'string', 'int', 'float', 'bool', 'iterable', 'void', 'object', 'mixed', 'never',
|
||||
'array',
|
||||
'callable',
|
||||
'bool',
|
||||
'int',
|
||||
'float',
|
||||
'string',
|
||||
'iterable',
|
||||
'void',
|
||||
'object',
|
||||
'null',
|
||||
'false',
|
||||
'mixed',
|
||||
'never',
|
||||
'true',
|
||||
];
|
||||
|
||||
$lowerType = strtolower($type);
|
||||
|
@ -162,7 +162,7 @@ class Name extends NodeAbstract
|
||||
$realLength = $numParts - $realOffset;
|
||||
} else {
|
||||
$realLength = $length < 0 ? $length + $numParts - $realOffset : $length;
|
||||
if ($realLength < 0 || $realLength > $numParts) {
|
||||
if ($realLength < 0 || $realLength > $numParts - $realOffset) {
|
||||
throw new \OutOfBoundsException(sprintf('Length %d is out of bounds', $length));
|
||||
}
|
||||
}
|
||||
|
@ -23,21 +23,23 @@ class ClassMethod extends Node\Stmt implements FunctionLike
|
||||
public $attrGroups;
|
||||
|
||||
private static $magicNames = [
|
||||
'__construct' => true,
|
||||
'__destruct' => true,
|
||||
'__call' => true,
|
||||
'__callstatic' => true,
|
||||
'__get' => true,
|
||||
'__set' => true,
|
||||
'__isset' => true,
|
||||
'__unset' => true,
|
||||
'__sleep' => true,
|
||||
'__wakeup' => true,
|
||||
'__tostring' => true,
|
||||
'__set_state' => true,
|
||||
'__clone' => true,
|
||||
'__invoke' => true,
|
||||
'__debuginfo' => true,
|
||||
'__construct' => true,
|
||||
'__destruct' => true,
|
||||
'__call' => true,
|
||||
'__callstatic' => true,
|
||||
'__get' => true,
|
||||
'__set' => true,
|
||||
'__isset' => true,
|
||||
'__unset' => true,
|
||||
'__sleep' => true,
|
||||
'__wakeup' => true,
|
||||
'__tostring' => true,
|
||||
'__set_state' => true,
|
||||
'__clone' => true,
|
||||
'__invoke' => true,
|
||||
'__debuginfo' => true,
|
||||
'__serialize' => true,
|
||||
'__unserialize' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -10,7 +10,7 @@ class UnionType extends ComplexType
|
||||
/**
|
||||
* Constructs a union type.
|
||||
*
|
||||
* @param (Identifier|Name)[] $types Types
|
||||
* @param (Identifier|Name|IntersectionType)[] $types Types
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(array $types, array $attributes = []) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -664,6 +664,7 @@ abstract class ParserAbstract implements Parser
|
||||
'false' => true,
|
||||
'mixed' => true,
|
||||
'never' => true,
|
||||
'true' => true,
|
||||
];
|
||||
|
||||
if (!$name->isUnqualified()) {
|
||||
|
@ -46,7 +46,15 @@ class Standard extends PrettyPrinterAbstract
|
||||
}
|
||||
|
||||
protected function pUnionType(Node\UnionType $node) {
|
||||
return $this->pImplode($node->types, '|');
|
||||
$types = [];
|
||||
foreach ($node->types as $typeNode) {
|
||||
if ($typeNode instanceof Node\IntersectionType) {
|
||||
$types[] = '('. $this->p($typeNode) . ')';
|
||||
continue;
|
||||
}
|
||||
$types[] = $this->p($typeNode);
|
||||
}
|
||||
return implode('|', $types);
|
||||
}
|
||||
|
||||
protected function pIntersectionType(Node\IntersectionType $node) {
|
||||
|
@ -927,7 +927,7 @@ abstract class PrettyPrinterAbstract
|
||||
$result .= $this->p($delayedAddNode, true);
|
||||
$first = false;
|
||||
}
|
||||
$result .= $extraRight;
|
||||
$result .= $extraRight === "\n" ? $this->nl : $extraRight;
|
||||
}
|
||||
|
||||
return $result;
|
||||
@ -1454,6 +1454,16 @@ abstract class PrettyPrinterAbstract
|
||||
'Stmt_ClassMethod->params' => ['(', '', ''],
|
||||
'Stmt_Interface->extends' => [null, ' extends ', ''],
|
||||
'Stmt_Function->params' => ['(', '', ''],
|
||||
'Stmt_Interface->attrGroups' => [null, '', "\n"],
|
||||
'Stmt_Class->attrGroups' => [null, '', "\n"],
|
||||
'Stmt_ClassConst->attrGroups' => [null, '', "\n"],
|
||||
'Stmt_ClassMethod->attrGroups' => [null, '', "\n"],
|
||||
'Stmt_Function->attrGroups' => [null, '', "\n"],
|
||||
'Stmt_Property->attrGroups' => [null, '', "\n"],
|
||||
'Stmt_Trait->attrGroups' => [null, '', "\n"],
|
||||
'Expr_ArrowFunction->attrGroups' => [null, '', ' '],
|
||||
'Expr_Closure->attrGroups' => [null, '', ' '],
|
||||
'Expr_PrintableNewAnonClass->attrGroups' => [\T_NEW, ' ', ''],
|
||||
|
||||
/* These cannot be empty to start with:
|
||||
* Expr_Isset->vars
|
||||
|
@ -125,8 +125,11 @@ class BuilderHelpersTest extends \PHPUnit\Framework\TestCase
|
||||
$this->assertEquals(new Node\Identifier('iterable'), BuilderHelpers::normalizeType('iterable'));
|
||||
$this->assertEquals(new Node\Identifier('void'), BuilderHelpers::normalizeType('void'));
|
||||
$this->assertEquals(new Node\Identifier('object'), BuilderHelpers::normalizeType('object'));
|
||||
$this->assertEquals(new Node\Identifier('null'), BuilderHelpers::normalizeType('null'));
|
||||
$this->assertEquals(new Node\Identifier('false'), BuilderHelpers::normalizeType('false'));
|
||||
$this->assertEquals(new Node\Identifier('mixed'), BuilderHelpers::normalizeType('mixed'));
|
||||
$this->assertEquals(new Node\Identifier('never'), BuilderHelpers::normalizeType('never'));
|
||||
$this->assertEquals(new Node\Identifier('true'), BuilderHelpers::normalizeType('true'));
|
||||
|
||||
$intIdentifier = new Node\Identifier('int');
|
||||
$this->assertSame($intIdentifier, BuilderHelpers::normalizeType($intIdentifier));
|
||||
|
@ -73,6 +73,12 @@ class NameTest extends \PHPUnit\Framework\TestCase
|
||||
(new Name('foo\bar\baz'))->slice(0, -4);
|
||||
}
|
||||
|
||||
public function testSliceLengthTooLargeWithOffset() {
|
||||
$this->expectException(\OutOfBoundsException::class);
|
||||
$this->expectExceptionMessage('Length 3 is out of bounds');
|
||||
(new Name('foo\bar\baz'))->slice(1, 3);
|
||||
}
|
||||
|
||||
public function testConcat() {
|
||||
$this->assertEquals(new Name('foo\bar\baz'), Name::concat('foo', 'bar\baz'));
|
||||
$this->assertEquals(
|
||||
|
@ -102,7 +102,6 @@ function() {};
|
||||
fn()
|
||||
=> 42;
|
||||
-----
|
||||
// TODO: Currently we lose formatting for this case.
|
||||
$attrGroup = new Node\AttributeGroup([
|
||||
new Node\Attribute(new Node\Name('A'), []),
|
||||
]);
|
||||
@ -119,39 +118,32 @@ $stmts[6]->expr->attrGroups[] = $attrGroup;
|
||||
-----
|
||||
<?php
|
||||
#[A]
|
||||
class X
|
||||
{
|
||||
class X {
|
||||
#[A]
|
||||
public function m()
|
||||
{
|
||||
}
|
||||
public function m() {}
|
||||
|
||||
#[A]
|
||||
public $prop;
|
||||
public
|
||||
$prop;
|
||||
|
||||
#[A]
|
||||
const X = 42;
|
||||
const
|
||||
X = 42;
|
||||
}
|
||||
|
||||
#[A]
|
||||
trait X
|
||||
{
|
||||
}
|
||||
trait X {}
|
||||
|
||||
#[A]
|
||||
interface X
|
||||
{
|
||||
}
|
||||
interface X {}
|
||||
|
||||
#[A]
|
||||
function f()
|
||||
{
|
||||
}
|
||||
function f() {}
|
||||
|
||||
new #[A] class
|
||||
{
|
||||
};
|
||||
#[A] function () {
|
||||
};
|
||||
#[A] fn() => 42;
|
||||
new #[A] class {};
|
||||
#[A] function() {};
|
||||
#[A] fn()
|
||||
=> 42;
|
||||
-----
|
||||
<?php
|
||||
|
||||
@ -176,4 +168,4 @@ class X {};
|
||||
B,
|
||||
C,
|
||||
]
|
||||
class X {};
|
||||
class X {};
|
||||
|
@ -107,4 +107,4 @@ Foo
|
||||
|
||||
new class
|
||||
($a, $b)
|
||||
extends Foo {};
|
||||
extends Foo {};
|
||||
|
158
test/code/parser/stmt/function/disjointNormalFormTypes.test
Normal file
158
test/code/parser/stmt/function/disjointNormalFormTypes.test
Normal file
@ -0,0 +1,158 @@
|
||||
DNF types
|
||||
-----
|
||||
<?php
|
||||
|
||||
class Test {
|
||||
public (A&B)|(X&Y) $prop;
|
||||
}
|
||||
|
||||
function test((A&B)|(X&Y) $a): (A&B)|(X&Y) {}
|
||||
-----
|
||||
!!php7
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
name: Identifier(
|
||||
name: Test
|
||||
)
|
||||
extends: null
|
||||
implements: array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_Property(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: MODIFIER_PUBLIC (1)
|
||||
type: UnionType(
|
||||
types: array(
|
||||
0: IntersectionType(
|
||||
types: array(
|
||||
0: Name(
|
||||
parts: array(
|
||||
0: A
|
||||
)
|
||||
)
|
||||
1: Name(
|
||||
parts: array(
|
||||
0: B
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
1: IntersectionType(
|
||||
types: array(
|
||||
0: Name(
|
||||
parts: array(
|
||||
0: X
|
||||
)
|
||||
)
|
||||
1: Name(
|
||||
parts: array(
|
||||
0: Y
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
props: array(
|
||||
0: Stmt_PropertyProperty(
|
||||
name: VarLikeIdentifier(
|
||||
name: prop
|
||||
)
|
||||
default: null
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
1: Stmt_Function(
|
||||
attrGroups: array(
|
||||
)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
name: test
|
||||
)
|
||||
params: array(
|
||||
0: Param(
|
||||
attrGroups: array(
|
||||
)
|
||||
flags: 0
|
||||
type: UnionType(
|
||||
types: array(
|
||||
0: IntersectionType(
|
||||
types: array(
|
||||
0: Name(
|
||||
parts: array(
|
||||
0: A
|
||||
)
|
||||
)
|
||||
1: Name(
|
||||
parts: array(
|
||||
0: B
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
1: IntersectionType(
|
||||
types: array(
|
||||
0: Name(
|
||||
parts: array(
|
||||
0: X
|
||||
)
|
||||
)
|
||||
1: Name(
|
||||
parts: array(
|
||||
0: Y
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
byRef: false
|
||||
variadic: false
|
||||
var: Expr_Variable(
|
||||
name: a
|
||||
)
|
||||
default: null
|
||||
)
|
||||
)
|
||||
returnType: UnionType(
|
||||
types: array(
|
||||
0: IntersectionType(
|
||||
types: array(
|
||||
0: Name(
|
||||
parts: array(
|
||||
0: A
|
||||
)
|
||||
)
|
||||
1: Name(
|
||||
parts: array(
|
||||
0: B
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
1: IntersectionType(
|
||||
types: array(
|
||||
0: Name(
|
||||
parts: array(
|
||||
0: X
|
||||
)
|
||||
)
|
||||
1: Name(
|
||||
parts: array(
|
||||
0: Y
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
)
|
56
test/code/parser/stmt/function/nullFalseTrueTypes.test
Normal file
56
test/code/parser/stmt/function/nullFalseTrueTypes.test
Normal file
@ -0,0 +1,56 @@
|
||||
standalone null, false and true types
|
||||
-----
|
||||
<?php
|
||||
|
||||
function test(): null {}
|
||||
function test(): false {}
|
||||
function test(): true {}
|
||||
-----
|
||||
!!php7
|
||||
array(
|
||||
0: Stmt_Function(
|
||||
attrGroups: array(
|
||||
)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
name: test
|
||||
)
|
||||
params: array(
|
||||
)
|
||||
returnType: Identifier(
|
||||
name: null
|
||||
)
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
1: Stmt_Function(
|
||||
attrGroups: array(
|
||||
)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
name: test
|
||||
)
|
||||
params: array(
|
||||
)
|
||||
returnType: Identifier(
|
||||
name: false
|
||||
)
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
2: Stmt_Function(
|
||||
attrGroups: array(
|
||||
)
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
name: test
|
||||
)
|
||||
params: array(
|
||||
)
|
||||
returnType: Identifier(
|
||||
name: true
|
||||
)
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
)
|
18
test/code/prettyPrinter/stmt/disjointNormalFormTypes.test
Normal file
18
test/code/prettyPrinter/stmt/disjointNormalFormTypes.test
Normal file
@ -0,0 +1,18 @@
|
||||
Union types
|
||||
-----
|
||||
<?php
|
||||
|
||||
class Test {
|
||||
public (A&B)|(X&Y) $prop;
|
||||
}
|
||||
|
||||
function test((A&B)|(X&Y) $a): (A&B)|(X&Y) {}
|
||||
-----
|
||||
!!php7
|
||||
class Test
|
||||
{
|
||||
public (A&B)|(X&Y) $prop;
|
||||
}
|
||||
function test((A&B)|(X&Y) $a) : (A&B)|(X&Y)
|
||||
{
|
||||
}
|
Reference in New Issue
Block a user