mirror of
https://github.com/nikic/PHP-Parser.git
synced 2025-06-21 07:21:24 +02:00
Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
bd43ec7152 | |||
d86ca0f745 | |||
c8d1216531 | |||
8999a13cb2 | |||
c1eaa0d5cd | |||
ca5f7c9761 | |||
ad696ee75d | |||
6770deda3d | |||
568236a305 | |||
9dda080a9d | |||
3ec87ef757 | |||
a2443aaefa | |||
64f4d5b619 | |||
f56d90d4f8 | |||
40aa2282df | |||
46cbd9393a | |||
602af9060d | |||
bf086d9833 | |||
521addec91 | |||
88f3a669c1 | |||
ba9cf39999 | |||
f862853987 |
7
.gitattributes
vendored
7
.gitattributes
vendored
@ -1,8 +1,9 @@
|
||||
/doc export-ignore
|
||||
/test export-ignore
|
||||
/test_old export-ignore
|
||||
/doc export-ignore
|
||||
CHANGELOG.md export-ignore
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
.travis.yml export-ignore
|
||||
CHANGELOG.md export-ignore
|
||||
phpunit.xml.dist export-ignore
|
||||
UPGRADE-*.md export-ignore
|
||||
.gitignore export-ignore
|
||||
|
@ -1,6 +1,5 @@
|
||||
language: php
|
||||
dist: xenial
|
||||
sudo: false
|
||||
|
||||
cache:
|
||||
directories:
|
||||
@ -11,7 +10,7 @@ php:
|
||||
- 7.1
|
||||
- 7.2
|
||||
- 7.3
|
||||
- 7.4snapshot
|
||||
- 7.4
|
||||
- nightly
|
||||
|
||||
install:
|
||||
|
21
CHANGELOG.md
21
CHANGELOG.md
@ -1,8 +1,27 @@
|
||||
Version 4.3.1-dev
|
||||
Version 4.4.1-dev
|
||||
-----------------
|
||||
|
||||
Nothing yet.
|
||||
|
||||
Version 4.4.0 (2020-04-10)
|
||||
--------------------------
|
||||
|
||||
### Added
|
||||
|
||||
* Added support for passing union types in builders.
|
||||
* Added end line, token position and file position information for comments.
|
||||
* Added `getProperty()` method to `ClassLike` nodes.
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed generation of invalid code when using the formatting preserving pretty printer, and
|
||||
inserting code next to certain nop statements. The formatting is still ugly though.
|
||||
* `getDocComment()` no longer requires that the very last comment before a node be a doc comment.
|
||||
There may not be non-doc comments between the doc comment and the declaration.
|
||||
* Allowed arbitrary expressions in `isset()` and `list()`, rather than just variables.
|
||||
In particular, this allows `isset(($x))`, which is legal PHP code.
|
||||
* [PHP 8.0] Add support for [variable syntax tweaks RFC](https://wiki.php.net/rfc/variable_syntax_tweaks).
|
||||
|
||||
Version 4.3.0 (2019-11-08)
|
||||
--------------------------
|
||||
|
||||
|
@ -232,7 +232,7 @@ non_empty_statement:
|
||||
| T_RETURN optional_expr semi { $$ = Stmt\Return_[$2]; }
|
||||
| T_GLOBAL global_var_list semi { $$ = Stmt\Global_[$2]; }
|
||||
| T_STATIC static_var_list semi { $$ = Stmt\Static_[$2]; }
|
||||
| T_ECHO expr_list semi { $$ = Stmt\Echo_[$2]; }
|
||||
| T_ECHO expr_list_forbid_comma semi { $$ = Stmt\Echo_[$2]; }
|
||||
| T_INLINE_HTML { $$ = Stmt\InlineHTML[$1]; }
|
||||
| expr semi { $$ = Stmt\Expression[$1]; }
|
||||
| T_UNSET '(' variables_list ')' semi { $$ = Stmt\Unset_[$3]; }
|
||||
@ -632,10 +632,14 @@ property_declaration:
|
||||
| property_decl_name '=' expr { $$ = Stmt\PropertyProperty[$1, $3]; }
|
||||
;
|
||||
|
||||
expr_list:
|
||||
expr_list_forbid_comma:
|
||||
non_empty_expr_list no_comma { $$ = $1; }
|
||||
;
|
||||
|
||||
expr_list_allow_comma:
|
||||
non_empty_expr_list optional_comma { $$ = $1; }
|
||||
;
|
||||
|
||||
non_empty_expr_list:
|
||||
non_empty_expr_list ',' expr { push($1, $3); }
|
||||
| expr { init($1); }
|
||||
@ -643,7 +647,7 @@ non_empty_expr_list:
|
||||
|
||||
for_expr:
|
||||
/* empty */ { $$ = array(); }
|
||||
| expr_list { $$ = $1; }
|
||||
| expr_list_forbid_comma { $$ = $1; }
|
||||
;
|
||||
|
||||
expr:
|
||||
@ -706,7 +710,7 @@ expr:
|
||||
| expr '?' expr ':' expr { $$ = Expr\Ternary[$1, $3, $5]; }
|
||||
| expr '?' ':' expr { $$ = Expr\Ternary[$1, null, $4]; }
|
||||
| expr T_COALESCE expr { $$ = Expr\BinaryOp\Coalesce[$1, $3]; }
|
||||
| T_ISSET '(' variables_list ')' { $$ = Expr\Isset_[$3]; }
|
||||
| T_ISSET '(' expr_list_allow_comma ')' { $$ = Expr\Isset_[$3]; }
|
||||
| T_EMPTY '(' expr ')' { $$ = Expr\Empty_[$3]; }
|
||||
| T_INCLUDE expr { $$ = Expr\Include_[$2, Expr\Include_::TYPE_INCLUDE]; }
|
||||
| T_INCLUDE_ONCE expr { $$ = Expr\Include_[$2, Expr\Include_::TYPE_INCLUDE_ONCE]; }
|
||||
@ -800,12 +804,13 @@ name:
|
||||
class_name_reference:
|
||||
class_name { $$ = $1; }
|
||||
| new_variable { $$ = $1; }
|
||||
| '(' expr ')' { $$ = $2; }
|
||||
| error { $$ = Expr\Error[]; $this->errorState = 2; }
|
||||
;
|
||||
|
||||
class_name_or_var:
|
||||
class_name { $$ = $1; }
|
||||
| dereferencable { $$ = $1; }
|
||||
| fully_dereferencable { $$ = $1; }
|
||||
;
|
||||
|
||||
exit_expr:
|
||||
@ -827,9 +832,20 @@ ctor_arguments:
|
||||
|
||||
constant:
|
||||
name { $$ = Expr\ConstFetch[$1]; }
|
||||
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM identifier_ex
|
||||
| T_LINE { $$ = Scalar\MagicConst\Line[]; }
|
||||
| T_FILE { $$ = Scalar\MagicConst\File[]; }
|
||||
| T_DIR { $$ = Scalar\MagicConst\Dir[]; }
|
||||
| T_CLASS_C { $$ = Scalar\MagicConst\Class_[]; }
|
||||
| T_TRAIT_C { $$ = Scalar\MagicConst\Trait_[]; }
|
||||
| T_METHOD_C { $$ = Scalar\MagicConst\Method[]; }
|
||||
| T_FUNC_C { $$ = Scalar\MagicConst\Function_[]; }
|
||||
| T_NS_C { $$ = Scalar\MagicConst\Namespace_[]; }
|
||||
;
|
||||
|
||||
class_constant:
|
||||
class_name_or_var T_PAAMAYIM_NEKUDOTAYIM identifier_ex
|
||||
{ $$ = Expr\ClassConstFetch[$1, $3]; }
|
||||
/* We interpret and isolated FOO:: as an unfinished class constant fetch. It could also be
|
||||
/* We interpret an isolated FOO:: as an unfinished class constant fetch. It could also be
|
||||
an unfinished static property fetch or unfinished scoped call. */
|
||||
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM error
|
||||
{ $$ = Expr\ClassConstFetch[$1, new Expr\Error(stackAttributes(#3))]; $this->errorState = 2; }
|
||||
@ -849,28 +865,21 @@ dereferencable_scalar:
|
||||
| T_CONSTANT_ENCAPSED_STRING
|
||||
{ $attrs = attributes(); $attrs['kind'] = strKind($1);
|
||||
$$ = new Scalar\String_(Scalar\String_::parse($1), $attrs); }
|
||||
| '"' encaps_list '"'
|
||||
{ $attrs = attributes(); $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED;
|
||||
parseEncapsed($2, '"', true); $$ = new Scalar\Encapsed($2, $attrs); }
|
||||
;
|
||||
|
||||
scalar:
|
||||
T_LNUMBER { $$ = $this->parseLNumber($1, attributes()); }
|
||||
| T_DNUMBER { $$ = Scalar\DNumber[Scalar\DNumber::parse($1)]; }
|
||||
| T_LINE { $$ = Scalar\MagicConst\Line[]; }
|
||||
| T_FILE { $$ = Scalar\MagicConst\File[]; }
|
||||
| T_DIR { $$ = Scalar\MagicConst\Dir[]; }
|
||||
| T_CLASS_C { $$ = Scalar\MagicConst\Class_[]; }
|
||||
| T_TRAIT_C { $$ = Scalar\MagicConst\Trait_[]; }
|
||||
| T_METHOD_C { $$ = Scalar\MagicConst\Method[]; }
|
||||
| T_FUNC_C { $$ = Scalar\MagicConst\Function_[]; }
|
||||
| T_NS_C { $$ = Scalar\MagicConst\Namespace_[]; }
|
||||
| dereferencable_scalar { $$ = $1; }
|
||||
| constant { $$ = $1; }
|
||||
| class_constant { $$ = $1; }
|
||||
| T_START_HEREDOC T_ENCAPSED_AND_WHITESPACE T_END_HEREDOC
|
||||
{ $$ = $this->parseDocString($1, $2, $3, attributes(), stackAttributes(#3), true); }
|
||||
| T_START_HEREDOC T_END_HEREDOC
|
||||
{ $$ = $this->parseDocString($1, '', $2, attributes(), stackAttributes(#2), true); }
|
||||
| '"' encaps_list '"'
|
||||
{ $attrs = attributes(); $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED;
|
||||
parseEncapsed($2, '"', true); $$ = new Scalar\Encapsed($2, $attrs); }
|
||||
| T_START_HEREDOC encaps_list T_END_HEREDOC
|
||||
{ $$ = $this->parseDocString($1, $2, $3, attributes(), stackAttributes(#3), true); }
|
||||
;
|
||||
@ -880,10 +889,16 @@ optional_expr:
|
||||
| expr { $$ = $1; }
|
||||
;
|
||||
|
||||
dereferencable:
|
||||
fully_dereferencable:
|
||||
variable { $$ = $1; }
|
||||
| '(' expr ')' { $$ = $2; }
|
||||
| dereferencable_scalar { $$ = $1; }
|
||||
| class_constant { $$ = $1; }
|
||||
;
|
||||
|
||||
array_object_dereferencable:
|
||||
fully_dereferencable { $$ = $1; }
|
||||
| constant { $$ = $1; }
|
||||
;
|
||||
|
||||
callable_expr:
|
||||
@ -894,18 +909,18 @@ callable_expr:
|
||||
|
||||
callable_variable:
|
||||
simple_variable { $$ = Expr\Variable[$1]; }
|
||||
| dereferencable '[' optional_expr ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
| constant '[' optional_expr ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
| dereferencable '{' expr '}' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
| array_object_dereferencable '[' optional_expr ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
| array_object_dereferencable '{' expr '}' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
| function_call { $$ = $1; }
|
||||
| dereferencable T_OBJECT_OPERATOR property_name argument_list
|
||||
| array_object_dereferencable T_OBJECT_OPERATOR property_name argument_list
|
||||
{ $$ = Expr\MethodCall[$1, $3, $4]; }
|
||||
;
|
||||
|
||||
variable:
|
||||
callable_variable { $$ = $1; }
|
||||
| static_member { $$ = $1; }
|
||||
| dereferencable T_OBJECT_OPERATOR property_name { $$ = Expr\PropertyFetch[$1, $3]; }
|
||||
| array_object_dereferencable T_OBJECT_OPERATOR property_name
|
||||
{ $$ = Expr\PropertyFetch[$1, $3]; }
|
||||
;
|
||||
|
||||
simple_variable:
|
||||
@ -950,22 +965,7 @@ property_name:
|
||||
;
|
||||
|
||||
list_expr:
|
||||
T_LIST '(' list_expr_elements ')' { $$ = Expr\List_[$3]; }
|
||||
;
|
||||
|
||||
list_expr_elements:
|
||||
list_expr_elements ',' list_expr_element { push($1, $3); }
|
||||
| list_expr_element { init($1); }
|
||||
;
|
||||
|
||||
list_expr_element:
|
||||
variable { $$ = Expr\ArrayItem[$1, null, false]; }
|
||||
| '&' variable { $$ = Expr\ArrayItem[$2, null, true]; }
|
||||
| list_expr { $$ = Expr\ArrayItem[$1, null, false]; }
|
||||
| expr T_DOUBLE_ARROW variable { $$ = Expr\ArrayItem[$3, $1, false]; }
|
||||
| expr T_DOUBLE_ARROW '&' variable { $$ = Expr\ArrayItem[$4, $1, true]; }
|
||||
| expr T_DOUBLE_ARROW list_expr { $$ = Expr\ArrayItem[$3, $1, false]; }
|
||||
| /* empty */ { $$ = null; }
|
||||
T_LIST '(' inner_array_pair_list ')' { $$ = Expr\List_[$3]; }
|
||||
;
|
||||
|
||||
array_pair_list:
|
||||
@ -985,10 +985,12 @@ inner_array_pair_list:
|
||||
;
|
||||
|
||||
array_pair:
|
||||
expr T_DOUBLE_ARROW expr { $$ = Expr\ArrayItem[$3, $1, false]; }
|
||||
| expr { $$ = Expr\ArrayItem[$1, null, false]; }
|
||||
| expr T_DOUBLE_ARROW '&' variable { $$ = Expr\ArrayItem[$4, $1, true]; }
|
||||
expr { $$ = Expr\ArrayItem[$1, null, false]; }
|
||||
| '&' variable { $$ = Expr\ArrayItem[$2, null, true]; }
|
||||
| list_expr { $$ = Expr\ArrayItem[$1, null, false]; }
|
||||
| expr T_DOUBLE_ARROW expr { $$ = Expr\ArrayItem[$3, $1, false]; }
|
||||
| expr T_DOUBLE_ARROW '&' variable { $$ = Expr\ArrayItem[$4, $1, true]; }
|
||||
| expr T_DOUBLE_ARROW list_expr { $$ = Expr\ArrayItem[$3, $1, false]; }
|
||||
| T_ELLIPSIS expr { $$ = Expr\ArrayItem[$2, null, false, attributes(), true]; }
|
||||
| /* empty */ { $$ = null; }
|
||||
;
|
||||
|
@ -178,7 +178,7 @@ function resolveMacros($code) {
|
||||
|
||||
return '$startAttributes = ' . $args[1] . ';'
|
||||
. ' if (isset($startAttributes[\'comments\']))'
|
||||
. ' { ' . $args[0] . ' = new Stmt\Nop($this->createZeroLengthAttributes($startAttributes)); }'
|
||||
. ' { ' . $args[0] . ' = new Stmt\Nop($this->createCommentNopAttributes($startAttributes[\'comments\'])); }'
|
||||
. ' else { ' . $args[0] . ' = null; }';
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ class Param implements PhpParser\Builder
|
||||
/**
|
||||
* Sets type for the parameter.
|
||||
*
|
||||
* @param string|Node\Name|Node\NullableType $type Parameter type
|
||||
* @param string|Node\Name|Node\NullableType|Node\UnionType $type Parameter type
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
@ -60,7 +60,7 @@ class Param implements PhpParser\Builder
|
||||
/**
|
||||
* Sets type for the parameter.
|
||||
*
|
||||
* @param string|Node\Name|Node\NullableType $type Parameter type
|
||||
* @param string|Node\Name|Node\NullableType|Node\UnionType $type Parameter type
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*
|
||||
|
@ -8,6 +8,7 @@ use PhpParser\Node\Name;
|
||||
use PhpParser\Node\NullableType;
|
||||
use PhpParser\Node\Scalar;
|
||||
use PhpParser\Node\Stmt;
|
||||
use PhpParser\Node\UnionType;
|
||||
|
||||
/**
|
||||
* This class defines helpers used in the implementation of builders. Don't use it directly.
|
||||
@ -158,16 +159,19 @@ final class BuilderHelpers
|
||||
* In particular, builtin types become Identifiers, custom types become Names and nullables
|
||||
* are wrapped in NullableType nodes.
|
||||
*
|
||||
* @param string|Name|Identifier|NullableType $type The type to normalize
|
||||
* @param string|Name|Identifier|NullableType|UnionType $type The type to normalize
|
||||
*
|
||||
* @return Name|Identifier|NullableType The normalized type
|
||||
* @return Name|Identifier|NullableType|UnionType The normalized type
|
||||
*/
|
||||
public static function normalizeType($type) {
|
||||
if (!is_string($type)) {
|
||||
if (!$type instanceof Name && !$type instanceof Identifier
|
||||
&& !$type instanceof NullableType) {
|
||||
if (
|
||||
!$type instanceof Name && !$type instanceof Identifier &&
|
||||
!$type instanceof NullableType && !$type instanceof UnionType
|
||||
) {
|
||||
throw new \LogicException(
|
||||
'Type must be a string, or an instance of Name, Identifier or NullableType');
|
||||
'Type must be a string, or an instance of Name, Identifier, NullableType or UnionType'
|
||||
);
|
||||
}
|
||||
return $type;
|
||||
}
|
||||
@ -193,7 +197,7 @@ final class BuilderHelpers
|
||||
throw new \LogicException('void type cannot be nullable');
|
||||
}
|
||||
|
||||
return $nullable ? new Node\NullableType($type) : $type;
|
||||
return $nullable ? new NullableType($type) : $type;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,9 +5,12 @@ namespace PhpParser;
|
||||
class Comment implements \JsonSerializable
|
||||
{
|
||||
protected $text;
|
||||
protected $line;
|
||||
protected $filePos;
|
||||
protected $tokenPos;
|
||||
protected $startLine;
|
||||
protected $startFilePos;
|
||||
protected $startTokenPos;
|
||||
protected $endLine;
|
||||
protected $endFilePos;
|
||||
protected $endTokenPos;
|
||||
|
||||
/**
|
||||
* Constructs a comment node.
|
||||
@ -18,12 +21,17 @@ class Comment implements \JsonSerializable
|
||||
* @param int $startTokenPos Token offset the comment started on
|
||||
*/
|
||||
public function __construct(
|
||||
string $text, int $startLine = -1, int $startFilePos = -1, int $startTokenPos = -1
|
||||
string $text,
|
||||
int $startLine = -1, int $startFilePos = -1, int $startTokenPos = -1,
|
||||
int $endLine = -1, int $endFilePos = -1, int $endTokenPos = -1
|
||||
) {
|
||||
$this->text = $text;
|
||||
$this->line = $startLine;
|
||||
$this->filePos = $startFilePos;
|
||||
$this->tokenPos = $startTokenPos;
|
||||
$this->startLine = $startLine;
|
||||
$this->startFilePos = $startFilePos;
|
||||
$this->startTokenPos = $startTokenPos;
|
||||
$this->endLine = $endLine;
|
||||
$this->endFilePos = $endFilePos;
|
||||
$this->endTokenPos = $endTokenPos;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -38,28 +46,88 @@ class Comment implements \JsonSerializable
|
||||
/**
|
||||
* Gets the line number the comment started on.
|
||||
*
|
||||
* @return int Line number
|
||||
* @return int Line number (or -1 if not available)
|
||||
*/
|
||||
public function getLine() : int {
|
||||
return $this->line;
|
||||
public function getStartLine() : int {
|
||||
return $this->startLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file offset the comment started on.
|
||||
*
|
||||
* @return int File offset
|
||||
* @return int File offset (or -1 if not available)
|
||||
*/
|
||||
public function getFilePos() : int {
|
||||
return $this->filePos;
|
||||
public function getStartFilePos() : int {
|
||||
return $this->startFilePos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the token offset the comment started on.
|
||||
*
|
||||
* @return int Token offset (or -1 if not available)
|
||||
*/
|
||||
public function getStartTokenPos() : int {
|
||||
return $this->startTokenPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the line number the comment ends on.
|
||||
*
|
||||
* @return int Line number (or -1 if not available)
|
||||
*/
|
||||
public function getEndLine() : int {
|
||||
return $this->endLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file offset the comment ends on.
|
||||
*
|
||||
* @return int File offset (or -1 if not available)
|
||||
*/
|
||||
public function getEndFilePos() : int {
|
||||
return $this->endFilePos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the token offset the comment ends on.
|
||||
*
|
||||
* @return int Token offset (or -1 if not available)
|
||||
*/
|
||||
public function getEndTokenPos() : int {
|
||||
return $this->endTokenPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the line number the comment started on.
|
||||
*
|
||||
* @deprecated Use getStartLine() instead
|
||||
*
|
||||
* @return int Line number
|
||||
*/
|
||||
public function getLine() : int {
|
||||
return $this->startLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file offset the comment started on.
|
||||
*
|
||||
* @deprecated Use getStartFilePos() instead
|
||||
*
|
||||
* @return int File offset
|
||||
*/
|
||||
public function getFilePos() : int {
|
||||
return $this->startFilePos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the token offset the comment started on.
|
||||
*
|
||||
* @deprecated Use getStartTokenPos() instead
|
||||
*
|
||||
* @return int Token offset
|
||||
*/
|
||||
public function getTokenPos() : int {
|
||||
return $this->tokenPos;
|
||||
return $this->startTokenPos;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -159,9 +227,13 @@ class Comment implements \JsonSerializable
|
||||
return [
|
||||
'nodeType' => $type,
|
||||
'text' => $this->text,
|
||||
'line' => $this->line,
|
||||
'filePos' => $this->filePos,
|
||||
'tokenPos' => $this->tokenPos,
|
||||
// TODO: Rename these to include "start".
|
||||
'line' => $this->startLine,
|
||||
'filePos' => $this->startFilePos,
|
||||
'tokenPos' => $this->startTokenPos,
|
||||
'endLine' => $this->endLine,
|
||||
'endFilePos' => $this->endFilePos,
|
||||
'endTokenPos' => $this->endTokenPos,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +73,9 @@ class JsonDecoder
|
||||
}
|
||||
|
||||
return new $className(
|
||||
$value['text'], $value['line'] ?? -1, $value['filePos'] ?? -1, $value['tokenPos'] ?? -1
|
||||
$value['text'],
|
||||
$value['line'] ?? -1, $value['filePos'] ?? -1, $value['tokenPos'] ?? -1,
|
||||
$value['endLine'] ?? -1, $value['endFilePos'] ?? -1, $value['endTokenPos'] ?? -1
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -300,17 +300,23 @@ class Lexer
|
||||
$this->line += substr_count($value, "\n");
|
||||
$this->filePos += \strlen($value);
|
||||
} else {
|
||||
$origLine = $this->line;
|
||||
$origFilePos = $this->filePos;
|
||||
$this->line += substr_count($token[1], "\n");
|
||||
$this->filePos += \strlen($token[1]);
|
||||
|
||||
if (\T_COMMENT === $token[0] || \T_DOC_COMMENT === $token[0]) {
|
||||
if ($this->attributeCommentsUsed) {
|
||||
$comment = \T_DOC_COMMENT === $token[0]
|
||||
? new Comment\Doc($token[1], $this->line, $this->filePos, $this->pos)
|
||||
: new Comment($token[1], $this->line, $this->filePos, $this->pos);
|
||||
? new Comment\Doc($token[1],
|
||||
$origLine, $origFilePos, $this->pos,
|
||||
$this->line, $this->filePos - 1, $this->pos)
|
||||
: new Comment($token[1],
|
||||
$origLine, $origFilePos, $this->pos,
|
||||
$this->line, $this->filePos - 1, $this->pos);
|
||||
$startAttributes['comments'][] = $comment;
|
||||
}
|
||||
}
|
||||
|
||||
$this->line += substr_count($token[1], "\n");
|
||||
$this->filePos += \strlen($token[1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,8 @@ final class NumericLiteralSeparatorEmulator implements TokenEmulatorInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
return preg_match('~[0-9a-f]_[0-9a-f]~i', $code) !== false;
|
||||
return preg_match('~[0-9]_[0-9]~', $code)
|
||||
|| preg_match('~0x[0-9a-f]+_[0-9a-f]~i', $code);
|
||||
}
|
||||
|
||||
public function emulate(string $code, array $tokens): array
|
||||
|
@ -95,8 +95,6 @@ interface Node
|
||||
/**
|
||||
* Gets the doc comment of the node.
|
||||
*
|
||||
* The doc comment has to be the last comment associated with the node.
|
||||
*
|
||||
* @return null|Comment\Doc Doc comment object or null
|
||||
*/
|
||||
public function getDocComment();
|
||||
|
@ -53,6 +53,26 @@ abstract class ClassLike extends Node\Stmt
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets property with the given name defined directly in this class/interface/trait.
|
||||
*
|
||||
* @param string $name Name of the property
|
||||
*
|
||||
* @return Property|null Property node or null if the property does not exist
|
||||
*/
|
||||
public function getProperty(string $name) {
|
||||
foreach ($this->stmts as $stmt) {
|
||||
if ($stmt instanceof Property) {
|
||||
foreach ($stmt->props as $prop) {
|
||||
if ($prop instanceof PropertyProperty && $name === $prop->name->toString()) {
|
||||
return $stmt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all methods defined directly in this class/interface/trait
|
||||
*
|
||||
|
@ -108,22 +108,18 @@ abstract class NodeAbstract implements Node, \JsonSerializable
|
||||
/**
|
||||
* Gets the doc comment of the node.
|
||||
*
|
||||
* The doc comment has to be the last comment associated with the node.
|
||||
*
|
||||
* @return null|Comment\Doc Doc comment object or null
|
||||
*/
|
||||
public function getDocComment() {
|
||||
$comments = $this->getComments();
|
||||
if (!$comments) {
|
||||
return null;
|
||||
for ($i = count($comments) - 1; $i >= 0; $i--) {
|
||||
$comment = $comments[$i];
|
||||
if ($comment instanceof Comment\Doc) {
|
||||
return $comment;
|
||||
}
|
||||
}
|
||||
|
||||
$lastComment = $comments[count($comments) - 1];
|
||||
if (!$lastComment instanceof Comment\Doc) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $lastComment;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -135,16 +131,17 @@ abstract class NodeAbstract implements Node, \JsonSerializable
|
||||
*/
|
||||
public function setDocComment(Comment\Doc $docComment) {
|
||||
$comments = $this->getComments();
|
||||
|
||||
$numComments = count($comments);
|
||||
if ($numComments > 0 && $comments[$numComments - 1] instanceof Comment\Doc) {
|
||||
// Replace existing doc comment
|
||||
$comments[$numComments - 1] = $docComment;
|
||||
} else {
|
||||
// Append new comment
|
||||
$comments[] = $docComment;
|
||||
for ($i = count($comments) - 1; $i >= 0; $i--) {
|
||||
if ($comments[$i] instanceof Comment\Doc) {
|
||||
// Replace existing doc comment.
|
||||
$comments[$i] = $docComment;
|
||||
$this->setAttribute('comments', $comments);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Append new doc comment.
|
||||
$comments[] = $docComment;
|
||||
$this->setAttribute('comments', $comments);
|
||||
}
|
||||
|
||||
|
@ -944,7 +944,7 @@ class Php5 extends \PhpParser\ParserAbstract
|
||||
$this->semValue = array();
|
||||
},
|
||||
4 => function ($stackPos) {
|
||||
$startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($this->createZeroLengthAttributes($startAttributes)); } else { $nop = null; };
|
||||
$startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($this->createCommentNopAttributes($startAttributes['comments'])); } else { $nop = null; };
|
||||
if ($nop !== null) { $this->semStack[$stackPos-(1-1)][] = $nop; } $this->semValue = $this->semStack[$stackPos-(1-1)];
|
||||
},
|
||||
5 => function ($stackPos) {
|
||||
@ -1317,7 +1317,7 @@ class Php5 extends \PhpParser\ParserAbstract
|
||||
$this->semValue = array();
|
||||
},
|
||||
126 => function ($stackPos) {
|
||||
$startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($this->createZeroLengthAttributes($startAttributes)); } else { $nop = null; };
|
||||
$startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($this->createCommentNopAttributes($startAttributes['comments'])); } else { $nop = null; };
|
||||
if ($nop !== null) { $this->semStack[$stackPos-(1-1)][] = $nop; } $this->semValue = $this->semStack[$stackPos-(1-1)];
|
||||
},
|
||||
127 => function ($stackPos) {
|
||||
@ -1715,7 +1715,7 @@ class Php5 extends \PhpParser\ParserAbstract
|
||||
$this->semValue = array();
|
||||
},
|
||||
255 => function ($stackPos) {
|
||||
$startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($this->createZeroLengthAttributes($startAttributes)); } else { $nop = null; };
|
||||
$startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($this->createCommentNopAttributes($startAttributes['comments'])); } else { $nop = null; };
|
||||
if ($nop !== null) { $this->semStack[$stackPos-(1-1)][] = $nop; } $this->semValue = $this->semStack[$stackPos-(1-1)];
|
||||
},
|
||||
256 => function ($stackPos) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -842,21 +842,29 @@ abstract class ParserAbstract implements Parser
|
||||
}
|
||||
|
||||
/**
|
||||
* Create attributes for a zero-length node with the given start attributes.
|
||||
* Create attributes for a zero-length common-capturing nop.
|
||||
*
|
||||
* @param array $startAttributes
|
||||
* @param Comment[] $comments
|
||||
* @return array
|
||||
*/
|
||||
protected function createZeroLengthAttributes(array $startAttributes) {
|
||||
$attributes = $startAttributes;
|
||||
if (isset($startAttributes['startLine'])) {
|
||||
$attributes['endLine'] = $startAttributes['startLine'];
|
||||
protected function createCommentNopAttributes(array $comments) {
|
||||
$comment = $comments[count($comments) - 1];
|
||||
$commentEndLine = $comment->getEndLine();
|
||||
$commentEndFilePos = $comment->getEndFilePos();
|
||||
$commentEndTokenPos = $comment->getEndTokenPos();
|
||||
|
||||
$attributes = ['comments' => $comments];
|
||||
if (-1 !== $commentEndLine) {
|
||||
$attributes['startLine'] = $commentEndLine;
|
||||
$attributes['endLine'] = $commentEndLine;
|
||||
}
|
||||
if (isset($startAttributes['startTokenPos'])) {
|
||||
$attributes['endTokenPos'] = $startAttributes['startTokenPos'] - 1;
|
||||
if (-1 !== $commentEndFilePos) {
|
||||
$attributes['startFilePos'] = $commentEndFilePos + 1;
|
||||
$attributes['endFilePos'] = $commentEndFilePos;
|
||||
}
|
||||
if (isset($startAttributes['startFilePos'])) {
|
||||
$attributes['endFilePos'] = $startAttributes['startFilePos'] - 1;
|
||||
if (-1 !== $commentEndTokenPos) {
|
||||
$attributes['startTokenPos'] = $commentEndTokenPos + 1;
|
||||
$attributes['endTokenPos'] = $commentEndTokenPos;
|
||||
}
|
||||
return $attributes;
|
||||
}
|
||||
|
@ -756,20 +756,13 @@ abstract class PrettyPrinterAbstract
|
||||
$itemEndPos = $origArrItem->getEndTokenPos();
|
||||
\assert($itemStartPos >= 0 && $itemEndPos >= 0);
|
||||
|
||||
if ($itemEndPos < $itemStartPos) {
|
||||
// End can be before start for Nop nodes, because offsets refer to non-whitespace
|
||||
// locations, which for an "empty" node might result in an inverted order.
|
||||
assert($origArrItem instanceof Stmt\Nop);
|
||||
continue;
|
||||
}
|
||||
|
||||
$origIndentLevel = $this->indentLevel;
|
||||
$lastElemIndentLevel = $this->origTokens->getIndentationBefore($itemStartPos) + $indentAdjustment;
|
||||
$this->setIndentLevel($lastElemIndentLevel);
|
||||
|
||||
$comments = $arrItem->getComments();
|
||||
$origComments = $origArrItem->getComments();
|
||||
$commentStartPos = $origComments ? $origComments[0]->getTokenPos() : $itemStartPos;
|
||||
$commentStartPos = $origComments ? $origComments[0]->getStartTokenPos() : $itemStartPos;
|
||||
\assert($commentStartPos >= 0);
|
||||
|
||||
$commentsChanged = $comments !== $origComments;
|
||||
|
@ -80,6 +80,8 @@ class ParamTest extends \PHPUnit\Framework\TestCase
|
||||
|
||||
/**
|
||||
* @dataProvider provideTestTypes
|
||||
* @dataProvider provideTestNullableTypes
|
||||
* @dataProvider provideTestUnionTypes
|
||||
*/
|
||||
public function testTypes($typeHint, $expectedType) {
|
||||
$node = $this->createParamBuilder('test')
|
||||
@ -114,9 +116,14 @@ class ParamTest extends \PHPUnit\Framework\TestCase
|
||||
['Some\Class', new Node\Name('Some\Class')],
|
||||
['\Foo', new Node\Name\FullyQualified('Foo')],
|
||||
['self', new Node\Name('self')],
|
||||
[new Node\Name('Some\Class'), new Node\Name('Some\Class')],
|
||||
];
|
||||
}
|
||||
|
||||
public function provideTestNullableTypes() {
|
||||
return [
|
||||
['?array', new Node\NullableType(new Node\Identifier('array'))],
|
||||
['?Some\Class', new Node\NullableType(new Node\Name('Some\Class'))],
|
||||
[new Node\Name('Some\Class'), new Node\Name('Some\Class')],
|
||||
[
|
||||
new Node\NullableType(new Node\Identifier('int')),
|
||||
new Node\NullableType(new Node\Identifier('int'))
|
||||
@ -128,6 +135,33 @@ class ParamTest extends \PHPUnit\Framework\TestCase
|
||||
];
|
||||
}
|
||||
|
||||
public function provideTestUnionTypes() {
|
||||
return [
|
||||
[
|
||||
new Node\UnionType([
|
||||
new Node\Name('Some\Class'),
|
||||
new Node\Identifier('array'),
|
||||
]),
|
||||
new Node\UnionType([
|
||||
new Node\Name('Some\Class'),
|
||||
new Node\Identifier('array'),
|
||||
]),
|
||||
],
|
||||
[
|
||||
new Node\UnionType([
|
||||
new Node\Identifier('self'),
|
||||
new Node\Identifier('array'),
|
||||
new Node\Name\FullyQualified('Foo')
|
||||
]),
|
||||
new Node\UnionType([
|
||||
new Node\Identifier('self'),
|
||||
new Node\Identifier('array'),
|
||||
new Node\Name\FullyQualified('Foo')
|
||||
]),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function testVoidTypeError() {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Parameter type cannot be void');
|
||||
@ -136,7 +170,7 @@ class ParamTest extends \PHPUnit\Framework\TestCase
|
||||
|
||||
public function testInvalidTypeError() {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Type must be a string, or an instance of Name, Identifier or NullableType');
|
||||
$this->expectExceptionMessage('Type must be a string, or an instance of Name, Identifier, NullableType or UnionType');
|
||||
$this->createParamBuilder('test')->setType(new \stdClass);
|
||||
}
|
||||
|
||||
|
@ -4,14 +4,21 @@ namespace PhpParser;
|
||||
|
||||
class CommentTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testGetSet() {
|
||||
$comment = new Comment('/* Some comment */', 1, 10, 2);
|
||||
public function testGetters() {
|
||||
$comment = new Comment('/* Some comment */',
|
||||
1, 10, 2, 1, 27, 2);
|
||||
|
||||
$this->assertSame('/* Some comment */', $comment->getText());
|
||||
$this->assertSame('/* Some comment */', (string) $comment);
|
||||
$this->assertSame(1, $comment->getLine());
|
||||
$this->assertSame(10, $comment->getFilePos());
|
||||
$this->assertSame(2, $comment->getTokenPos());
|
||||
$this->assertSame(1, $comment->getStartLine());
|
||||
$this->assertSame(10, $comment->getStartFilePos());
|
||||
$this->assertSame(2, $comment->getStartTokenPos());
|
||||
$this->assertSame(1, $comment->getEndLine());
|
||||
$this->assertSame(27, $comment->getEndFilePos());
|
||||
$this->assertSame(2, $comment->getEndTokenPos());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -103,7 +103,9 @@ class LexerTest extends \PHPUnit\Framework\TestCase
|
||||
[
|
||||
'startLine' => 3,
|
||||
'comments' => [
|
||||
new Comment\Doc('/** doc' . "\n" . 'comment */', 2, 14, 5),
|
||||
new Comment\Doc('/** doc' . "\n" . 'comment */',
|
||||
2, 14, 5,
|
||||
3, 31, 5),
|
||||
]
|
||||
],
|
||||
['endLine' => 3]
|
||||
@ -120,10 +122,14 @@ class LexerTest extends \PHPUnit\Framework\TestCase
|
||||
[
|
||||
'startLine' => 2,
|
||||
'comments' => [
|
||||
new Comment('/* comment */', 1, 6, 1),
|
||||
new Comment('// comment' . "\n", 1, 20, 3),
|
||||
new Comment\Doc('/** docComment 1 */', 2, 31, 4),
|
||||
new Comment\Doc('/** docComment 2 */', 2, 50, 5),
|
||||
new Comment('/* comment */',
|
||||
1, 6, 1, 1, 18, 1),
|
||||
new Comment('// comment' . "\n",
|
||||
1, 20, 3, 2, 30, 3),
|
||||
new Comment\Doc('/** docComment 1 */',
|
||||
2, 31, 4, 2, 49, 4),
|
||||
new Comment\Doc('/** docComment 2 */',
|
||||
2, 50, 5, 2, 68, 5),
|
||||
],
|
||||
],
|
||||
['endLine' => 2]
|
||||
|
@ -94,6 +94,31 @@ class ClassTest extends \PHPUnit\Framework\TestCase
|
||||
$this->assertSame($properties, $class->getProperties());
|
||||
}
|
||||
|
||||
public function testGetProperty() {
|
||||
$properties = [
|
||||
$fooProp = new Property(Class_::MODIFIER_PUBLIC, [new PropertyProperty('foo1')]),
|
||||
$barProp = new Property(Class_::MODIFIER_PUBLIC, [new PropertyProperty('BAR1')]),
|
||||
$fooBarProp = new Property(Class_::MODIFIER_PUBLIC, [new PropertyProperty('foo2'), new PropertyProperty('bar2')]),
|
||||
];
|
||||
$class = new Class_('Foo', [
|
||||
'stmts' => [
|
||||
new TraitUse([]),
|
||||
$properties[0],
|
||||
new ClassConst([]),
|
||||
$properties[1],
|
||||
new ClassMethod('fooBar'),
|
||||
$properties[2],
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertSame($fooProp, $class->getProperty('foo1'));
|
||||
$this->assertSame($barProp, $class->getProperty('BAR1'));
|
||||
$this->assertSame($fooBarProp, $class->getProperty('foo2'));
|
||||
$this->assertSame($fooBarProp, $class->getProperty('bar2'));
|
||||
$this->assertNull($class->getProperty('bar1'));
|
||||
$this->assertNull($class->getProperty('nonExisting'));
|
||||
}
|
||||
|
||||
public function testGetMethod() {
|
||||
$methodConstruct = new ClassMethod('__CONSTRUCT');
|
||||
$methodTest = new ClassMethod('test');
|
||||
|
@ -34,8 +34,9 @@ class NodeAbstractTest extends \PHPUnit\Framework\TestCase
|
||||
'startFilePos' => 14,
|
||||
'endFilePos' => 15,
|
||||
'comments' => [
|
||||
new Comment('// Comment' . "\n"),
|
||||
new Comment('// Comment 1' . "\n"),
|
||||
new Comment\Doc('/** doc comment */'),
|
||||
new Comment('// Comment 2' . "\n"),
|
||||
],
|
||||
];
|
||||
|
||||
@ -79,12 +80,12 @@ class NodeAbstractTest extends \PHPUnit\Framework\TestCase
|
||||
$this->assertSame('/** doc comment */', $node->getDocComment()->getText());
|
||||
$comments = $node->getComments();
|
||||
|
||||
array_pop($comments); // remove doc comment
|
||||
array_splice($comments, 1, 1, []); // remove doc comment
|
||||
$node->setAttribute('comments', $comments);
|
||||
$this->assertNull($node->getDocComment());
|
||||
|
||||
array_pop($comments); // remove comment
|
||||
$node->setAttribute('comments', $comments);
|
||||
// Remove all comments.
|
||||
$node->setAttribute('comments', []);
|
||||
$this->assertNull($node->getDocComment());
|
||||
}
|
||||
|
||||
@ -108,6 +109,12 @@ class NodeAbstractTest extends \PHPUnit\Framework\TestCase
|
||||
$node->setAttribute('comments', [$c1, $c2]);
|
||||
$node->setDocComment($docComment);
|
||||
$this->assertSame([$c1, $c2, $docComment], $node->getAttribute('comments'));
|
||||
|
||||
// Replace doc comment that is not at the end.
|
||||
$newDocComment = new Comment\Doc('/** new baz */');
|
||||
$node->setAttribute('comments', [$c1, $docComment, $c2]);
|
||||
$node->setDocComment($newDocComment);
|
||||
$this->assertSame([$c1, $newDocComment, $c2], $node->getAttribute('comments'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -301,14 +308,20 @@ PHP;
|
||||
"text": "\/\/ comment\n",
|
||||
"line": 2,
|
||||
"filePos": 6,
|
||||
"tokenPos": 1
|
||||
"tokenPos": 1,
|
||||
"endLine": 3,
|
||||
"endFilePos": 16,
|
||||
"endTokenPos": 1
|
||||
},
|
||||
{
|
||||
"nodeType": "Comment_Doc",
|
||||
"text": "\/** doc comment *\/",
|
||||
"line": 3,
|
||||
"filePos": 17,
|
||||
"tokenPos": 2
|
||||
"tokenPos": 2,
|
||||
"endLine": 3,
|
||||
"endFilePos": 34,
|
||||
"endTokenPos": 2
|
||||
}
|
||||
],
|
||||
"endLine": 6
|
||||
|
@ -60,7 +60,8 @@ EOC;
|
||||
$this->assertInstanceOf(Stmt\Function_::class, $fn);
|
||||
$this->assertEquals([
|
||||
'comments' => [
|
||||
new Comment\Doc('/** Doc comment */', 2, 6, 1),
|
||||
new Comment\Doc('/** Doc comment */',
|
||||
2, 6, 1, 2, 23, 1),
|
||||
],
|
||||
'startLine' => 3,
|
||||
'endLine' => 7,
|
||||
@ -82,8 +83,10 @@ EOC;
|
||||
$this->assertInstanceOf(Stmt\Echo_::class, $echo);
|
||||
$this->assertEquals([
|
||||
'comments' => [
|
||||
new Comment("// Line\n", 4, 49, 12),
|
||||
new Comment("// Comments\n", 5, 61, 14),
|
||||
new Comment("// Line\n",
|
||||
4, 49, 12, 5, 56, 12),
|
||||
new Comment("// Comments\n",
|
||||
5, 61, 14, 6, 72, 14),
|
||||
],
|
||||
'startLine' => 6,
|
||||
'endLine' => 6,
|
||||
|
76
test/code/formatPreservation/classMethodNop.test
Normal file
76
test/code/formatPreservation/classMethodNop.test
Normal file
@ -0,0 +1,76 @@
|
||||
Adding statement to Class Method containing Nop
|
||||
-----
|
||||
<?php
|
||||
class Foo {
|
||||
public function __construct()
|
||||
{
|
||||
// I'm just a comment
|
||||
}
|
||||
}
|
||||
-----
|
||||
$stmts[0]->stmts[0]->stmts[] = new Stmt\Expression(new Node\Expr\Variable('foo'));
|
||||
-----
|
||||
<?php
|
||||
class Foo {
|
||||
public function __construct()
|
||||
{
|
||||
// I'm just a comment
|
||||
|
||||
$foo; }
|
||||
}
|
||||
-----
|
||||
<?php
|
||||
class Foo {
|
||||
public function __construct()
|
||||
{
|
||||
/* I'm just a comment */
|
||||
}
|
||||
}
|
||||
-----
|
||||
$stmts[0]->stmts[0]->stmts[] = new Stmt\Expression(new Node\Expr\Variable('foo'));
|
||||
-----
|
||||
<?php
|
||||
class Foo {
|
||||
public function __construct()
|
||||
{
|
||||
/* I'm just a comment */
|
||||
$foo;
|
||||
}
|
||||
}
|
||||
-----
|
||||
<?php
|
||||
class Foo {
|
||||
public function __construct()
|
||||
{
|
||||
/* I'm just a comment */
|
||||
}
|
||||
}
|
||||
-----
|
||||
$stmts[0]->stmts[0]->stmts[0]->setAttribute('comments', [new Comment("/* I'm a new comment */")]);
|
||||
-----
|
||||
<?php
|
||||
class Foo {
|
||||
public function __construct()
|
||||
{
|
||||
/* I'm a new comment */
|
||||
|
||||
}
|
||||
}
|
||||
-----
|
||||
<?php
|
||||
class Foo {
|
||||
public function __construct()
|
||||
{
|
||||
// I'm just a comment
|
||||
}
|
||||
}
|
||||
-----
|
||||
$stmts[0]->stmts[0]->stmts[0]->setAttribute('comments', [new Comment("// I'm a new comment\n")]);
|
||||
-----
|
||||
<?php
|
||||
class Foo {
|
||||
public function __construct()
|
||||
{
|
||||
// I'm a new comment
|
||||
}
|
||||
}
|
49
test/code/parser/expr/exprInIsset.test
Normal file
49
test/code/parser/expr/exprInIsset.test
Normal file
@ -0,0 +1,49 @@
|
||||
Expressions in isset()
|
||||
-----
|
||||
<?php
|
||||
// This is legal.
|
||||
isset(($a), (($b)));
|
||||
// This is illegal, but not a syntax error.
|
||||
isset(1 + 1);
|
||||
-----
|
||||
!!php7
|
||||
array(
|
||||
0: Stmt_Expression(
|
||||
expr: Expr_Isset(
|
||||
vars: array(
|
||||
0: Expr_Variable(
|
||||
name: a
|
||||
)
|
||||
1: Expr_Variable(
|
||||
name: b
|
||||
)
|
||||
)
|
||||
comments: array(
|
||||
0: // This is legal.
|
||||
)
|
||||
)
|
||||
comments: array(
|
||||
0: // This is legal.
|
||||
)
|
||||
)
|
||||
1: Stmt_Expression(
|
||||
expr: Expr_Isset(
|
||||
vars: array(
|
||||
0: Expr_BinaryOp_Plus(
|
||||
left: Scalar_LNumber(
|
||||
value: 1
|
||||
)
|
||||
right: Scalar_LNumber(
|
||||
value: 1
|
||||
)
|
||||
)
|
||||
)
|
||||
comments: array(
|
||||
0: // This is illegal, but not a syntax error.
|
||||
)
|
||||
)
|
||||
comments: array(
|
||||
0: // This is illegal, but not a syntax error.
|
||||
)
|
||||
)
|
||||
)
|
81
test/code/parser/expr/exprInList.test
Normal file
81
test/code/parser/expr/exprInList.test
Normal file
@ -0,0 +1,81 @@
|
||||
Expressions in list()
|
||||
-----
|
||||
<?php
|
||||
|
||||
// This is legal.
|
||||
list(($a), ((($b)))) = $x;
|
||||
// This is illegal, but not a syntax error.
|
||||
list(1 + 1) = $x;
|
||||
-----
|
||||
!!php7
|
||||
array(
|
||||
0: Stmt_Expression(
|
||||
expr: Expr_Assign(
|
||||
var: Expr_List(
|
||||
items: array(
|
||||
0: Expr_ArrayItem(
|
||||
key: null
|
||||
value: Expr_Variable(
|
||||
name: a
|
||||
)
|
||||
byRef: false
|
||||
unpack: false
|
||||
)
|
||||
1: Expr_ArrayItem(
|
||||
key: null
|
||||
value: Expr_Variable(
|
||||
name: b
|
||||
)
|
||||
byRef: false
|
||||
unpack: false
|
||||
)
|
||||
)
|
||||
comments: array(
|
||||
0: // This is legal.
|
||||
)
|
||||
)
|
||||
expr: Expr_Variable(
|
||||
name: x
|
||||
)
|
||||
comments: array(
|
||||
0: // This is legal.
|
||||
)
|
||||
)
|
||||
comments: array(
|
||||
0: // This is legal.
|
||||
)
|
||||
)
|
||||
1: Stmt_Expression(
|
||||
expr: Expr_Assign(
|
||||
var: Expr_List(
|
||||
items: array(
|
||||
0: Expr_ArrayItem(
|
||||
key: null
|
||||
value: Expr_BinaryOp_Plus(
|
||||
left: Scalar_LNumber(
|
||||
value: 1
|
||||
)
|
||||
right: Scalar_LNumber(
|
||||
value: 1
|
||||
)
|
||||
)
|
||||
byRef: false
|
||||
unpack: false
|
||||
)
|
||||
)
|
||||
comments: array(
|
||||
0: // This is illegal, but not a syntax error.
|
||||
)
|
||||
)
|
||||
expr: Expr_Variable(
|
||||
name: x
|
||||
)
|
||||
comments: array(
|
||||
0: // This is illegal, but not a syntax error.
|
||||
)
|
||||
)
|
||||
comments: array(
|
||||
0: // This is illegal, but not a syntax error.
|
||||
)
|
||||
)
|
||||
)
|
@ -5,6 +5,8 @@ Constant fetches
|
||||
A;
|
||||
A::B;
|
||||
A::class;
|
||||
$a::B;
|
||||
$a::class;
|
||||
-----
|
||||
array(
|
||||
0: Stmt_Expression(
|
||||
@ -40,4 +42,24 @@ array(
|
||||
)
|
||||
)
|
||||
)
|
||||
3: Stmt_Expression(
|
||||
expr: Expr_ClassConstFetch(
|
||||
class: Expr_Variable(
|
||||
name: a
|
||||
)
|
||||
name: Identifier(
|
||||
name: B
|
||||
)
|
||||
)
|
||||
)
|
||||
4: Stmt_Expression(
|
||||
expr: Expr_ClassConstFetch(
|
||||
class: Expr_Variable(
|
||||
name: a
|
||||
)
|
||||
name: Identifier(
|
||||
name: class
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
292
test/code/parser/expr/uvs/constDeref.test
Normal file
292
test/code/parser/expr/uvs/constDeref.test
Normal file
@ -0,0 +1,292 @@
|
||||
Dereferencing of constants
|
||||
-----
|
||||
<?php
|
||||
|
||||
A->length;
|
||||
A->length();
|
||||
A[0];
|
||||
A[0][1][2];
|
||||
A{0};
|
||||
|
||||
A::B[0];
|
||||
A::B[0][1][2];
|
||||
A::B{0};
|
||||
A::B->length;
|
||||
A::B->length();
|
||||
A::B::C;
|
||||
A::B::$c;
|
||||
A::B::c();
|
||||
|
||||
__FUNCTION__[0];
|
||||
__FUNCTION__->length;
|
||||
__FUNCIONT__->length();
|
||||
-----
|
||||
!!php7
|
||||
array(
|
||||
0: Stmt_Expression(
|
||||
expr: Expr_PropertyFetch(
|
||||
var: Expr_ConstFetch(
|
||||
name: Name(
|
||||
parts: array(
|
||||
0: A
|
||||
)
|
||||
)
|
||||
)
|
||||
name: Identifier(
|
||||
name: length
|
||||
)
|
||||
)
|
||||
)
|
||||
1: Stmt_Expression(
|
||||
expr: Expr_MethodCall(
|
||||
var: Expr_ConstFetch(
|
||||
name: Name(
|
||||
parts: array(
|
||||
0: A
|
||||
)
|
||||
)
|
||||
)
|
||||
name: Identifier(
|
||||
name: length
|
||||
)
|
||||
args: array(
|
||||
)
|
||||
)
|
||||
)
|
||||
2: Stmt_Expression(
|
||||
expr: Expr_ArrayDimFetch(
|
||||
var: Expr_ConstFetch(
|
||||
name: Name(
|
||||
parts: array(
|
||||
0: A
|
||||
)
|
||||
)
|
||||
)
|
||||
dim: Scalar_LNumber(
|
||||
value: 0
|
||||
)
|
||||
)
|
||||
)
|
||||
3: Stmt_Expression(
|
||||
expr: Expr_ArrayDimFetch(
|
||||
var: Expr_ArrayDimFetch(
|
||||
var: Expr_ArrayDimFetch(
|
||||
var: Expr_ConstFetch(
|
||||
name: Name(
|
||||
parts: array(
|
||||
0: A
|
||||
)
|
||||
)
|
||||
)
|
||||
dim: Scalar_LNumber(
|
||||
value: 0
|
||||
)
|
||||
)
|
||||
dim: Scalar_LNumber(
|
||||
value: 1
|
||||
)
|
||||
)
|
||||
dim: Scalar_LNumber(
|
||||
value: 2
|
||||
)
|
||||
)
|
||||
)
|
||||
4: Stmt_Expression(
|
||||
expr: Expr_ArrayDimFetch(
|
||||
var: Expr_ConstFetch(
|
||||
name: Name(
|
||||
parts: array(
|
||||
0: A
|
||||
)
|
||||
)
|
||||
)
|
||||
dim: Scalar_LNumber(
|
||||
value: 0
|
||||
)
|
||||
)
|
||||
)
|
||||
5: Stmt_Expression(
|
||||
expr: Expr_ArrayDimFetch(
|
||||
var: Expr_ClassConstFetch(
|
||||
class: Name(
|
||||
parts: array(
|
||||
0: A
|
||||
)
|
||||
)
|
||||
name: Identifier(
|
||||
name: B
|
||||
)
|
||||
)
|
||||
dim: Scalar_LNumber(
|
||||
value: 0
|
||||
)
|
||||
)
|
||||
)
|
||||
6: Stmt_Expression(
|
||||
expr: Expr_ArrayDimFetch(
|
||||
var: Expr_ArrayDimFetch(
|
||||
var: Expr_ArrayDimFetch(
|
||||
var: Expr_ClassConstFetch(
|
||||
class: Name(
|
||||
parts: array(
|
||||
0: A
|
||||
)
|
||||
)
|
||||
name: Identifier(
|
||||
name: B
|
||||
)
|
||||
)
|
||||
dim: Scalar_LNumber(
|
||||
value: 0
|
||||
)
|
||||
)
|
||||
dim: Scalar_LNumber(
|
||||
value: 1
|
||||
)
|
||||
)
|
||||
dim: Scalar_LNumber(
|
||||
value: 2
|
||||
)
|
||||
)
|
||||
)
|
||||
7: Stmt_Expression(
|
||||
expr: Expr_ArrayDimFetch(
|
||||
var: Expr_ClassConstFetch(
|
||||
class: Name(
|
||||
parts: array(
|
||||
0: A
|
||||
)
|
||||
)
|
||||
name: Identifier(
|
||||
name: B
|
||||
)
|
||||
)
|
||||
dim: Scalar_LNumber(
|
||||
value: 0
|
||||
)
|
||||
)
|
||||
)
|
||||
8: Stmt_Expression(
|
||||
expr: Expr_PropertyFetch(
|
||||
var: Expr_ClassConstFetch(
|
||||
class: Name(
|
||||
parts: array(
|
||||
0: A
|
||||
)
|
||||
)
|
||||
name: Identifier(
|
||||
name: B
|
||||
)
|
||||
)
|
||||
name: Identifier(
|
||||
name: length
|
||||
)
|
||||
)
|
||||
)
|
||||
9: Stmt_Expression(
|
||||
expr: Expr_MethodCall(
|
||||
var: Expr_ClassConstFetch(
|
||||
class: Name(
|
||||
parts: array(
|
||||
0: A
|
||||
)
|
||||
)
|
||||
name: Identifier(
|
||||
name: B
|
||||
)
|
||||
)
|
||||
name: Identifier(
|
||||
name: length
|
||||
)
|
||||
args: array(
|
||||
)
|
||||
)
|
||||
)
|
||||
10: Stmt_Expression(
|
||||
expr: Expr_ClassConstFetch(
|
||||
class: Expr_ClassConstFetch(
|
||||
class: Name(
|
||||
parts: array(
|
||||
0: A
|
||||
)
|
||||
)
|
||||
name: Identifier(
|
||||
name: B
|
||||
)
|
||||
)
|
||||
name: Identifier(
|
||||
name: C
|
||||
)
|
||||
)
|
||||
)
|
||||
11: Stmt_Expression(
|
||||
expr: Expr_StaticPropertyFetch(
|
||||
class: Expr_ClassConstFetch(
|
||||
class: Name(
|
||||
parts: array(
|
||||
0: A
|
||||
)
|
||||
)
|
||||
name: Identifier(
|
||||
name: B
|
||||
)
|
||||
)
|
||||
name: VarLikeIdentifier(
|
||||
name: c
|
||||
)
|
||||
)
|
||||
)
|
||||
12: Stmt_Expression(
|
||||
expr: Expr_StaticCall(
|
||||
class: Expr_ClassConstFetch(
|
||||
class: Name(
|
||||
parts: array(
|
||||
0: A
|
||||
)
|
||||
)
|
||||
name: Identifier(
|
||||
name: B
|
||||
)
|
||||
)
|
||||
name: Identifier(
|
||||
name: c
|
||||
)
|
||||
args: array(
|
||||
)
|
||||
)
|
||||
)
|
||||
13: Stmt_Expression(
|
||||
expr: Expr_ArrayDimFetch(
|
||||
var: Scalar_MagicConst_Function(
|
||||
)
|
||||
dim: Scalar_LNumber(
|
||||
value: 0
|
||||
)
|
||||
)
|
||||
)
|
||||
14: Stmt_Expression(
|
||||
expr: Expr_PropertyFetch(
|
||||
var: Scalar_MagicConst_Function(
|
||||
)
|
||||
name: Identifier(
|
||||
name: length
|
||||
)
|
||||
)
|
||||
)
|
||||
15: Stmt_Expression(
|
||||
expr: Expr_MethodCall(
|
||||
var: Expr_ConstFetch(
|
||||
name: Name(
|
||||
parts: array(
|
||||
0: __FUNCIONT__
|
||||
)
|
||||
)
|
||||
)
|
||||
name: Identifier(
|
||||
name: length
|
||||
)
|
||||
args: array(
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
@ -2,24 +2,36 @@ Uniform variable syntax in PHP 7 (misc)
|
||||
-----
|
||||
<?php
|
||||
|
||||
A::A[0];
|
||||
A::A[0][1][2];
|
||||
"string"->length();
|
||||
"foo$bar"[0];
|
||||
"foo$bar"->length();
|
||||
(clone $obj)->b[0](1);
|
||||
[0, 1][0] = 1;
|
||||
-----
|
||||
!!php7
|
||||
array(
|
||||
0: Stmt_Expression(
|
||||
expr: Expr_MethodCall(
|
||||
var: Scalar_String(
|
||||
value: string
|
||||
)
|
||||
name: Identifier(
|
||||
name: length
|
||||
)
|
||||
args: array(
|
||||
)
|
||||
)
|
||||
)
|
||||
1: Stmt_Expression(
|
||||
expr: Expr_ArrayDimFetch(
|
||||
var: Expr_ClassConstFetch(
|
||||
class: Name(
|
||||
parts: array(
|
||||
0: A
|
||||
var: Scalar_Encapsed(
|
||||
parts: array(
|
||||
0: Scalar_EncapsedStringPart(
|
||||
value: foo
|
||||
)
|
||||
1: Expr_Variable(
|
||||
name: bar
|
||||
)
|
||||
)
|
||||
name: Identifier(
|
||||
name: A
|
||||
)
|
||||
)
|
||||
dim: Scalar_LNumber(
|
||||
@ -27,37 +39,17 @@ array(
|
||||
)
|
||||
)
|
||||
)
|
||||
1: Stmt_Expression(
|
||||
expr: Expr_ArrayDimFetch(
|
||||
var: Expr_ArrayDimFetch(
|
||||
var: Expr_ArrayDimFetch(
|
||||
var: Expr_ClassConstFetch(
|
||||
class: Name(
|
||||
parts: array(
|
||||
0: A
|
||||
)
|
||||
)
|
||||
name: Identifier(
|
||||
name: A
|
||||
)
|
||||
)
|
||||
dim: Scalar_LNumber(
|
||||
value: 0
|
||||
)
|
||||
)
|
||||
dim: Scalar_LNumber(
|
||||
value: 1
|
||||
)
|
||||
)
|
||||
dim: Scalar_LNumber(
|
||||
value: 2
|
||||
)
|
||||
)
|
||||
)
|
||||
2: Stmt_Expression(
|
||||
expr: Expr_MethodCall(
|
||||
var: Scalar_String(
|
||||
value: string
|
||||
var: Scalar_Encapsed(
|
||||
parts: array(
|
||||
0: Scalar_EncapsedStringPart(
|
||||
value: foo
|
||||
)
|
||||
1: Expr_Variable(
|
||||
name: bar
|
||||
)
|
||||
)
|
||||
)
|
||||
name: Identifier(
|
||||
name: length
|
||||
|
61
test/code/parser/expr/uvs/newInstanceofExpr.test
Normal file
61
test/code/parser/expr/uvs/newInstanceofExpr.test
Normal file
@ -0,0 +1,61 @@
|
||||
Arbitrary expressions in new and instanceof
|
||||
-----
|
||||
<?php
|
||||
|
||||
new ('Foo' . $bar);
|
||||
new ('Foo' . $bar)($arg);
|
||||
$obj instanceof ('Foo' . $bar);
|
||||
-----
|
||||
!!php7
|
||||
array(
|
||||
0: Stmt_Expression(
|
||||
expr: Expr_New(
|
||||
class: Expr_BinaryOp_Concat(
|
||||
left: Scalar_String(
|
||||
value: Foo
|
||||
)
|
||||
right: Expr_Variable(
|
||||
name: bar
|
||||
)
|
||||
)
|
||||
args: array(
|
||||
)
|
||||
)
|
||||
)
|
||||
1: Stmt_Expression(
|
||||
expr: Expr_New(
|
||||
class: Expr_BinaryOp_Concat(
|
||||
left: Scalar_String(
|
||||
value: Foo
|
||||
)
|
||||
right: Expr_Variable(
|
||||
name: bar
|
||||
)
|
||||
)
|
||||
args: array(
|
||||
0: Arg(
|
||||
value: Expr_Variable(
|
||||
name: arg
|
||||
)
|
||||
byRef: false
|
||||
unpack: false
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
2: Stmt_Expression(
|
||||
expr: Expr_Instanceof(
|
||||
expr: Expr_Variable(
|
||||
name: obj
|
||||
)
|
||||
class: Expr_BinaryOp_Concat(
|
||||
left: Scalar_String(
|
||||
value: Foo
|
||||
)
|
||||
right: Expr_Variable(
|
||||
name: bar
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
@ -10,4 +10,18 @@ array(
|
||||
0: /* Comment */
|
||||
)
|
||||
)
|
||||
)
|
||||
-----
|
||||
<?php
|
||||
{
|
||||
/* comment */
|
||||
}
|
||||
-----
|
||||
!!positions
|
||||
array(
|
||||
0: Stmt_Nop[3:0 - 3:17](
|
||||
comments: array(
|
||||
0: /* comment */
|
||||
)
|
||||
)
|
||||
)
|
Reference in New Issue
Block a user