mirror of
https://github.com/nikic/PHP-Parser.git
synced 2025-07-19 13:21:45 +02:00
Compare commits
32 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
6bb5176bc4 | ||
|
cad49f8ed3 | ||
|
570e980a20 | ||
|
a50b4310f7 | ||
|
8863f92b58 | ||
|
3182d12b55 | ||
|
1df465cd90 | ||
|
f59bbe44bf | ||
|
2e11deec46 | ||
|
a4fe65bf60 | ||
|
e072fd2c30 | ||
|
7027899d7f | ||
|
2f1fd784fe | ||
|
0ef6c55a3f | ||
|
8216e878be | ||
|
617d0220b9 | ||
|
a951e9e24d | ||
|
b30e7e73d5 | ||
|
ff24d1d61a | ||
|
e55f8c6b30 | ||
|
3ee592b6aa | ||
|
3fe2422e34 | ||
|
2d589921f2 | ||
|
cdb731fa8b | ||
|
e727475d08 | ||
|
34bea19b6e | ||
|
678ccbe072 | ||
|
5d83adcc0e | ||
|
3bf0082455 | ||
|
d3eb10aca1 | ||
|
a6e34665fd | ||
|
f4b835f7d8 |
8
.github/workflows/main.yml
vendored
8
.github/workflows/main.yml
vendored
@@ -40,9 +40,7 @@ jobs:
|
|||||||
- "7.4"
|
- "7.4"
|
||||||
- "8.0"
|
- "8.0"
|
||||||
- "8.1"
|
- "8.1"
|
||||||
include:
|
- "8.2"
|
||||||
- php-version: "8.1"
|
|
||||||
flags: "--ignore-platform-req=php"
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout"
|
- name: "Checkout"
|
||||||
uses: "actions/checkout@v2"
|
uses: "actions/checkout@v2"
|
||||||
@@ -74,7 +72,7 @@ jobs:
|
|||||||
run: "test_old/run-php-src.sh 7.3.21"
|
run: "test_old/run-php-src.sh 7.3.21"
|
||||||
test_old_80_70:
|
test_old_80_70:
|
||||||
runs-on: "ubuntu-latest"
|
runs-on: "ubuntu-latest"
|
||||||
name: "PHP 8.0 Code on PHP 7.0 Integration Tests"
|
name: "PHP 8.1 Code on PHP 7.0 Integration Tests"
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout"
|
- name: "Checkout"
|
||||||
uses: "actions/checkout@v2"
|
uses: "actions/checkout@v2"
|
||||||
@@ -87,4 +85,4 @@ jobs:
|
|||||||
- name: "Install PHP 8 dependencies"
|
- name: "Install PHP 8 dependencies"
|
||||||
run: "composer update --no-progress --prefer-dist"
|
run: "composer update --no-progress --prefer-dist"
|
||||||
- name: "Tests"
|
- name: "Tests"
|
||||||
run: "test_old/run-php-src.sh 8.0.0"
|
run: "test_old/run-php-src.sh 8.1.6"
|
||||||
|
58
CHANGELOG.md
58
CHANGELOG.md
@@ -1,7 +1,59 @@
|
|||||||
Version 4.13.3-dev
|
Version 4.15.4 (2023-03-05)
|
||||||
------------------
|
---------------------------
|
||||||
|
|
||||||
Nothing yet.
|
### Fixed
|
||||||
|
|
||||||
|
* Fixed formatting-preservation for alternative if syntax with trailing comments.
|
||||||
|
|
||||||
|
Version 4.15.3 (2023-01-16)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Support readonly property with PHP 8.2 DNF type.
|
||||||
|
* Fixed PHP attribute group and PHP-Parser attribute mixup in EnumCase builder.
|
||||||
|
|
||||||
|
Version 4.15.2 (2022-11-12)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fixed parsing of large hex float literals that contain an "e" character.
|
||||||
|
* Fixed tests to pass on 32-bit.
|
||||||
|
* Fixed generation of invalid code when using formatting-preserving pretty printer with code that
|
||||||
|
uses inline HTML.
|
||||||
|
|
||||||
|
Version 4.15.1 (2022-09-04)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fixed formatting preservation when adding *multiple* attributes to a class/method/etc that
|
||||||
|
previously had none. This fixes a regression in the 4.15.0 release.
|
||||||
|
|
||||||
|
Version 4.15.0 (2022-09-03)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
### 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)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
### 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)
|
Version 4.13.2 (2021-11-30)
|
||||||
---------------------------
|
---------------------------
|
||||||
|
@@ -3,10 +3,10 @@ PHP Parser
|
|||||||
|
|
||||||
[](https://coveralls.io/github/nikic/PHP-Parser?branch=master)
|
[](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.2 parser written in PHP. Its purpose is to simplify static code analysis and
|
||||||
manipulation.
|
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_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).
|
[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
|
* Parent and sibling references
|
||||||
|
|
||||||
[doc_3_x]: https://github.com/nikic/PHP-Parser/tree/3.x/doc
|
[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
|
||||||
|
@@ -264,8 +264,13 @@ optional_ellipsis:
|
|||||||
| T_ELLIPSIS { $$ = true; }
|
| T_ELLIPSIS { $$ = true; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
identifier_maybe_readonly:
|
||||||
|
identifier { $$ = $1; }
|
||||||
|
| T_READONLY { $$ = Node\Identifier[$1]; }
|
||||||
|
;
|
||||||
|
|
||||||
function_declaration_statement:
|
function_declaration_statement:
|
||||||
T_FUNCTION optional_ref identifier '(' parameter_list ')' optional_return_type '{' inner_statement_list '}'
|
T_FUNCTION optional_ref identifier_maybe_readonly '(' parameter_list ')' optional_return_type '{' inner_statement_list '}'
|
||||||
{ $$ = Stmt\Function_[$3, ['byRef' => $2, 'params' => $5, 'returnType' => $7, 'stmts' => $9]]; }
|
{ $$ = Stmt\Function_[$3, ['byRef' => $2, 'params' => $5, 'returnType' => $7, 'stmts' => $9]]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -689,9 +694,7 @@ array_expr:
|
|||||||
|
|
||||||
scalar_dereference:
|
scalar_dereference:
|
||||||
array_expr '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
array_expr '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||||
| T_CONSTANT_ENCAPSED_STRING '[' dim_offset ']'
|
| T_CONSTANT_ENCAPSED_STRING '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[Scalar\String_::fromString($1, attributes()), $3]; }
|
||||||
{ $attrs = attributes(); $attrs['kind'] = strKind($1);
|
|
||||||
$$ = Expr\ArrayDimFetch[new Scalar\String_(Scalar\String_::parse($1), $attrs), $3]; }
|
|
||||||
| constant '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
| constant '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||||
| scalar_dereference '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
| scalar_dereference '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||||
/* alternative array syntax missing intentionally */
|
/* alternative array syntax missing intentionally */
|
||||||
@@ -723,8 +726,13 @@ lexical_var:
|
|||||||
optional_ref plain_variable { $$ = Expr\ClosureUse[$2, $1]; }
|
optional_ref plain_variable { $$ = Expr\ClosureUse[$2, $1]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
name_readonly:
|
||||||
|
T_READONLY { $$ = Name[$1]; }
|
||||||
|
;
|
||||||
|
|
||||||
function_call:
|
function_call:
|
||||||
name argument_list { $$ = Expr\FuncCall[$1, $2]; }
|
name argument_list { $$ = Expr\FuncCall[$1, $2]; }
|
||||||
|
| name_readonly argument_list { $$ = Expr\FuncCall[$1, $2]; }
|
||||||
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM identifier_ex argument_list
|
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM identifier_ex argument_list
|
||||||
{ $$ = Expr\StaticCall[$1, $3, $4]; }
|
{ $$ = Expr\StaticCall[$1, $3, $4]; }
|
||||||
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM '{' expr '}' argument_list
|
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM '{' expr '}' argument_list
|
||||||
@@ -793,10 +801,8 @@ ctor_arguments:
|
|||||||
|
|
||||||
common_scalar:
|
common_scalar:
|
||||||
T_LNUMBER { $$ = $this->parseLNumber($1, attributes(), true); }
|
T_LNUMBER { $$ = $this->parseLNumber($1, attributes(), true); }
|
||||||
| T_DNUMBER { $$ = Scalar\DNumber[Scalar\DNumber::parse($1)]; }
|
| T_DNUMBER { $$ = Scalar\DNumber::fromString($1, attributes()); }
|
||||||
| T_CONSTANT_ENCAPSED_STRING
|
| T_CONSTANT_ENCAPSED_STRING { $$ = Scalar\String_::fromString($1, attributes(), false); }
|
||||||
{ $attrs = attributes(); $attrs['kind'] = strKind($1);
|
|
||||||
$$ = new Scalar\String_(Scalar\String_::parse($1, false), $attrs); }
|
|
||||||
| T_LINE { $$ = Scalar\MagicConst\Line[]; }
|
| T_LINE { $$ = Scalar\MagicConst\Line[]; }
|
||||||
| T_FILE { $$ = Scalar\MagicConst\File[]; }
|
| T_FILE { $$ = Scalar\MagicConst\File[]; }
|
||||||
| T_DIR { $$ = Scalar\MagicConst\Dir[]; }
|
| T_DIR { $$ = Scalar\MagicConst\Dir[]; }
|
||||||
|
@@ -350,15 +350,23 @@ block_or_error:
|
|||||||
| error { $$ = []; }
|
| error { $$ = []; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
identifier_maybe_readonly:
|
||||||
|
identifier_not_reserved { $$ = $1; }
|
||||||
|
| T_READONLY { $$ = Node\Identifier[$1]; }
|
||||||
|
;
|
||||||
|
|
||||||
function_declaration_statement:
|
function_declaration_statement:
|
||||||
T_FUNCTION optional_ref identifier_not_reserved '(' parameter_list ')' optional_return_type block_or_error
|
T_FUNCTION optional_ref identifier_maybe_readonly '(' parameter_list ')' optional_return_type block_or_error
|
||||||
{ $$ = Stmt\Function_[$3, ['byRef' => $2, 'params' => $5, 'returnType' => $7, 'stmts' => $8, 'attrGroups' => []]]; }
|
{ $$ = Stmt\Function_[$3, ['byRef' => $2, 'params' => $5, 'returnType' => $7, 'stmts' => $8, 'attrGroups' => []]]; }
|
||||||
| attributes T_FUNCTION optional_ref identifier_not_reserved '(' parameter_list ')' optional_return_type block_or_error
|
| attributes T_FUNCTION optional_ref identifier_maybe_readonly '(' parameter_list ')' optional_return_type block_or_error
|
||||||
{ $$ = Stmt\Function_[$4, ['byRef' => $3, 'params' => $6, 'returnType' => $8, 'stmts' => $9, 'attrGroups' => $1]]; }
|
{ $$ = Stmt\Function_[$4, ['byRef' => $3, 'params' => $6, 'returnType' => $8, 'stmts' => $9, 'attrGroups' => $1]]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
class_declaration_statement:
|
class_declaration_statement:
|
||||||
optional_attributes class_entry_type identifier_not_reserved extends_from implements_list '{' class_statement_list '}'
|
class_entry_type identifier_not_reserved extends_from implements_list '{' class_statement_list '}'
|
||||||
|
{ $$ = Stmt\Class_[$2, ['type' => $1, 'extends' => $3, 'implements' => $4, 'stmts' => $6, 'attrGroups' => []]];
|
||||||
|
$this->checkClass($$, #2); }
|
||||||
|
| attributes class_entry_type identifier_not_reserved extends_from implements_list '{' class_statement_list '}'
|
||||||
{ $$ = Stmt\Class_[$3, ['type' => $2, 'extends' => $4, 'implements' => $5, 'stmts' => $7, 'attrGroups' => $1]];
|
{ $$ = Stmt\Class_[$3, ['type' => $2, 'extends' => $4, 'implements' => $5, 'stmts' => $7, 'attrGroups' => $1]];
|
||||||
$this->checkClass($$, #3); }
|
$this->checkClass($$, #3); }
|
||||||
| optional_attributes T_INTERFACE identifier_not_reserved interface_extends_list '{' class_statement_list '}'
|
| optional_attributes T_INTERFACE identifier_not_reserved interface_extends_list '{' class_statement_list '}'
|
||||||
@@ -382,8 +390,18 @@ enum_case_expr:
|
|||||||
|
|
||||||
class_entry_type:
|
class_entry_type:
|
||||||
T_CLASS { $$ = 0; }
|
T_CLASS { $$ = 0; }
|
||||||
| T_ABSTRACT T_CLASS { $$ = Stmt\Class_::MODIFIER_ABSTRACT; }
|
| class_modifiers T_CLASS { $$ = $1; }
|
||||||
| T_FINAL T_CLASS { $$ = Stmt\Class_::MODIFIER_FINAL; }
|
;
|
||||||
|
|
||||||
|
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:
|
extends_from:
|
||||||
@@ -500,7 +518,8 @@ new_elseif_list:
|
|||||||
;
|
;
|
||||||
|
|
||||||
new_elseif:
|
new_elseif:
|
||||||
T_ELSEIF '(' expr ')' ':' inner_statement_list { $$ = Stmt\ElseIf_[$3, $6]; }
|
T_ELSEIF '(' expr ')' ':' inner_statement_list
|
||||||
|
{ $$ = Stmt\ElseIf_[$3, $6]; $this->fixupAlternativeElse($$); }
|
||||||
;
|
;
|
||||||
|
|
||||||
else_single:
|
else_single:
|
||||||
@@ -510,7 +529,8 @@ else_single:
|
|||||||
|
|
||||||
new_else_single:
|
new_else_single:
|
||||||
/* empty */ { $$ = null; }
|
/* empty */ { $$ = null; }
|
||||||
| T_ELSE ':' inner_statement_list { $$ = Stmt\Else_[$3]; }
|
| T_ELSE ':' inner_statement_list
|
||||||
|
{ $$ = Stmt\Else_[$3]; $this->fixupAlternativeElse($$); }
|
||||||
;
|
;
|
||||||
|
|
||||||
foreach_variable:
|
foreach_variable:
|
||||||
@@ -561,7 +581,7 @@ type_expr:
|
|||||||
type { $$ = $1; }
|
type { $$ = $1; }
|
||||||
| '?' type { $$ = Node\NullableType[$2]; }
|
| '?' type { $$ = Node\NullableType[$2]; }
|
||||||
| union_type { $$ = Node\UnionType[$1]; }
|
| union_type { $$ = Node\UnionType[$1]; }
|
||||||
| intersection_type { $$ = Node\IntersectionType[$1]; }
|
| intersection_type { $$ = $1; }
|
||||||
;
|
;
|
||||||
|
|
||||||
type:
|
type:
|
||||||
@@ -575,34 +595,52 @@ type_without_static:
|
|||||||
| T_CALLABLE { $$ = Node\Identifier['callable']; }
|
| T_CALLABLE { $$ = Node\Identifier['callable']; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
union_type_element:
|
||||||
|
type { $$ = $1; }
|
||||||
|
| '(' intersection_type ')' { $$ = $2; }
|
||||||
|
;
|
||||||
|
|
||||||
union_type:
|
union_type:
|
||||||
type '|' type { init($1, $3); }
|
union_type_element '|' union_type_element { init($1, $3); }
|
||||||
| union_type '|' type { push($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:
|
union_type_without_static:
|
||||||
type_without_static '|' type_without_static { init($1, $3); }
|
union_type_without_static_element '|' union_type_without_static_element { init($1, $3); }
|
||||||
| union_type_without_static '|' type_without_static { push($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:
|
intersection_type:
|
||||||
type T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type { init($1, $3); }
|
intersection_type_list { $$ = Node\IntersectionType[$1]; }
|
||||||
| intersection_type T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type
|
;
|
||||||
|
|
||||||
|
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); }
|
{ push($1, $3); }
|
||||||
;
|
;
|
||||||
|
|
||||||
intersection_type_without_static:
|
intersection_type_without_static:
|
||||||
type_without_static T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type_without_static
|
intersection_type_without_static_list { $$ = Node\IntersectionType[$1]; }
|
||||||
{ init($1, $3); }
|
|
||||||
| intersection_type_without_static T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type_without_static
|
|
||||||
{ push($1, $3); }
|
|
||||||
;
|
;
|
||||||
|
|
||||||
type_expr_without_static:
|
type_expr_without_static:
|
||||||
type_without_static { $$ = $1; }
|
type_without_static { $$ = $1; }
|
||||||
| '?' type_without_static { $$ = Node\NullableType[$2]; }
|
| '?' type_without_static { $$ = Node\NullableType[$2]; }
|
||||||
| union_type_without_static { $$ = Node\UnionType[$1]; }
|
| union_type_without_static { $$ = Node\UnionType[$1]; }
|
||||||
| intersection_type_without_static { $$ = Node\IntersectionType[$1]; }
|
| intersection_type_without_static { $$ = $1; }
|
||||||
;
|
;
|
||||||
|
|
||||||
optional_type_without_static:
|
optional_type_without_static:
|
||||||
@@ -934,8 +972,13 @@ lexical_var:
|
|||||||
optional_ref plain_variable { $$ = Expr\ClosureUse[$2, $1]; }
|
optional_ref plain_variable { $$ = Expr\ClosureUse[$2, $1]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
name_readonly:
|
||||||
|
T_READONLY { $$ = Name[$1]; }
|
||||||
|
;
|
||||||
|
|
||||||
function_call:
|
function_call:
|
||||||
name argument_list { $$ = Expr\FuncCall[$1, $2]; }
|
name argument_list { $$ = Expr\FuncCall[$1, $2]; }
|
||||||
|
| name_readonly argument_list { $$ = Expr\FuncCall[$1, $2]; }
|
||||||
| callable_expr argument_list { $$ = Expr\FuncCall[$1, $2]; }
|
| callable_expr argument_list { $$ = Expr\FuncCall[$1, $2]; }
|
||||||
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM member_name argument_list
|
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM member_name argument_list
|
||||||
{ $$ = Expr\StaticCall[$1, $3, $4]; }
|
{ $$ = Expr\StaticCall[$1, $3, $4]; }
|
||||||
@@ -1014,9 +1057,7 @@ dereferencable_scalar:
|
|||||||
{ $attrs = attributes(); $attrs['kind'] = Expr\Array_::KIND_LONG;
|
{ $attrs = attributes(); $attrs['kind'] = Expr\Array_::KIND_LONG;
|
||||||
$$ = new Expr\Array_($3, $attrs); }
|
$$ = new Expr\Array_($3, $attrs); }
|
||||||
| array_short_syntax { $$ = $1; }
|
| array_short_syntax { $$ = $1; }
|
||||||
| T_CONSTANT_ENCAPSED_STRING
|
| T_CONSTANT_ENCAPSED_STRING { $$ = Scalar\String_::fromString($1, attributes()); }
|
||||||
{ $attrs = attributes(); $attrs['kind'] = strKind($1);
|
|
||||||
$$ = new Scalar\String_(Scalar\String_::parse($1), $attrs); }
|
|
||||||
| '"' encaps_list '"'
|
| '"' encaps_list '"'
|
||||||
{ $attrs = attributes(); $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED;
|
{ $attrs = attributes(); $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED;
|
||||||
parseEncapsed($2, '"', true); $$ = new Scalar\Encapsed($2, $attrs); }
|
parseEncapsed($2, '"', true); $$ = new Scalar\Encapsed($2, $attrs); }
|
||||||
@@ -1024,7 +1065,7 @@ dereferencable_scalar:
|
|||||||
|
|
||||||
scalar:
|
scalar:
|
||||||
T_LNUMBER { $$ = $this->parseLNumber($1, attributes()); }
|
T_LNUMBER { $$ = $this->parseLNumber($1, attributes()); }
|
||||||
| T_DNUMBER { $$ = Scalar\DNumber[Scalar\DNumber::parse($1)]; }
|
| T_DNUMBER { $$ = Scalar\DNumber::fromString($1, attributes()); }
|
||||||
| dereferencable_scalar { $$ = $1; }
|
| dereferencable_scalar { $$ = $1; }
|
||||||
| constant { $$ = $1; }
|
| constant { $$ = $1; }
|
||||||
| class_constant { $$ = $1; }
|
| class_constant { $$ = $1; }
|
||||||
|
@@ -128,14 +128,6 @@ function resolveMacros($code) {
|
|||||||
. ' else { ' . $args[0] . ' = null; }';
|
. ' 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) {
|
if ('prependLeadingComments' === $name) {
|
||||||
assertArgs(1, $args, $name);
|
assertArgs(1, $args, $name);
|
||||||
|
|
||||||
|
@@ -67,7 +67,7 @@ class Class_ extends Declaration
|
|||||||
* @return $this The builder instance (for fluid interface)
|
* @return $this The builder instance (for fluid interface)
|
||||||
*/
|
*/
|
||||||
public function makeAbstract() {
|
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;
|
return $this;
|
||||||
}
|
}
|
||||||
@@ -78,7 +78,13 @@ class Class_ extends Declaration
|
|||||||
* @return $this The builder instance (for fluid interface)
|
* @return $this The builder instance (for fluid interface)
|
||||||
*/
|
*/
|
||||||
public function makeFinal() {
|
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;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@@ -78,8 +78,8 @@ class EnumCase implements PhpParser\Builder
|
|||||||
return new Stmt\EnumCase(
|
return new Stmt\EnumCase(
|
||||||
$this->name,
|
$this->name,
|
||||||
$this->value,
|
$this->value,
|
||||||
$this->attributes,
|
$this->attributeGroups,
|
||||||
$this->attributeGroups
|
$this->attributes
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -178,7 +178,20 @@ final class BuilderHelpers
|
|||||||
}
|
}
|
||||||
|
|
||||||
$builtinTypes = [
|
$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);
|
$lowerType = strtolower($type);
|
||||||
@@ -310,4 +323,13 @@ final class BuilderHelpers
|
|||||||
Stmt\Class_::verifyModifier($modifiers, $modifier);
|
Stmt\Class_::verifyModifier($modifiers, $modifier);
|
||||||
return $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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -206,6 +206,11 @@ class TokenStream
|
|||||||
|| $this->haveTokenInRange($startPos, $endPos, '}');
|
|| $this->haveTokenInRange($startPos, $endPos, '}');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function haveTagInRange(int $startPos, int $endPos): bool {
|
||||||
|
return $this->haveTokenInRange($startPos, $endPos, \T_OPEN_TAG)
|
||||||
|
|| $this->haveTokenInRange($startPos, $endPos, \T_CLOSE_TAG);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get indentation before token position.
|
* Get indentation before token position.
|
||||||
*
|
*
|
||||||
|
@@ -14,6 +14,7 @@ use PhpParser\Lexer\TokenEmulator\FnTokenEmulator;
|
|||||||
use PhpParser\Lexer\TokenEmulator\MatchTokenEmulator;
|
use PhpParser\Lexer\TokenEmulator\MatchTokenEmulator;
|
||||||
use PhpParser\Lexer\TokenEmulator\NullsafeTokenEmulator;
|
use PhpParser\Lexer\TokenEmulator\NullsafeTokenEmulator;
|
||||||
use PhpParser\Lexer\TokenEmulator\NumericLiteralSeparatorEmulator;
|
use PhpParser\Lexer\TokenEmulator\NumericLiteralSeparatorEmulator;
|
||||||
|
use PhpParser\Lexer\TokenEmulator\ReadonlyFunctionTokenEmulator;
|
||||||
use PhpParser\Lexer\TokenEmulator\ReadonlyTokenEmulator;
|
use PhpParser\Lexer\TokenEmulator\ReadonlyTokenEmulator;
|
||||||
use PhpParser\Lexer\TokenEmulator\ReverseEmulator;
|
use PhpParser\Lexer\TokenEmulator\ReverseEmulator;
|
||||||
use PhpParser\Lexer\TokenEmulator\TokenEmulator;
|
use PhpParser\Lexer\TokenEmulator\TokenEmulator;
|
||||||
@@ -24,6 +25,7 @@ class Emulative extends Lexer
|
|||||||
const PHP_7_4 = '7.4dev';
|
const PHP_7_4 = '7.4dev';
|
||||||
const PHP_8_0 = '8.0dev';
|
const PHP_8_0 = '8.0dev';
|
||||||
const PHP_8_1 = '8.1dev';
|
const PHP_8_1 = '8.1dev';
|
||||||
|
const PHP_8_2 = '8.2dev';
|
||||||
|
|
||||||
/** @var mixed[] Patches used to reverse changes introduced in the code */
|
/** @var mixed[] Patches used to reverse changes introduced in the code */
|
||||||
private $patches = [];
|
private $patches = [];
|
||||||
@@ -41,7 +43,7 @@ class Emulative extends Lexer
|
|||||||
*/
|
*/
|
||||||
public function __construct(array $options = [])
|
public function __construct(array $options = [])
|
||||||
{
|
{
|
||||||
$this->targetPhpVersion = $options['phpVersion'] ?? Emulative::PHP_8_1;
|
$this->targetPhpVersion = $options['phpVersion'] ?? Emulative::PHP_8_2;
|
||||||
unset($options['phpVersion']);
|
unset($options['phpVersion']);
|
||||||
|
|
||||||
parent::__construct($options);
|
parent::__construct($options);
|
||||||
@@ -57,6 +59,7 @@ class Emulative extends Lexer
|
|||||||
new EnumTokenEmulator(),
|
new EnumTokenEmulator(),
|
||||||
new ReadonlyTokenEmulator(),
|
new ReadonlyTokenEmulator(),
|
||||||
new ExplicitOctalEmulator(),
|
new ExplicitOctalEmulator(),
|
||||||
|
new ReadonlyFunctionTokenEmulator(),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Collect emulators that are relevant for the PHP version we're running
|
// Collect emulators that are relevant for the PHP version we're running
|
||||||
|
@@ -33,7 +33,7 @@ abstract class KeywordEmulator extends TokenEmulator
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mixed[] $tokens
|
* @param mixed[] $tokens
|
||||||
* @return mixed[]|null
|
* @return array|string|null
|
||||||
*/
|
*/
|
||||||
private function getPreviousNonSpaceToken(array $tokens, int $start)
|
private function getPreviousNonSpaceToken(array $tokens, int $start)
|
||||||
{
|
{
|
||||||
|
@@ -0,0 +1,31 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Lexer\TokenEmulator;
|
||||||
|
|
||||||
|
use PhpParser\Lexer\Emulative;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In PHP 8.1, "readonly(" was special cased in the lexer in order to support functions with
|
||||||
|
* name readonly. In PHP 8.2, this may conflict with readonly properties having a DNF type. For
|
||||||
|
* this reason, PHP 8.2 instead treats this as T_READONLY and then handles it specially in the
|
||||||
|
* parser. This emulator only exists to handle this special case, which is skipped by the
|
||||||
|
* PHP 8.1 ReadonlyTokenEmulator.
|
||||||
|
*/
|
||||||
|
class ReadonlyFunctionTokenEmulator extends KeywordEmulator {
|
||||||
|
public function getKeywordString(): string {
|
||||||
|
return 'readonly';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getKeywordToken(): int {
|
||||||
|
return \T_READONLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPhpVersion(): string {
|
||||||
|
return Emulative::PHP_8_2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reverseEmulate(string $code, array $tokens): array {
|
||||||
|
// Don't bother
|
||||||
|
return $tokens;
|
||||||
|
}
|
||||||
|
}
|
@@ -20,4 +20,17 @@ final class ReadonlyTokenEmulator extends KeywordEmulator
|
|||||||
{
|
{
|
||||||
return \T_READONLY;
|
return \T_READONLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function isKeywordContext(array $tokens, int $pos): bool
|
||||||
|
{
|
||||||
|
if (!parent::isKeywordContext($tokens, $pos)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Support "function readonly("
|
||||||
|
return !(isset($tokens[$pos + 1]) &&
|
||||||
|
($tokens[$pos + 1][0] === '(' ||
|
||||||
|
($tokens[$pos + 1][0] === \T_WHITESPACE &&
|
||||||
|
isset($tokens[$pos + 2]) &&
|
||||||
|
$tokens[$pos + 2][0] === '(')));
|
||||||
|
}
|
||||||
}
|
}
|
@@ -11,7 +11,7 @@ class Const_ extends NodeAbstract
|
|||||||
/** @var Expr Value */
|
/** @var Expr Value */
|
||||||
public $value;
|
public $value;
|
||||||
|
|
||||||
/** @var Name Namespaced name (if using NameResolver) */
|
/** @var Name|null Namespaced name (if using NameResolver) */
|
||||||
public $namespacedName;
|
public $namespacedName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -162,7 +162,7 @@ class Name extends NodeAbstract
|
|||||||
$realLength = $numParts - $realOffset;
|
$realLength = $numParts - $realOffset;
|
||||||
} else {
|
} else {
|
||||||
$realLength = $length < 0 ? $length + $numParts - $realOffset : $length;
|
$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));
|
throw new \OutOfBoundsException(sprintf('Length %d is out of bounds', $length));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,6 +24,17 @@ class DNumber extends Scalar
|
|||||||
return ['value'];
|
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
|
* @internal
|
||||||
*
|
*
|
||||||
@@ -36,13 +47,7 @@ class DNumber extends Scalar
|
|||||||
public static function parse(string $str) : float {
|
public static function parse(string $str) : float {
|
||||||
$str = str_replace('_', '', $str);
|
$str = str_replace('_', '', $str);
|
||||||
|
|
||||||
// if string contains any of .eE just cast it to float
|
// Check whether this is one of the special integer notations.
|
||||||
if (false !== strpbrk($str, '.eE')) {
|
|
||||||
return (float) $str;
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise it's an integer notation that overflowed into a float
|
|
||||||
// if it starts with 0 it's one of the special integer notations
|
|
||||||
if ('0' === $str[0]) {
|
if ('0' === $str[0]) {
|
||||||
// hex
|
// hex
|
||||||
if ('x' === $str[1] || 'X' === $str[1]) {
|
if ('x' === $str[1] || 'X' === $str[1]) {
|
||||||
@@ -54,10 +59,12 @@ class DNumber extends Scalar
|
|||||||
return bindec($str);
|
return bindec($str);
|
||||||
}
|
}
|
||||||
|
|
||||||
// oct
|
// oct, but only if the string does not contain any of '.eE'.
|
||||||
// substr($str, 0, strcspn($str, '89')) cuts the string at the first invalid digit (8 or 9)
|
if (false === strpbrk($str, '.eE')) {
|
||||||
// so that only the digits before that are used
|
// substr($str, 0, strcspn($str, '89')) cuts the string at the first invalid digit
|
||||||
return octdec(substr($str, 0, strcspn($str, '89')));
|
// (8 or 9) so that only the digits before that are used.
|
||||||
|
return octdec(substr($str, 0, strcspn($str, '89')));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// dec
|
// dec
|
||||||
|
@@ -41,6 +41,8 @@ class LNumber extends Scalar
|
|||||||
* @return LNumber The constructed LNumber, including kind attribute
|
* @return LNumber The constructed LNumber, including kind attribute
|
||||||
*/
|
*/
|
||||||
public static function fromString(string $str, array $attributes = [], bool $allowInvalidOctal = false) : LNumber {
|
public static function fromString(string $str, array $attributes = [], bool $allowInvalidOctal = false) : LNumber {
|
||||||
|
$attributes['rawValue'] = $str;
|
||||||
|
|
||||||
$str = str_replace('_', '', $str);
|
$str = str_replace('_', '', $str);
|
||||||
|
|
||||||
if ('0' !== $str[0] || '0' === $str) {
|
if ('0' !== $str[0] || '0' === $str) {
|
||||||
|
@@ -42,6 +42,22 @@ class String_ extends Scalar
|
|||||||
return ['value'];
|
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
|
* @internal
|
||||||
*
|
*
|
||||||
|
@@ -13,7 +13,7 @@ abstract class ClassLike extends Node\Stmt
|
|||||||
/** @var Node\AttributeGroup[] PHP attribute groups */
|
/** @var Node\AttributeGroup[] PHP attribute groups */
|
||||||
public $attrGroups;
|
public $attrGroups;
|
||||||
|
|
||||||
/** @var Node\Name Namespaced name (if using NameResolver) */
|
/** @var Node\Name|null Namespaced name (if using NameResolver) */
|
||||||
public $namespacedName;
|
public $namespacedName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -23,21 +23,23 @@ class ClassMethod extends Node\Stmt implements FunctionLike
|
|||||||
public $attrGroups;
|
public $attrGroups;
|
||||||
|
|
||||||
private static $magicNames = [
|
private static $magicNames = [
|
||||||
'__construct' => true,
|
'__construct' => true,
|
||||||
'__destruct' => true,
|
'__destruct' => true,
|
||||||
'__call' => true,
|
'__call' => true,
|
||||||
'__callstatic' => true,
|
'__callstatic' => true,
|
||||||
'__get' => true,
|
'__get' => true,
|
||||||
'__set' => true,
|
'__set' => true,
|
||||||
'__isset' => true,
|
'__isset' => true,
|
||||||
'__unset' => true,
|
'__unset' => true,
|
||||||
'__sleep' => true,
|
'__sleep' => true,
|
||||||
'__wakeup' => true,
|
'__wakeup' => true,
|
||||||
'__tostring' => true,
|
'__tostring' => true,
|
||||||
'__set_state' => true,
|
'__set_state' => true,
|
||||||
'__clone' => true,
|
'__clone' => true,
|
||||||
'__invoke' => true,
|
'__invoke' => true,
|
||||||
'__debuginfo' => true,
|
'__debuginfo' => true,
|
||||||
|
'__serialize' => true,
|
||||||
|
'__unserialize' => true,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -68,6 +68,10 @@ class Class_ extends ClassLike
|
|||||||
return (bool) ($this->flags & self::MODIFIER_FINAL);
|
return (bool) ($this->flags & self::MODIFIER_FINAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isReadonly() : bool {
|
||||||
|
return (bool) ($this->flags & self::MODIFIER_READONLY);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the class is anonymous.
|
* Whether the class is anonymous.
|
||||||
*
|
*
|
||||||
@@ -77,6 +81,27 @@ class Class_ extends ClassLike
|
|||||||
return null === $this->name;
|
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
|
* @internal
|
||||||
*/
|
*/
|
||||||
|
@@ -20,7 +20,7 @@ class Function_ extends Node\Stmt implements FunctionLike
|
|||||||
/** @var Node\AttributeGroup[] PHP attribute groups */
|
/** @var Node\AttributeGroup[] PHP attribute groups */
|
||||||
public $attrGroups;
|
public $attrGroups;
|
||||||
|
|
||||||
/** @var Node\Name Namespaced name (if using NameResolver) */
|
/** @var Node\Name|null Namespaced name (if using NameResolver) */
|
||||||
public $namespacedName;
|
public $namespacedName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -4,13 +4,13 @@ namespace PhpParser\Node;
|
|||||||
|
|
||||||
class UnionType extends ComplexType
|
class UnionType extends ComplexType
|
||||||
{
|
{
|
||||||
/** @var (Identifier|Name)[] Types */
|
/** @var (Identifier|Name|IntersectionType)[] Types */
|
||||||
public $types;
|
public $types;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a union type.
|
* Constructs a union type.
|
||||||
*
|
*
|
||||||
* @param (Identifier|Name)[] $types Types
|
* @param (Identifier|Name|IntersectionType)[] $types Types
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
*/
|
*/
|
||||||
public function __construct(array $types, array $attributes = []) {
|
public function __construct(array $types, array $attributes = []) {
|
||||||
|
@@ -161,7 +161,7 @@ class NameResolver extends NodeVisitorAbstract
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function addAlias(Stmt\UseUse $use, $type, Name $prefix = null) {
|
private function addAlias(Stmt\UseUse $use, int $type, Name $prefix = null) {
|
||||||
// Add prefix for group uses
|
// Add prefix for group uses
|
||||||
$name = $prefix ? Name::concat($prefix, $use->name) : $use->name;
|
$name = $prefix ? Name::concat($prefix, $use->name) : $use->name;
|
||||||
// Type is determined either by individual element or whole use declaration
|
// Type is determined either by individual element or whole use declaration
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -16,9 +16,12 @@ use PhpParser\Node\Scalar\String_;
|
|||||||
use PhpParser\Node\Stmt\Class_;
|
use PhpParser\Node\Stmt\Class_;
|
||||||
use PhpParser\Node\Stmt\ClassConst;
|
use PhpParser\Node\Stmt\ClassConst;
|
||||||
use PhpParser\Node\Stmt\ClassMethod;
|
use PhpParser\Node\Stmt\ClassMethod;
|
||||||
|
use PhpParser\Node\Stmt\Else_;
|
||||||
|
use PhpParser\Node\Stmt\ElseIf_;
|
||||||
use PhpParser\Node\Stmt\Enum_;
|
use PhpParser\Node\Stmt\Enum_;
|
||||||
use PhpParser\Node\Stmt\Interface_;
|
use PhpParser\Node\Stmt\Interface_;
|
||||||
use PhpParser\Node\Stmt\Namespace_;
|
use PhpParser\Node\Stmt\Namespace_;
|
||||||
|
use PhpParser\Node\Stmt\Nop;
|
||||||
use PhpParser\Node\Stmt\Property;
|
use PhpParser\Node\Stmt\Property;
|
||||||
use PhpParser\Node\Stmt\TryCatch;
|
use PhpParser\Node\Stmt\TryCatch;
|
||||||
use PhpParser\Node\Stmt\UseUse;
|
use PhpParser\Node\Stmt\UseUse;
|
||||||
@@ -664,6 +667,7 @@ abstract class ParserAbstract implements Parser
|
|||||||
'false' => true,
|
'false' => true,
|
||||||
'mixed' => true,
|
'mixed' => true,
|
||||||
'never' => true,
|
'never' => true,
|
||||||
|
'true' => true,
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!$name->isUnqualified()) {
|
if (!$name->isUnqualified()) {
|
||||||
@@ -875,6 +879,33 @@ abstract class ParserAbstract implements Parser
|
|||||||
return $attributes;
|
return $attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @param ElseIf_|Else_ $node */
|
||||||
|
protected function fixupAlternativeElse($node) {
|
||||||
|
// Make sure a trailing nop statement carrying comments is part of the node.
|
||||||
|
$numStmts = \count($node->stmts);
|
||||||
|
if ($numStmts !== 0 && $node->stmts[$numStmts - 1] instanceof Nop) {
|
||||||
|
$nopAttrs = $node->stmts[$numStmts - 1]->getAttributes();
|
||||||
|
if (isset($nopAttrs['endLine'])) {
|
||||||
|
$node->setAttribute('endLine', $nopAttrs['endLine']);
|
||||||
|
}
|
||||||
|
if (isset($nopAttrs['endFilePos'])) {
|
||||||
|
$node->setAttribute('endFilePos', $nopAttrs['endFilePos']);
|
||||||
|
}
|
||||||
|
if (isset($nopAttrs['endTokenPos'])) {
|
||||||
|
$node->setAttribute('endTokenPos', $nopAttrs['endTokenPos']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
protected function checkModifier($a, $b, $modifierPos) {
|
||||||
// Jumping through some hoops here because verifyModifier() is also used elsewhere
|
// Jumping through some hoops here because verifyModifier() is also used elsewhere
|
||||||
try {
|
try {
|
||||||
|
@@ -46,7 +46,15 @@ class Standard extends PrettyPrinterAbstract
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected function pUnionType(Node\UnionType $node) {
|
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) {
|
protected function pIntersectionType(Node\IntersectionType $node) {
|
||||||
|
@@ -774,7 +774,8 @@ abstract class PrettyPrinterAbstract
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($skipRemovedNode) {
|
if ($skipRemovedNode) {
|
||||||
if ($isStmtList && $this->origTokens->haveBracesInRange($pos, $itemStartPos)) {
|
if ($isStmtList && ($this->origTokens->haveBracesInRange($pos, $itemStartPos) ||
|
||||||
|
$this->origTokens->haveTagInRange($pos, $itemStartPos))) {
|
||||||
// We'd remove the brace of a code block.
|
// We'd remove the brace of a code block.
|
||||||
// TODO: Preserve formatting.
|
// TODO: Preserve formatting.
|
||||||
$this->setIndentLevel($origIndentLevel);
|
$this->setIndentLevel($origIndentLevel);
|
||||||
@@ -877,7 +878,8 @@ abstract class PrettyPrinterAbstract
|
|||||||
$pos, $itemStartPos, $indentAdjustment);
|
$pos, $itemStartPos, $indentAdjustment);
|
||||||
$skipRemovedNode = true;
|
$skipRemovedNode = true;
|
||||||
} else {
|
} else {
|
||||||
if ($isStmtList && $this->origTokens->haveBracesInRange($pos, $itemStartPos)) {
|
if ($isStmtList && ($this->origTokens->haveBracesInRange($pos, $itemStartPos) ||
|
||||||
|
$this->origTokens->haveTagInRange($pos, $itemStartPos))) {
|
||||||
// We'd remove the brace of a code block.
|
// We'd remove the brace of a code block.
|
||||||
// TODO: Preserve formatting.
|
// TODO: Preserve formatting.
|
||||||
return null;
|
return null;
|
||||||
@@ -923,11 +925,14 @@ abstract class PrettyPrinterAbstract
|
|||||||
foreach ($delayedAdd as $delayedAddNode) {
|
foreach ($delayedAdd as $delayedAddNode) {
|
||||||
if (!$first) {
|
if (!$first) {
|
||||||
$result .= $insertStr;
|
$result .= $insertStr;
|
||||||
|
if ($insertNewline) {
|
||||||
|
$result .= $this->nl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$result .= $this->p($delayedAddNode, true);
|
$result .= $this->p($delayedAddNode, true);
|
||||||
$first = false;
|
$first = false;
|
||||||
}
|
}
|
||||||
$result .= $extraRight;
|
$result .= $extraRight === "\n" ? $this->nl : $extraRight;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
@@ -1454,6 +1459,16 @@ abstract class PrettyPrinterAbstract
|
|||||||
'Stmt_ClassMethod->params' => ['(', '', ''],
|
'Stmt_ClassMethod->params' => ['(', '', ''],
|
||||||
'Stmt_Interface->extends' => [null, ' extends ', ''],
|
'Stmt_Interface->extends' => [null, ' extends ', ''],
|
||||||
'Stmt_Function->params' => ['(', '', ''],
|
'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:
|
/* These cannot be empty to start with:
|
||||||
* Expr_Isset->vars
|
* Expr_Isset->vars
|
||||||
|
@@ -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() {
|
public function testStatementOrder() {
|
||||||
$method = new Stmt\ClassMethod('testMethod');
|
$method = new Stmt\ClassMethod('testMethod');
|
||||||
$property = new Stmt\Property(
|
$property = new Stmt\Property(
|
||||||
|
@@ -27,6 +27,7 @@ class EnumCaseTest extends \PHPUnit\Framework\TestCase
|
|||||||
new Stmt\EnumCase(
|
new Stmt\EnumCase(
|
||||||
"TEST",
|
"TEST",
|
||||||
null,
|
null,
|
||||||
|
[],
|
||||||
[
|
[
|
||||||
'comments' => [new Comment\Doc('/** Test */')]
|
'comments' => [new Comment\Doc('/** Test */')]
|
||||||
]
|
]
|
||||||
@@ -50,7 +51,6 @@ class EnumCaseTest extends \PHPUnit\Framework\TestCase
|
|||||||
new Stmt\EnumCase(
|
new Stmt\EnumCase(
|
||||||
"ATTR_GROUP",
|
"ATTR_GROUP",
|
||||||
null,
|
null,
|
||||||
[],
|
|
||||||
[$attributeGroup]
|
[$attributeGroup]
|
||||||
),
|
),
|
||||||
$node
|
$node
|
||||||
|
@@ -125,8 +125,11 @@ class BuilderHelpersTest extends \PHPUnit\Framework\TestCase
|
|||||||
$this->assertEquals(new Node\Identifier('iterable'), BuilderHelpers::normalizeType('iterable'));
|
$this->assertEquals(new Node\Identifier('iterable'), BuilderHelpers::normalizeType('iterable'));
|
||||||
$this->assertEquals(new Node\Identifier('void'), BuilderHelpers::normalizeType('void'));
|
$this->assertEquals(new Node\Identifier('void'), BuilderHelpers::normalizeType('void'));
|
||||||
$this->assertEquals(new Node\Identifier('object'), BuilderHelpers::normalizeType('object'));
|
$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('mixed'), BuilderHelpers::normalizeType('mixed'));
|
||||||
$this->assertEquals(new Node\Identifier('never'), BuilderHelpers::normalizeType('never'));
|
$this->assertEquals(new Node\Identifier('never'), BuilderHelpers::normalizeType('never'));
|
||||||
|
$this->assertEquals(new Node\Identifier('true'), BuilderHelpers::normalizeType('true'));
|
||||||
|
|
||||||
$intIdentifier = new Node\Identifier('int');
|
$intIdentifier = new Node\Identifier('int');
|
||||||
$this->assertSame($intIdentifier, BuilderHelpers::normalizeType($intIdentifier));
|
$this->assertSame($intIdentifier, BuilderHelpers::normalizeType($intIdentifier));
|
||||||
|
@@ -241,8 +241,8 @@ class EmulativeTest extends LexerTest
|
|||||||
['1_000', [
|
['1_000', [
|
||||||
[Tokens::T_LNUMBER, '1_000'],
|
[Tokens::T_LNUMBER, '1_000'],
|
||||||
]],
|
]],
|
||||||
['0xCAFE_F00D', [
|
['0x7AFE_F00D', [
|
||||||
[Tokens::T_LNUMBER, '0xCAFE_F00D'],
|
[Tokens::T_LNUMBER, '0x7AFE_F00D'],
|
||||||
]],
|
]],
|
||||||
['0b0101_1111', [
|
['0b0101_1111', [
|
||||||
[Tokens::T_LNUMBER, '0b0101_1111'],
|
[Tokens::T_LNUMBER, '0b0101_1111'],
|
||||||
@@ -354,6 +354,20 @@ class EmulativeTest extends LexerTest
|
|||||||
['0o1000000000000000000000', [
|
['0o1000000000000000000000', [
|
||||||
[Tokens::T_DNUMBER, '0o1000000000000000000000'],
|
[Tokens::T_DNUMBER, '0o1000000000000000000000'],
|
||||||
]],
|
]],
|
||||||
|
['readonly class', [
|
||||||
|
[Tokens::T_READONLY, 'readonly'],
|
||||||
|
[Tokens::T_CLASS, 'class'],
|
||||||
|
]],
|
||||||
|
['function readonly(', [
|
||||||
|
[Tokens::T_FUNCTION, 'function'],
|
||||||
|
[Tokens::T_READONLY, 'readonly'],
|
||||||
|
[ord('('), '('],
|
||||||
|
]],
|
||||||
|
['function readonly (', [
|
||||||
|
[Tokens::T_FUNCTION, 'function'],
|
||||||
|
[Tokens::T_READONLY, 'readonly'],
|
||||||
|
[ord('('), '('],
|
||||||
|
]],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -73,6 +73,12 @@ class NameTest extends \PHPUnit\Framework\TestCase
|
|||||||
(new Name('foo\bar\baz'))->slice(0, -4);
|
(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() {
|
public function testConcat() {
|
||||||
$this->assertEquals(new Name('foo\bar\baz'), Name::concat('foo', 'bar\baz'));
|
$this->assertEquals(new Name('foo\bar\baz'), Name::concat('foo', 'bar\baz'));
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
|
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;
|
namespace PhpParser\Node\Scalar;
|
||||||
|
|
||||||
|
use PhpParser\Node\Stmt\Echo_;
|
||||||
|
use PhpParser\ParserFactory;
|
||||||
|
|
||||||
class StringTest extends \PHPUnit\Framework\TestCase
|
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
|
* @dataProvider provideTestParseEscapeSequences
|
||||||
*/
|
*/
|
||||||
|
@@ -245,6 +245,7 @@ PHP;
|
|||||||
"attributes": {
|
"attributes": {
|
||||||
"startLine": 4,
|
"startLine": 4,
|
||||||
"endLine": 4,
|
"endLine": 4,
|
||||||
|
"rawValue": "0",
|
||||||
"kind": 10
|
"kind": 10
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -273,7 +274,8 @@ PHP;
|
|||||||
"value": 1,
|
"value": 1,
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"startLine": 4,
|
"startLine": 4,
|
||||||
"endLine": 4
|
"endLine": 4,
|
||||||
|
"rawValue": "1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags": 0,
|
"flags": 0,
|
||||||
@@ -295,7 +297,8 @@ PHP;
|
|||||||
"attributes": {
|
"attributes": {
|
||||||
"startLine": 5,
|
"startLine": 5,
|
||||||
"endLine": 5,
|
"endLine": 5,
|
||||||
"kind": 1
|
"kind": 1,
|
||||||
|
"rawValue": "'Foo'"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -398,6 +401,7 @@ JSON;
|
|||||||
"attributes": {
|
"attributes": {
|
||||||
"startLine": 4,
|
"startLine": 4,
|
||||||
"endLine": 4,
|
"endLine": 4,
|
||||||
|
"rawValue": "0",
|
||||||
"kind": 10
|
"kind": 10
|
||||||
},
|
},
|
||||||
"value": 0
|
"value": 0
|
||||||
@@ -426,7 +430,8 @@ JSON;
|
|||||||
"nodeType": "Scalar_DNumber",
|
"nodeType": "Scalar_DNumber",
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"startLine": 4,
|
"startLine": 4,
|
||||||
"endLine": 4
|
"endLine": 4,
|
||||||
|
"rawValue": "1.0"
|
||||||
},
|
},
|
||||||
"value": 1
|
"value": 1
|
||||||
},
|
},
|
||||||
@@ -448,7 +453,8 @@ JSON;
|
|||||||
"attributes": {
|
"attributes": {
|
||||||
"startLine": 5,
|
"startLine": 5,
|
||||||
"endLine": 5,
|
"endLine": 5,
|
||||||
"kind": 1
|
"kind": 1,
|
||||||
|
"rawValue": "'Foo'"
|
||||||
},
|
},
|
||||||
"value": "Foo"
|
"value": "Foo"
|
||||||
}
|
}
|
||||||
|
@@ -102,12 +102,16 @@ function() {};
|
|||||||
fn()
|
fn()
|
||||||
=> 42;
|
=> 42;
|
||||||
-----
|
-----
|
||||||
// TODO: Currently we lose formatting for this case.
|
|
||||||
$attrGroup = new Node\AttributeGroup([
|
$attrGroup = new Node\AttributeGroup([
|
||||||
new Node\Attribute(new Node\Name('A'), []),
|
new Node\Attribute(new Node\Name('A'), []),
|
||||||
]);
|
]);
|
||||||
|
$attrGroup2 = new Node\AttributeGroup([
|
||||||
|
new Node\Attribute(new Node\Name('B'), []),
|
||||||
|
]);
|
||||||
$stmts[0]->attrGroups[] = $attrGroup;
|
$stmts[0]->attrGroups[] = $attrGroup;
|
||||||
|
$stmts[0]->attrGroups[] = $attrGroup2;
|
||||||
$stmts[0]->stmts[0]->attrGroups[] = $attrGroup;
|
$stmts[0]->stmts[0]->attrGroups[] = $attrGroup;
|
||||||
|
$stmts[0]->stmts[0]->attrGroups[] = $attrGroup2;
|
||||||
$stmts[0]->stmts[1]->attrGroups[] = $attrGroup;
|
$stmts[0]->stmts[1]->attrGroups[] = $attrGroup;
|
||||||
$stmts[0]->stmts[2]->attrGroups[] = $attrGroup;
|
$stmts[0]->stmts[2]->attrGroups[] = $attrGroup;
|
||||||
$stmts[1]->attrGroups[] = $attrGroup;
|
$stmts[1]->attrGroups[] = $attrGroup;
|
||||||
@@ -119,39 +123,34 @@ $stmts[6]->expr->attrGroups[] = $attrGroup;
|
|||||||
-----
|
-----
|
||||||
<?php
|
<?php
|
||||||
#[A]
|
#[A]
|
||||||
class X
|
#[B]
|
||||||
{
|
class X {
|
||||||
#[A]
|
#[A]
|
||||||
public function m()
|
#[B]
|
||||||
{
|
public function m() {}
|
||||||
}
|
|
||||||
#[A]
|
#[A]
|
||||||
public $prop;
|
public
|
||||||
|
$prop;
|
||||||
|
|
||||||
#[A]
|
#[A]
|
||||||
const X = 42;
|
const
|
||||||
|
X = 42;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[A]
|
#[A]
|
||||||
trait X
|
trait X {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#[A]
|
#[A]
|
||||||
interface X
|
interface X {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#[A]
|
#[A]
|
||||||
function f()
|
function f() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
new #[A] class
|
new #[A] class {};
|
||||||
{
|
#[A] function() {};
|
||||||
};
|
#[A] fn()
|
||||||
#[A] function () {
|
=> 42;
|
||||||
};
|
|
||||||
#[A] fn() => 42;
|
|
||||||
-----
|
-----
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
@@ -42,13 +42,71 @@ function test() {
|
|||||||
baz();
|
baz();
|
||||||
}
|
}
|
||||||
-----
|
-----
|
||||||
// TODO Fix broken result
|
// TODO Preserve formatting
|
||||||
$stmts[0]->stmts[1] = $stmts[0]->stmts[2];
|
$stmts[0]->stmts[1] = $stmts[0]->stmts[2];
|
||||||
-----
|
-----
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
function test() {
|
function test()
|
||||||
foo();<?php
|
{
|
||||||
|
foo();
|
||||||
baz();
|
baz();
|
||||||
baz();
|
baz();
|
||||||
}
|
}
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
foo();
|
||||||
|
?>Bar<?php
|
||||||
|
baz();
|
||||||
|
}
|
||||||
|
-----
|
||||||
|
// TODO Preserve formatting
|
||||||
|
unset($stmts[0]->stmts[2]);
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
|
||||||
|
function test()
|
||||||
|
{
|
||||||
|
foo();
|
||||||
|
?>Bar<?php
|
||||||
|
}
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
foo();
|
||||||
|
?>Bar<?php
|
||||||
|
baz();
|
||||||
|
}
|
||||||
|
-----
|
||||||
|
// TODO Preserve formatting
|
||||||
|
array_splice($stmts[0]->stmts, 0, 1, []);
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
|
||||||
|
function test()
|
||||||
|
{
|
||||||
|
?>Bar<?php
|
||||||
|
baz();
|
||||||
|
}
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
foo();
|
||||||
|
?>Bar<?php
|
||||||
|
baz();
|
||||||
|
}
|
||||||
|
-----
|
||||||
|
// TODO Preserve formatting
|
||||||
|
array_splice($stmts[0]->stmts, 1, 1, []);
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
|
||||||
|
function test()
|
||||||
|
{
|
||||||
|
foo();
|
||||||
|
baz();
|
||||||
|
}
|
||||||
|
@@ -17,6 +17,7 @@ Different float syntaxes
|
|||||||
// (all are actually the same number, just in different representations)
|
// (all are actually the same number, just in different representations)
|
||||||
18446744073709551615;
|
18446744073709551615;
|
||||||
0xFFFFFFFFFFFFFFFF;
|
0xFFFFFFFFFFFFFFFF;
|
||||||
|
0xEEEEEEEEEEEEEEEE;
|
||||||
01777777777777777777777;
|
01777777777777777777777;
|
||||||
0177777777777777777777787;
|
0177777777777777777777787;
|
||||||
0b1111111111111111111111111111111111111111111111111111111111111111;
|
0b1111111111111111111111111111111111111111111111111111111111111111;
|
||||||
@@ -92,7 +93,7 @@ array(
|
|||||||
)
|
)
|
||||||
12: Stmt_Expression(
|
12: Stmt_Expression(
|
||||||
expr: Scalar_DNumber(
|
expr: Scalar_DNumber(
|
||||||
value: 1.844674407371E+19
|
value: 1.7216961135462E+19
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
13: Stmt_Expression(
|
13: Stmt_Expression(
|
||||||
@@ -105,4 +106,9 @@ array(
|
|||||||
value: 1.844674407371E+19
|
value: 1.844674407371E+19
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
15: Stmt_Expression(
|
||||||
|
expr: Scalar_DNumber(
|
||||||
|
value: 1.844674407371E+19
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
@@ -4,7 +4,7 @@ Different integer syntaxes
|
|||||||
|
|
||||||
6.674_083e-11;
|
6.674_083e-11;
|
||||||
299_792_458;
|
299_792_458;
|
||||||
0xCAFE_F00D;
|
0x7AFE_F00D;
|
||||||
0b0101_1111;
|
0b0101_1111;
|
||||||
0137_041;
|
0137_041;
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ array(
|
|||||||
)
|
)
|
||||||
2: Stmt_Expression(
|
2: Stmt_Expression(
|
||||||
expr: Scalar_LNumber(
|
expr: Scalar_LNumber(
|
||||||
value: 3405705229
|
value: 2063527949
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
3: Stmt_Expression(
|
3: Stmt_Expression(
|
||||||
|
@@ -66,7 +66,7 @@ array(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
-----
|
-----
|
||||||
<?php class A { readonly readonly $a; }
|
<?php class C { readonly readonly $a; }
|
||||||
-----
|
-----
|
||||||
!!php7
|
!!php7
|
||||||
Multiple readonly modifiers are not allowed from 1:26 to 1:33
|
Multiple readonly modifiers are not allowed from 1:26 to 1:33
|
||||||
@@ -76,7 +76,7 @@ array(
|
|||||||
)
|
)
|
||||||
flags: 0
|
flags: 0
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
name: A
|
name: C
|
||||||
)
|
)
|
||||||
extends: null
|
extends: null
|
||||||
implements: array(
|
implements: array(
|
||||||
@@ -231,8 +231,29 @@ array(
|
|||||||
)
|
)
|
||||||
-----
|
-----
|
||||||
<?php abstract final class A { }
|
<?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
|
// 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
|
Syntax error, unexpected T_FINAL, expecting T_CLASS from 1:16 to 1:20
|
||||||
array(
|
array(
|
||||||
0: Stmt_Class(
|
0: Stmt_Class(
|
||||||
@@ -258,7 +279,8 @@ array(
|
|||||||
<?php readonly class A { }
|
<?php readonly class A { }
|
||||||
// Type in the partial parse could conceivably be any of 0, 16 or 32
|
// 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
|
!!php5
|
||||||
|
Syntax error, unexpected T_CLASS, expecting '(' from 1:16 to 1:20
|
||||||
array(
|
array(
|
||||||
0: Stmt_Class(
|
0: Stmt_Class(
|
||||||
attrGroups: array(
|
attrGroups: array(
|
||||||
@@ -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
|
Properties cannot be declared abstract from 1:17 to 1:24
|
||||||
array(
|
array(
|
||||||
@@ -289,7 +311,7 @@ array(
|
|||||||
)
|
)
|
||||||
flags: 0
|
flags: 0
|
||||||
name: Identifier(
|
name: Identifier(
|
||||||
name: A
|
name: B
|
||||||
)
|
)
|
||||||
extends: null
|
extends: null
|
||||||
implements: array(
|
implements: array(
|
||||||
@@ -303,7 +325,7 @@ array(
|
|||||||
props: array(
|
props: array(
|
||||||
0: Stmt_PropertyProperty(
|
0: Stmt_PropertyProperty(
|
||||||
name: VarLikeIdentifier(
|
name: VarLikeIdentifier(
|
||||||
name: a
|
name: b
|
||||||
)
|
)
|
||||||
default: null
|
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_CLASS, expecting '(' from 3:10 to 3:14
|
||||||
|
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(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
195
test/code/parser/stmt/function/disjointNormalFormTypes.test
Normal file
195
test/code/parser/stmt/function/disjointNormalFormTypes.test
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
DNF types
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Test {
|
||||||
|
public (A&B)|(X&Y) $prop;
|
||||||
|
public readonly (A&B)|C $prop2;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_Property(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
|
flags: MODIFIER_PUBLIC | MODIFIER_READONLY (65)
|
||||||
|
type: UnionType(
|
||||||
|
types: array(
|
||||||
|
0: IntersectionType(
|
||||||
|
types: array(
|
||||||
|
0: Name(
|
||||||
|
parts: array(
|
||||||
|
0: A
|
||||||
|
)
|
||||||
|
)
|
||||||
|
1: Name(
|
||||||
|
parts: array(
|
||||||
|
0: B
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
1: Name(
|
||||||
|
parts: array(
|
||||||
|
0: C
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
props: array(
|
||||||
|
0: Stmt_PropertyProperty(
|
||||||
|
name: VarLikeIdentifier(
|
||||||
|
name: prop2
|
||||||
|
)
|
||||||
|
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(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
32
test/code/parser/stmt/function/readonlyFunction.test
Normal file
32
test/code/parser/stmt/function/readonlyFunction.test
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
readonly function
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
function readonly() {}
|
||||||
|
readonly();
|
||||||
|
-----
|
||||||
|
array(
|
||||||
|
0: Stmt_Function(
|
||||||
|
attrGroups: array(
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
name: Identifier(
|
||||||
|
name: readonly
|
||||||
|
)
|
||||||
|
params: array(
|
||||||
|
)
|
||||||
|
returnType: null
|
||||||
|
stmts: array(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
1: Stmt_Expression(
|
||||||
|
expr: Expr_FuncCall(
|
||||||
|
name: Name(
|
||||||
|
parts: array(
|
||||||
|
0: readonly
|
||||||
|
)
|
||||||
|
)
|
||||||
|
args: array(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
@@ -65,3 +65,27 @@ function test()
|
|||||||
{
|
{
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
|
||||||
|
function noDuplicateComment()
|
||||||
|
{
|
||||||
|
if (true):
|
||||||
|
// TEST 1
|
||||||
|
elseif (true):
|
||||||
|
// TEST 2
|
||||||
|
else:
|
||||||
|
// TEST 3
|
||||||
|
endif;
|
||||||
|
}
|
||||||
|
-----
|
||||||
|
function noDuplicateComment()
|
||||||
|
{
|
||||||
|
if (true) {
|
||||||
|
// TEST 1
|
||||||
|
} elseif (true) {
|
||||||
|
// TEST 2
|
||||||
|
} else {
|
||||||
|
// TEST 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
}
|
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
|
||||||
|
{
|
||||||
|
}
|
@@ -98,7 +98,9 @@ switch ($testType) {
|
|||||||
# pretty print difference due to INF vs 1e1000
|
# pretty print difference due to INF vs 1e1000
|
||||||
| ext.standard.tests.general_functions.bug27678
|
| ext.standard.tests.general_functions.bug27678
|
||||||
| tests.lang.bug24640
|
| tests.lang.bug24640
|
||||||
|
| tests.lang.integer_literals.(binary|octal|hexadecimal)_(32|64)bit
|
||||||
| Zend.tests.bug74947
|
| Zend.tests.bug74947
|
||||||
|
| Zend.tests.float_to_int.union_int_string_type_arg
|
||||||
# pretty print differences due to negative LNumbers
|
# pretty print differences due to negative LNumbers
|
||||||
| Zend.tests.neg_num_string
|
| Zend.tests.neg_num_string
|
||||||
| Zend.tests.numeric_strings.neg_num_string
|
| Zend.tests.numeric_strings.neg_num_string
|
||||||
|
Reference in New Issue
Block a user