Skip to content

Commit e722c78

Browse files
committed
Support goto, labels, add some missing flags to generated ast node
fix bug analyzing namespaces with properties. Emulate edge cases of php-ast for property visibility flags. Emulate the way php-ast parses a `list($x,) =` expression. Fix RETURNS_REF flag for global functions Copy test files from php-src which revealed new bugs
1 parent e7dab48 commit e722c78

File tree

9 files changed

+181
-6
lines changed

9 files changed

+181
-6
lines changed

src/ASTConverter/ASTConverter.php

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,16 @@ private static final function _phpparser_node_to_ast_node($n) {
200200
private static function _init_handle_map() : array {
201201
$closures = [
202202
'PhpParser\Node\Arg' => function(PhpParser\Node\Arg $n, int $startLine) {
203-
return self::_phpparser_node_to_ast_node($n->value);
203+
$result = self::_phpparser_node_to_ast_node($n->value);
204+
if ($n->unpack) {
205+
return new ast\Node(
206+
\ast\AST_UNPACK,
207+
0,
208+
['expr' => $result],
209+
$startLine
210+
);
211+
}
212+
return $result;
204213
},
205214
'PhpParser\Node\Expr\Array_' => function(PhpParser\Node\Expr\Array_ $n, int $startLine) : ast\Node {
206215
return self::_phpparser_array_to_ast_array($n, $startLine);
@@ -782,6 +791,14 @@ private static function _init_handle_map() : array {
782791
}
783792
return \count($globalNodes) === 1 ? $globalNodes[0] : $globalNodes;
784793
},
794+
'PhpParser\Node\Stmt\Goto_' => function(PhpParser\Node\Stmt\Goto_ $n, int $startLine) : ast\Node {
795+
return new ast\Node(
796+
\ast\AST_GOTO,
797+
0,
798+
['label' => $n->name],
799+
$startLine
800+
);
801+
},
785802
'PhpParser\Node\Stmt\HaltCompiler' => function(PhpParser\Node\Stmt\HaltCompiler $n, int $startLine) : ast\Node {
786803
return new ast\Node(
787804
\ast\AST_HALT_COMPILER,
@@ -810,6 +827,14 @@ private static function _init_handle_map() : array {
810827
self::_extract_phpdoc_comment($n->getAttribute('comments'))
811828
);
812829
},
830+
'PhpParser\Node\Stmt\Label' => function(PhpParser\Node\Stmt\Label $n, int $startLine) : ast\Node {
831+
return new ast\Node(
832+
\ast\AST_LABEL,
833+
0,
834+
['name' => $n->name],
835+
$startLine
836+
);
837+
},
813838
'PhpParser\Node\Stmt\For_' => function(PhpParser\Node\Stmt\For_ $n, int $startLine) : ast\Node {
814839
return new ast\Node(
815840
ast\AST_FOR,
@@ -842,8 +867,12 @@ private static function _init_handle_map() : array {
842867
$endLineOfWrapper = ($n->getAttribute('endLine') ?: $n->getAttribute('startLine') ?: null);
843868
$withinNamespace = $n->stmts ?? [];
844869
if (count($withinNamespace) > 0) {
845-
$lastStmt = end($withinNamespace);
846-
$endLineOfContents = $lastStmt->getAttribute('endLine') ?: $lastStmt->getAttribute('startLine') ?: $endLineOfWrapper;
870+
foreach ($withinNamespace as $s) {
871+
$endLineOfContents = $s->getAttribute('endLine') ?: $s->getAttribute('startLine');
872+
if ($endLineOfContents && $endLineOfContents != $endLineOfWrapper) {
873+
break;
874+
}
875+
}
847876
}
848877
}
849878

@@ -1388,6 +1417,9 @@ private static function _ast_decl_function(
13881417
?string $docComment
13891418
) : ast\Node {
13901419
$flags = 0;
1420+
if ($byRef) {
1421+
$flags |= \ast\flags\RETURNS_REF;
1422+
}
13911423
/*
13921424
if (PHP_VERSION_ID >= 70100 && self::_function_body_is_generator($stmts)) {
13931425
$flags |= 0x800000;
@@ -1697,15 +1729,17 @@ private static function _phpparser_visibility_to_ast_visibility(int $visibility,
16971729
return $ast_visibility;
16981730
}
16991731

1700-
private static function _phpparser_property_to_ast_node(PhpParser\Node $n, int $startLine) : ast\Node {
1701-
assert($n instanceof \PHPParser\Node\Stmt\Property);
1732+
private static function _phpparser_property_to_ast_node(PHPParser\Node\Stmt\Property $n, int $startLine) : ast\Node {
17021733

17031734
$propElems = [];
17041735
$docComment = self::_extract_phpdoc_comment($n->getAttribute('comments'));
17051736
foreach ($n->props as $i => $prop) {
17061737
$propElems[] = self::_phpparser_propelem_to_ast_propelem($prop, $i === 0 ? $docComment : null);
17071738
}
1708-
$flags = self::_phpparser_visibility_to_ast_visibility($n->flags);
1739+
$flags = self::_phpparser_visibility_to_ast_visibility($n->flags, false);
1740+
if ($flags === 0) {
1741+
$flags = ast\flags\MODIFIER_PUBLIC;
1742+
}
17091743

17101744
return new ast\Node(ast\AST_PROP_DECL, $flags, $propElems, $propElems[0]->lineno ?: $startLine);
17111745
}
@@ -1824,6 +1858,11 @@ private static function _phpparser_list_to_ast_list(PhpParser\Node\Expr\List_ $n
18241858
], $item->getAttribute('startLine'));
18251859
}
18261860
}
1861+
1862+
// convert list($x,) to list($x), list(,) to list(), etc.
1863+
if (\count($astItems) > 0 && end($astItems) === null) {
1864+
array_pop($astItems);
1865+
}
18271866
return new ast\Node(ast\AST_ARRAY, ast\flags\ARRAY_SYNTAX_LIST, $astItems, $startLine);
18281867
}
18291868

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
class test
3+
{
4+
protected $_id;
5+
static $instances;
6+
7+
public function __construct($id) {
8+
$this->_id = $id;
9+
self::$instances[$this->_id] = $this;
10+
}
11+
12+
function __destruct() {
13+
unset(self::$instances[$this->_id]);
14+
}
15+
}
16+
$test = new test(2);
17+
$test = new test(1);
18+
$test = new test(2);
19+
$test = new test(3);
20+
echo "ok\n";
21+
?>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
namespace foobar;
3+
4+
class foo {
5+
public function bar(self $a) { }
6+
}
7+
8+
$foo = new foo;
9+
$foo->bar($foo); // Ok!
10+
$foo->bar(new \stdclass); // Error, ok!
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
function crash() {
4+
sin(...[0]);
5+
throw new \Exception();
6+
yield;
7+
}
8+
9+
iterator_to_array(crash());
10+
11+
?>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
function test() {}
4+
$iterator = new LimitIterator(
5+
new InfiniteIterator(new ArrayIterator([42])),
6+
0, 17000
7+
);
8+
test(...$iterator);
9+
10+
?>
11+
===DONE===
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
error_reporting(E_ALL);
4+
5+
$a = array();
6+
7+
function &a() {
8+
return $GLOBALS['a'];
9+
}
10+
11+
var_dump($h =& a());
12+
$h[] = 1;
13+
var_dump(a()[0]);
14+
15+
$h[] = array($h);
16+
var_dump(a()[1][0][0]);
17+
18+
?>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
$array = [['a', 'b'], 'c', 'd'];
4+
5+
foreach($array as $key => list()) {
6+
}
7+
8+
?>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
namespace TestNamespace
3+
{
4+
class TestClass
5+
{
6+
public static function staticMethod()
7+
{
8+
echo "Static method called!\n";
9+
}
10+
11+
public static function staticMethodWithArgs($arg1, $arg2, $arg3)
12+
{
13+
printf("Static method called with args: %s, %s, %s\n", $arg1, $arg2, $arg3);
14+
}
15+
}
16+
}
17+
18+
namespace CallNamespace
19+
{
20+
// Test basic call using Class::method syntax.
21+
$callback = 'TestNamespace\TestClass::staticMethod';
22+
$callback();
23+
24+
// Case should not matter.
25+
$callback = 'testnamespace\testclass::staticmethod';
26+
$callback();
27+
28+
$args = ['arg1', 'arg2', 'arg3'];
29+
$callback = 'TestNamespace\TestClass::staticMethodWithArgs';
30+
31+
// Test call with args.
32+
$callback($args[0], $args[1], $args[2]);
33+
34+
// Test call with splat operator.
35+
$callback(...$args);
36+
}
37+
?>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
goto a;
3+
e: return;
4+
try {
5+
a: print 1;
6+
goto b;
7+
try {
8+
b: print 2;
9+
goto c;
10+
}
11+
catch(Exception $e) {
12+
c: print 3;
13+
goto d;
14+
}
15+
}
16+
catch(Exception $e) {
17+
d: print 4;
18+
goto e;
19+
}
20+
?>

0 commit comments

Comments
 (0)