Skip to content

Commit fb1be83

Browse files
TysonAndrenikic
authored andcommitted
Add AST_CLASS_NAME in AST version 70 (nikic#111)
Add AST_CLASS_NAME in AST version 70 Fixes nikic#109 Tests pass with a local PHP 7.4 installation
1 parent d4c370d commit fb1be83

11 files changed

+165
-8
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,7 @@ AST_CAST: expr
361361
AST_CATCH: class, var, stmts
362362
AST_CLASS: name, docComment, extends, implements, stmts
363363
AST_CLASS_CONST: class, const
364+
AST_CLASS_NAME: class // version 70+
364365
AST_CLONE: expr
365366
AST_CLOSURE: name, docComment, params, uses, stmts, returnType
366367
AST_CLOSURE_VAR: name
@@ -472,6 +473,7 @@ are listed.
472473
The property visibility modifiers are now part of `AST_PROP_GROUP` instead of `AST_PROP_DECL`.
473474

474475
Note that property group type information is only available with AST versions 70+.
476+
* `AST_CLASS_NAME` is created instead of `AST_CLASS_CONST` for `SomeClass::class`.
475477

476478
### 60 (current)
477479

ast.c

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -312,9 +312,7 @@ static inline zend_bool ast_kind_uses_attr(zend_ast_kind kind) {
312312
|| kind == ZEND_AST_UNARY_OP || kind == ZEND_AST_BINARY_OP || kind == ZEND_AST_ASSIGN_OP
313313
|| kind == ZEND_AST_CAST || kind == ZEND_AST_MAGIC_CONST || kind == ZEND_AST_ARRAY_ELEM
314314
|| kind == ZEND_AST_INCLUDE_OR_EVAL || kind == ZEND_AST_USE || kind == ZEND_AST_PROP_DECL
315-
#if PHP_VERSION_ID >= 70400
316315
|| kind == ZEND_AST_PROP_GROUP
317-
#endif
318316
|| kind == ZEND_AST_GROUP_USE || kind == ZEND_AST_USE_ELEM
319317
|| kind == AST_NAME || kind == AST_CLOSURE_VAR || kind == ZEND_AST_CLASS_CONST_DECL
320318
|| kind == ZEND_AST_ARRAY;
@@ -340,9 +338,7 @@ static inline zend_bool ast_is_name(zend_ast *ast, zend_ast *parent, uint32_t i)
340338
|| parent->kind == ZEND_AST_CALL || parent->kind == ZEND_AST_CONST
341339
|| parent->kind == ZEND_AST_NEW || parent->kind == ZEND_AST_STATIC_CALL
342340
|| parent->kind == ZEND_AST_CLASS_CONST || parent->kind == ZEND_AST_STATIC_PROP
343-
#if PHP_VERSION_ID >= 70400
344-
|| parent->kind == ZEND_AST_PROP_GROUP
345-
#endif
341+
|| parent->kind == ZEND_AST_PROP_GROUP || parent->kind == ZEND_AST_CLASS_NAME
346342
;
347343
}
348344

@@ -724,6 +720,46 @@ static void ast_to_zval(zval *zv, zend_ast *ast, ast_state_info_t *state) {
724720
ast->kind = ZEND_AST_ASSIGN_OP;
725721
ast->attr = AST_BINARY_COALESCE;
726722
break;
723+
case ZEND_AST_CLASS_NAME:
724+
if (state->version < 70) {
725+
zval name_zval;
726+
ast_to_zval(&name_zval, ast->child[0], state);
727+
zval class_name_zval;
728+
ast_create_virtual_node_ex(
729+
&class_name_zval, AST_NAME, ast->child[0]->attr, zend_ast_get_lineno(ast), state, 1, &name_zval);
730+
zval const_zval;
731+
ZVAL_STR_COPY(&const_zval, AST_STR(str_class));
732+
ast_create_virtual_node_ex(
733+
zv, ZEND_AST_CLASS_CONST, 0, zend_ast_get_lineno(ast), state, 2, &class_name_zval, &const_zval);
734+
return;
735+
}
736+
break;
737+
#else
738+
case ZEND_AST_CLASS_CONST:
739+
if (state->version >= 70) {
740+
// Convert to an AST_CLASS_NAME instead. This is the opposite of the work done in the ZEND_AST_CLASS_NAME case.
741+
zend_ast *const_name_ast = ast->child[1];
742+
zend_string *str = zend_ast_get_str(const_name_ast);
743+
if (zend_string_equals_ci(AST_STR(str_class), str)) {
744+
zend_ast *class_name_ast = ast->child[0];
745+
zval class_name_zval;
746+
if (class_name_ast->kind == ZEND_AST_ZVAL) {
747+
// e.g. Foo::class
748+
zval class_name_raw_zval;
749+
ZVAL_COPY(&class_name_raw_zval, zend_ast_get_zval(class_name_ast));
750+
ast_create_virtual_node_ex(
751+
&class_name_zval, AST_NAME, class_name_ast->attr, zend_ast_get_lineno(class_name_ast), state, 1, &class_name_raw_zval);
752+
} else {
753+
// e.g. []::class (not a parse error, but a runtime error)
754+
ast_to_zval(&class_name_zval, class_name_ast, state);
755+
}
756+
757+
ast_create_virtual_node_ex(
758+
zv, ZEND_AST_CLASS_NAME, 0, zend_ast_get_lineno(ast), state, 1, &class_name_zval);
759+
return;
760+
}
761+
}
762+
break;
727763
#endif
728764
}
729765

ast_data.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ const zend_ast_kind ast_kinds[] = {
5555
ZEND_AST_GOTO,
5656
ZEND_AST_BREAK,
5757
ZEND_AST_CONTINUE,
58+
ZEND_AST_CLASS_NAME,
5859
ZEND_AST_DIM,
5960
ZEND_AST_PROP,
6061
ZEND_AST_STATIC_PROP,
@@ -153,6 +154,7 @@ const char *ast_kind_to_name(zend_ast_kind kind) {
153154
case ZEND_AST_GOTO: return "AST_GOTO";
154155
case ZEND_AST_BREAK: return "AST_BREAK";
155156
case ZEND_AST_CONTINUE: return "AST_CONTINUE";
157+
case ZEND_AST_CLASS_NAME: return "AST_CLASS_NAME";
156158
case ZEND_AST_DIM: return "AST_DIM";
157159
case ZEND_AST_PROP: return "AST_PROP";
158160
case ZEND_AST_STATIC_PROP: return "AST_STATIC_PROP";
@@ -388,6 +390,11 @@ zend_string *ast_kind_child_name(zend_ast_kind kind, uint32_t child) {
388390
case 0: return AST_STR(str_depth);
389391
}
390392
return NULL;
393+
case ZEND_AST_CLASS_NAME:
394+
switch (child) {
395+
case 0: return AST_STR(str_class);
396+
}
397+
return NULL;
391398
case ZEND_AST_DIM:
392399
switch (child) {
393400
case 0: return AST_STR(str_expr);
@@ -688,6 +695,7 @@ void ast_register_kind_constants(INIT_FUNC_ARGS) {
688695
REGISTER_NS_LONG_CONSTANT("ast", "AST_GOTO", ZEND_AST_GOTO, CONST_CS | CONST_PERSISTENT);
689696
REGISTER_NS_LONG_CONSTANT("ast", "AST_BREAK", ZEND_AST_BREAK, CONST_CS | CONST_PERSISTENT);
690697
REGISTER_NS_LONG_CONSTANT("ast", "AST_CONTINUE", ZEND_AST_CONTINUE, CONST_CS | CONST_PERSISTENT);
698+
REGISTER_NS_LONG_CONSTANT("ast", "AST_CLASS_NAME", ZEND_AST_CLASS_NAME, CONST_CS | CONST_PERSISTENT);
691699
REGISTER_NS_LONG_CONSTANT("ast", "AST_DIM", ZEND_AST_DIM, CONST_CS | CONST_PERSISTENT);
692700
REGISTER_NS_LONG_CONSTANT("ast", "AST_PROP", ZEND_AST_PROP, CONST_CS | CONST_PERSISTENT);
693701
REGISTER_NS_LONG_CONSTANT("ast", "AST_STATIC_PROP", ZEND_AST_STATIC_PROP, CONST_CS | CONST_PERSISTENT);

ast_str_defs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@
2323
X(offset) \
2424
X(label) \
2525
X(depth) \
26+
X(class) \
2627
X(dim) \
2728
X(prop) \
28-
X(class) \
2929
X(args) \
3030
X(const) \
3131
X(left) \

package.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@
2424
<license uri="https://github.com/nikic/php-ast/blob/master/LICENSE">BSD-3-Clause</license>
2525
<notes>
2626
- Fix a segfault in PHP 7.4-dev after typed properties support was added.
27-
- Add AST version 70 (experimental): This adds AST_PROP_GROUP with type information for property groups.
2827
- Support BINARY_COALESCE as a flag of AST_ASSIGN_OP for the `??=` operator in PHP 7.4-dev.
28+
- Add AST version 70 (experimental):
29+
- Version 70 adds AST_PROP_GROUP with type information for property groups.
30+
- Version 70 adds AST_CLASS_CONST for `Foo::class`
2931
</notes>
3032
<contents>
3133
<dir name="/">

php_ast.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ extern ast_str_globals str_globals;
5151

5252
// 544 is already taken by ZEND_AST_GROUP_USE
5353
#if PHP_VERSION_ID < 70400
54+
// NOTE: The first hex digit is the number of child nodes a given kind has
55+
# define ZEND_AST_CLASS_NAME 0x1ff
5456
# define ZEND_AST_PROP_GROUP 0x2ff
5557
#endif
5658

scripts/generate_ast_data.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
'ZEND_AST_GOTO' => ['label'],
9494
'ZEND_AST_BREAK' => ['depth'],
9595
'ZEND_AST_CONTINUE' => ['depth'],
96+
'ZEND_AST_CLASS_NAME' => ['class'],
9697

9798
/* 2 child nodes */
9899
'ZEND_AST_DIM' => ['expr', 'dim'],

tests/class_name_version_50.phpt

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
--TEST--
2+
Class properties in AST version 50
3+
--FILE--
4+
<?php
5+
6+
require __DIR__ . '/../util.php';
7+
8+
$code = <<<'PHP'
9+
<?php
10+
namespace Foo;
11+
echo Bar::class;
12+
echo namespace\Bat::class;
13+
echo \Baz::class;
14+
PHP;
15+
16+
$node = ast\parse_code($code, $version=50);
17+
echo ast_dump($node), "\n";
18+
?>
19+
--EXPECTF--
20+
AST_STMT_LIST
21+
0: AST_NAMESPACE
22+
name: "Foo"
23+
stmts: null
24+
1: AST_ECHO
25+
expr: AST_CLASS_CONST
26+
class: AST_NAME
27+
flags: NAME_NOT_FQ (1)
28+
name: "Bar"
29+
const: "class"
30+
2: AST_ECHO
31+
expr: AST_CLASS_CONST
32+
class: AST_NAME
33+
flags: NAME_RELATIVE (2)
34+
name: "Bat"
35+
const: "class"
36+
3: AST_ECHO
37+
expr: AST_CLASS_CONST
38+
class: AST_NAME
39+
flags: NAME_FQ (0)
40+
name: "Baz"
41+
const: "class"

tests/class_name_version_70.phpt

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
--TEST--
2+
Class properties in AST version 70
3+
--FILE--
4+
<?php
5+
6+
require __DIR__ . '/../util.php';
7+
8+
$code = <<<'PHP'
9+
<?php
10+
namespace Foo;
11+
echo Bar::class,
12+
namespace\Bat::class,
13+
Bat::class_, // this is a regular class constant
14+
\Baz::CLASS;
15+
echo []::class; // this is a runtime error, not a syntax error
16+
echo 'foo'::class; // this is valid but rare
17+
echo (new \stdClass())::class; // this is a runtime error, not a syntax error
18+
PHP;
19+
20+
$node = ast\parse_code($code, $version=70);
21+
echo ast_dump($node), "\n";
22+
?>
23+
--EXPECTF--
24+
AST_STMT_LIST
25+
0: AST_NAMESPACE
26+
name: "Foo"
27+
stmts: null
28+
1: AST_ECHO
29+
expr: AST_CLASS_NAME
30+
class: AST_NAME
31+
flags: NAME_NOT_FQ (%d)
32+
name: "Bar"
33+
2: AST_ECHO
34+
expr: AST_CLASS_NAME
35+
class: AST_NAME
36+
flags: NAME_RELATIVE (%d)
37+
name: "Bat"
38+
3: AST_ECHO
39+
expr: AST_CLASS_CONST
40+
class: AST_NAME
41+
flags: NAME_NOT_FQ (%d)
42+
name: "Bat"
43+
const: "class_"
44+
4: AST_ECHO
45+
expr: AST_CLASS_NAME
46+
class: AST_NAME
47+
flags: NAME_FQ (%d)
48+
name: "Baz"
49+
5: AST_ECHO
50+
expr: AST_CLASS_NAME
51+
class: AST_ARRAY
52+
flags: %s
53+
6: AST_ECHO
54+
expr: AST_CLASS_NAME
55+
class: AST_NAME
56+
flags: NAME_FQ (%d)
57+
name: "foo"
58+
7: AST_ECHO
59+
expr: AST_CLASS_NAME
60+
class: AST_NEW
61+
class: AST_NAME
62+
flags: NAME_FQ (0)
63+
name: "stdClass"
64+
args: AST_ARG_LIST

tests/metadata.phpt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ AST_THROW: []
7272
AST_GOTO: []
7373
AST_BREAK: []
7474
AST_CONTINUE: []
75+
AST_CLASS_NAME: []
7576
AST_DIM: []
7677
AST_PROP: []
7778
AST_STATIC_PROP: []

0 commit comments

Comments
 (0)