Skip to content

Commit faf5397

Browse files
authored
Support the php 8.0 nullsafe operator(?->) (nikic#169)
1 parent c6cc7b1 commit faf5397

File tree

7 files changed

+74
-0
lines changed

7 files changed

+74
-0
lines changed

ast_data.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const zend_ast_kind ast_kinds[] = {
6363
ZEND_AST_CLASS_CONST_GROUP,
6464
ZEND_AST_DIM,
6565
ZEND_AST_PROP,
66+
ZEND_AST_NULLSAFE_PROP,
6667
ZEND_AST_STATIC_PROP,
6768
ZEND_AST_CALL,
6869
ZEND_AST_CLASS_CONST,
@@ -96,6 +97,7 @@ const zend_ast_kind ast_kinds[] = {
9697
ZEND_AST_MATCH_ARM,
9798
ZEND_AST_NAMED_ARG,
9899
ZEND_AST_METHOD_CALL,
100+
ZEND_AST_NULLSAFE_METHOD_CALL,
99101
ZEND_AST_STATIC_CALL,
100102
ZEND_AST_CONDITIONAL,
101103
ZEND_AST_TRY,
@@ -171,6 +173,7 @@ const char *ast_kind_to_name(zend_ast_kind kind) {
171173
case ZEND_AST_CLASS_CONST_GROUP: return "AST_CLASS_CONST_GROUP";
172174
case ZEND_AST_DIM: return "AST_DIM";
173175
case ZEND_AST_PROP: return "AST_PROP";
176+
case ZEND_AST_NULLSAFE_PROP: return "AST_NULLSAFE_PROP";
174177
case ZEND_AST_STATIC_PROP: return "AST_STATIC_PROP";
175178
case ZEND_AST_CALL: return "AST_CALL";
176179
case ZEND_AST_CLASS_CONST: return "AST_CLASS_CONST";
@@ -204,6 +207,7 @@ const char *ast_kind_to_name(zend_ast_kind kind) {
204207
case ZEND_AST_MATCH_ARM: return "AST_MATCH_ARM";
205208
case ZEND_AST_NAMED_ARG: return "AST_NAMED_ARG";
206209
case ZEND_AST_METHOD_CALL: return "AST_METHOD_CALL";
210+
case ZEND_AST_NULLSAFE_METHOD_CALL: return "AST_NULLSAFE_METHOD_CALL";
207211
case ZEND_AST_STATIC_CALL: return "AST_STATIC_CALL";
208212
case ZEND_AST_CONDITIONAL: return "AST_CONDITIONAL";
209213
case ZEND_AST_TRY: return "AST_TRY";
@@ -444,6 +448,12 @@ zend_string *ast_kind_child_name(zend_ast_kind kind, uint32_t child) {
444448
case 1: return AST_STR(str_prop);
445449
}
446450
return NULL;
451+
case ZEND_AST_NULLSAFE_PROP:
452+
switch (child) {
453+
case 0: return AST_STR(str_expr);
454+
case 1: return AST_STR(str_prop);
455+
}
456+
return NULL;
447457
case ZEND_AST_STATIC_PROP:
448458
switch (child) {
449459
case 0: return AST_STR(str_class);
@@ -646,6 +656,13 @@ zend_string *ast_kind_child_name(zend_ast_kind kind, uint32_t child) {
646656
case 2: return AST_STR(str_args);
647657
}
648658
return NULL;
659+
case ZEND_AST_NULLSAFE_METHOD_CALL:
660+
switch (child) {
661+
case 0: return AST_STR(str_expr);
662+
case 1: return AST_STR(str_method);
663+
case 2: return AST_STR(str_args);
664+
}
665+
return NULL;
649666
case ZEND_AST_STATIC_CALL:
650667
switch (child) {
651668
case 0: return AST_STR(str_class);
@@ -767,6 +784,7 @@ void ast_register_kind_constants(INIT_FUNC_ARGS) {
767784
REGISTER_NS_LONG_CONSTANT("ast", "AST_CLASS_CONST_GROUP", ZEND_AST_CLASS_CONST_GROUP, CONST_CS | CONST_PERSISTENT);
768785
REGISTER_NS_LONG_CONSTANT("ast", "AST_DIM", ZEND_AST_DIM, CONST_CS | CONST_PERSISTENT);
769786
REGISTER_NS_LONG_CONSTANT("ast", "AST_PROP", ZEND_AST_PROP, CONST_CS | CONST_PERSISTENT);
787+
REGISTER_NS_LONG_CONSTANT("ast", "AST_NULLSAFE_PROP", ZEND_AST_NULLSAFE_PROP, CONST_CS | CONST_PERSISTENT);
770788
REGISTER_NS_LONG_CONSTANT("ast", "AST_STATIC_PROP", ZEND_AST_STATIC_PROP, CONST_CS | CONST_PERSISTENT);
771789
REGISTER_NS_LONG_CONSTANT("ast", "AST_CALL", ZEND_AST_CALL, CONST_CS | CONST_PERSISTENT);
772790
REGISTER_NS_LONG_CONSTANT("ast", "AST_CLASS_CONST", ZEND_AST_CLASS_CONST, CONST_CS | CONST_PERSISTENT);
@@ -800,6 +818,7 @@ void ast_register_kind_constants(INIT_FUNC_ARGS) {
800818
REGISTER_NS_LONG_CONSTANT("ast", "AST_MATCH_ARM", ZEND_AST_MATCH_ARM, CONST_CS | CONST_PERSISTENT);
801819
REGISTER_NS_LONG_CONSTANT("ast", "AST_NAMED_ARG", ZEND_AST_NAMED_ARG, CONST_CS | CONST_PERSISTENT);
802820
REGISTER_NS_LONG_CONSTANT("ast", "AST_METHOD_CALL", ZEND_AST_METHOD_CALL, CONST_CS | CONST_PERSISTENT);
821+
REGISTER_NS_LONG_CONSTANT("ast", "AST_NULLSAFE_METHOD_CALL", ZEND_AST_NULLSAFE_METHOD_CALL, CONST_CS | CONST_PERSISTENT);
803822
REGISTER_NS_LONG_CONSTANT("ast", "AST_STATIC_CALL", ZEND_AST_STATIC_CALL, CONST_CS | CONST_PERSISTENT);
804823
REGISTER_NS_LONG_CONSTANT("ast", "AST_CONDITIONAL", ZEND_AST_CONDITIONAL, CONST_CS | CONST_PERSISTENT);
805824
REGISTER_NS_LONG_CONSTANT("ast", "AST_TRY", ZEND_AST_TRY, CONST_CS | CONST_PERSISTENT);

ast_stub.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
const AST_CLASS_CONST_GROUP = 546;
7070
const AST_DIM = 512;
7171
const AST_PROP = 513;
72+
const AST_NULLSAFE_PROP = 514;
7273
const AST_STATIC_PROP = 515;
7374
const AST_CALL = 516;
7475
const AST_CLASS_CONST = 517;
@@ -100,7 +101,9 @@
100101
const AST_ATTRIBUTE = 547;
101102
const AST_MATCH = 548;
102103
const AST_MATCH_ARM = 549;
104+
const AST_NAMED_ARG = 550;
103105
const AST_METHOD_CALL = 768;
106+
const AST_NULLSAFE_METHOD_CALL = 769;
104107
const AST_STATIC_CALL = 770;
105108
const AST_CONDITIONAL = 771;
106109
const AST_TRY = 772;

package.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
The new constants will have the same values as `ast\flags\MODIFIER_*` in PHP 8.0+, but different values in PHP 7
3535
(and these flags will never be set in php 7).
3636
- Support PHP 8.0's named arguments.
37+
- Support PHP 8.0's nullsafe operator (`?->`).
3738
</notes>
3839
<contents>
3940
<dir name="/">
@@ -103,6 +104,7 @@
103104
<file name="php74_type_hints.phpt" role="test" />
104105
<file name="php80_named_params.phpt" role="test" />
105106
<file name="php80_noncapturing_catch.phpt" role="test" />
107+
<file name="php80_nullsafe_operator.phpt" role="test" />
106108
<file name="php80_promotion.phpt" role="test" />
107109
<file name="php80_static_type.phpt" role="test" />
108110
<file name="php80_union_types.phpt" role="test" />

php_ast.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,15 @@ extern ast_str_globals str_globals;
6262
# define ZEND_AST_TYPE_UNION ((1 << (ZEND_AST_IS_LIST_SHIFT + 1)) - 2)
6363
# define ZEND_AST_ATTRIBUTE_LIST ((1 << (ZEND_AST_IS_LIST_SHIFT + 1)) - 3)
6464
# define ZEND_AST_MATCH_ARM_LIST ((1 << (ZEND_AST_IS_LIST_SHIFT + 1)) - 4)
65+
/* 2 child nodes */
6566
# define ZEND_AST_CLASS_CONST_GROUP 0x2fe
6667
# define ZEND_AST_ATTRIBUTE 0x2fd
6768
# define ZEND_AST_MATCH 0x2fc
6869
# define ZEND_AST_MATCH_ARM 0x2fb
6970
# define ZEND_AST_NAMED_ARG 0x2fa
71+
# define ZEND_AST_NULLSAFE_PROP 0x2f9
72+
/* 3 child nodes */
73+
# define ZEND_AST_NULLSAFE_METHOD_CALL 0x3ff
7074
// NOTE: The first hex digit is the number of child nodes a given kind has
7175
#endif
7276

scripts/generate_ast_data.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
'ZEND_AST_CLASS_CONST_GROUP' => ['const', 'attributes'],
101101
'ZEND_AST_DIM' => ['expr', 'dim'],
102102
'ZEND_AST_PROP' => ['expr', 'prop'],
103+
'ZEND_AST_NULLSAFE_PROP' => ['expr', 'prop'],
103104
'ZEND_AST_STATIC_PROP' => ['class', 'prop'],
104105
'ZEND_AST_CALL' => ['expr', 'args'],
105106
'ZEND_AST_CLASS_CONST' => ['class', 'const'],
@@ -136,6 +137,7 @@
136137

137138
/* 3 child nodes */
138139
'ZEND_AST_METHOD_CALL' => ['expr', 'method', 'args'],
140+
'ZEND_AST_NULLSAFE_METHOD_CALL' => ['expr', 'method', 'args'],
139141
'ZEND_AST_STATIC_CALL' => ['class', 'method', 'args'],
140142
'ZEND_AST_CONDITIONAL' => ['cond', 'true', 'false'],
141143

tests/metadata.phpt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ AST_CLASS_NAME: []
8888
AST_CLASS_CONST_GROUP: (combinable) [MODIFIER_PUBLIC, MODIFIER_PROTECTED, MODIFIER_PRIVATE]
8989
AST_DIM: (combinable) [DIM_ALTERNATIVE_SYNTAX]
9090
AST_PROP: []
91+
AST_NULLSAFE_PROP: []
9192
AST_STATIC_PROP: []
9293
AST_CALL: []
9394
AST_CLASS_CONST: []
@@ -121,6 +122,7 @@ AST_MATCH: []
121122
AST_MATCH_ARM: []
122123
AST_NAMED_ARG: []
123124
AST_METHOD_CALL: []
125+
AST_NULLSAFE_METHOD_CALL: []
124126
AST_STATIC_CALL: []
125127
AST_CONDITIONAL: (combinable) [PARENTHESIZED_CONDITIONAL]
126128
AST_TRY: []

tests/php80_nullsafe_operator.phpt

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
--TEST--
2+
Nullsafe operator in PHP 8.0
3+
--SKIPIF--
4+
<?php if (PHP_VERSION_ID < 80000) die('skip PHP >= 8.0 only'); ?>
5+
--FILE--
6+
<?php
7+
8+
require __DIR__ . '/../util.php';
9+
10+
$code = <<<'PHP'
11+
<?php
12+
$foo?->bar(2);
13+
$a = $b?->c;
14+
$a = new $b?->c;
15+
PHP;
16+
17+
$node = ast\parse_code($code, $version=70);
18+
echo ast_dump($node), "\n";
19+
--EXPECTF--
20+
AST_STMT_LIST
21+
0: AST_NULLSAFE_METHOD_CALL
22+
expr: AST_VAR
23+
name: "foo"
24+
method: "bar"
25+
args: AST_ARG_LIST
26+
0: 2
27+
1: AST_ASSIGN
28+
var: AST_VAR
29+
name: "a"
30+
expr: AST_NULLSAFE_PROP
31+
expr: AST_VAR
32+
name: "b"
33+
prop: "c"
34+
2: AST_ASSIGN
35+
var: AST_VAR
36+
name: "a"
37+
expr: AST_NEW
38+
class: AST_NULLSAFE_PROP
39+
expr: AST_VAR
40+
name: "b"
41+
prop: "c"
42+
args: AST_ARG_LIST

0 commit comments

Comments
 (0)