Skip to content

Commit ffdd968

Browse files
committed
Convert between AST_CLONE and AST_CALL
1 parent 1d59f6e commit ffdd968

File tree

7 files changed

+174
-5
lines changed

7 files changed

+174
-5
lines changed

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ AST_CLASS: name, docComment, extends, implements, stmts, (for enu
394394
AST_CLASS_CONST: class, const
395395
AST_CLASS_CONST_GROUP class, attributes, type // version 80+
396396
AST_CLASS_NAME: class // version 70+
397-
AST_CLONE: expr
397+
AST_CLONE: expr // version <120
398398
AST_CLOSURE: name, docComment, params, uses, stmts, returnType, attributes // name removed in version 110
399399
AST_CLOSURE_VAR: name
400400
AST_CONDITIONAL: cond, true, false
@@ -511,6 +511,12 @@ function accepts a boolean argument that determines whether deprecated versions
511511
In the following the changes in the respective AST versions, as well as their current support state,
512512
are listed.
513513

514+
### 120 (experimental)
515+
516+
Supported since 1.1.3 (TBD).
517+
518+
* `clone $expr` is now represented like a `clone($expr)` function call (using `AST_CALL`).
519+
514520
### 110 (current)
515521

516522
Supported since 1.1.2 (2024-08-10).

ast.c

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,49 @@ static void ast_to_zval(zval *zv, zend_ast *ast, ast_state_info_t *state) {
10811081
ast->attr = IS_VOID;
10821082
break;
10831083
#endif
1084+
case ZEND_AST_CALL:
1085+
if (state->version < 120) {
1086+
// Convert clone($expr) call to ZEND_AST_CLONE node.
1087+
zend_ast *name_ast = ast->child[0];
1088+
if (name_ast->kind != ZEND_AST_ZVAL || name_ast->attr != ZEND_NAME_FQ ||
1089+
Z_TYPE_P(zend_ast_get_zval(name_ast)) != IS_STRING ||
1090+
ast->child[1]->kind != ZEND_AST_ARG_LIST) {
1091+
break;
1092+
}
1093+
1094+
zend_ast_list *args = zend_ast_get_list(ast->child[1]);
1095+
if (args->children != 1) {
1096+
break;
1097+
}
1098+
1099+
zend_ast *arg = args->child[0];
1100+
if (arg->kind == ZEND_AST_NAMED_ARG || arg->kind == ZEND_AST_UNPACK) {
1101+
break;
1102+
}
1103+
1104+
zend_string *name = zend_ast_get_str(name_ast);
1105+
if (zend_string_equals_literal_ci(name, "clone")) {
1106+
ast_create_virtual_node(zv, ZEND_AST_CLONE, 0, arg, state);
1107+
return;
1108+
}
1109+
}
1110+
break;
1111+
case ZEND_AST_CLONE:
1112+
if (state->version >= 120) {
1113+
// Convert to call node.
1114+
uint32_t lineno = zend_ast_get_lineno(ast);
1115+
zval name_str_zv, name_node_zv, args_zv, arg_zv;
1116+
ZVAL_STR(&name_str_zv, AST_STR(str_clone));
1117+
ast_to_zval(&arg_zv, ast->child[0], state);
1118+
ast_create_virtual_node_ex(
1119+
&name_node_zv, AST_NAME, ZEND_NAME_FQ, lineno, state, 1, &name_str_zv);
1120+
ast_create_virtual_node_ex(
1121+
&args_zv, ZEND_AST_ARG_LIST, 0, lineno, state, 1, &arg_zv);
1122+
ast_create_virtual_node_ex(
1123+
zv, ZEND_AST_CALL, 0, lineno, state, 2, &name_node_zv, &args_zv);
1124+
return;
1125+
}
1126+
break;
10841127
}
10851128

10861129
object_init_ex(zv, ast_node_ce);
@@ -1171,7 +1214,7 @@ static void ast_to_zval(zval *zv, zend_ast *ast, ast_state_info_t *state) {
11711214
#endif
11721215
}
11731216

1174-
static const zend_long versions[] = {50, 60, 70, 80, 85, 90, 100, 110};
1217+
static const zend_long versions[] = {50, 60, 70, 80, 85, 90, 100, 110, 120};
11751218
static const size_t versions_count = sizeof(versions)/sizeof(versions[0]);
11761219

11771220
static inline zend_bool ast_version_deprecated(zend_long version) {

ast_str_defs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
X(endLineno, "endLineno") \
1212
X(__declId, "__declId") \
1313
X(flagsCombinable, "flagsCombinable") \
14+
X(clone, "clone") \
1415
X(type, "type") \
1516
X(params, "params") \
1617
X(uses, "uses") \

scripts/generate_ast_data.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ function get_possible_strings(array $spec) {
278278
$strings = array_fill_keys([
279279
'kind', 'flags', 'lineno', 'children',
280280
'name', 'docComment', 'endLineno', '__declId',
281-
'flagsCombinable',
281+
'flagsCombinable', 'clone',
282282
], true);
283283

284284
foreach ($spec as $kind => $children) {

tests/clone.phpt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--TEST--
2+
Simple clone
3+
--FILE--
4+
<?php
5+
6+
require __DIR__ . '/../util.php';
7+
8+
$code = <<<'PHP'
9+
<?php
10+
clone $foo;
11+
PHP;
12+
13+
$node = ast\parse_code($code, $version=110);
14+
echo ast_dump($node), "\n";
15+
$node = ast\parse_code($code, $version=120);
16+
echo ast_dump($node), "\n";
17+
--EXPECT--
18+
AST_STMT_LIST
19+
0: AST_CLONE
20+
expr: AST_VAR
21+
name: "foo"
22+
AST_STMT_LIST
23+
0: AST_CALL
24+
expr: AST_NAME
25+
flags: NAME_FQ (0)
26+
name: "clone"
27+
args: AST_ARG_LIST
28+
0: AST_VAR
29+
name: "foo"

tests/get_supported_versions.phpt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ var_dump(ast\get_supported_versions(true));
88

99
?>
1010
--EXPECT--
11-
array(8) {
11+
array(9) {
1212
[0]=>
1313
int(50)
1414
[1]=>
@@ -25,8 +25,10 @@ array(8) {
2525
int(100)
2626
[7]=>
2727
int(110)
28+
[8]=>
29+
int(120)
2830
}
29-
array(6) {
31+
array(7) {
3032
[0]=>
3133
int(70)
3234
[1]=>
@@ -39,4 +41,6 @@ array(6) {
3941
int(100)
4042
[5]=>
4143
int(110)
44+
[6]=>
45+
int(120)
4246
}

tests/php85_clone.phpt

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
--TEST--
2+
Complex clone
3+
--SKIPIF--
4+
<?php if (PHP_VERSION_ID < 80500) die('skip PHP >= 8.5 only'); ?>
5+
--FILE--
6+
<?php
7+
8+
require __DIR__ . '/../util.php';
9+
10+
$code = <<<'PHP'
11+
<?php
12+
clone($foo, $bar);
13+
clone(a: $foo);
14+
clone(...$foo);
15+
clone(...);
16+
PHP;
17+
18+
$node = ast\parse_code($code, $version=110);
19+
echo ast_dump($node), "\n";
20+
$node = ast\parse_code($code, $version=120);
21+
echo ast_dump($node), "\n";
22+
--EXPECT--
23+
AST_STMT_LIST
24+
0: AST_CALL
25+
expr: AST_NAME
26+
flags: NAME_FQ (0)
27+
name: "clone"
28+
args: AST_ARG_LIST
29+
0: AST_VAR
30+
name: "foo"
31+
1: AST_VAR
32+
name: "bar"
33+
1: AST_CALL
34+
expr: AST_NAME
35+
flags: NAME_FQ (0)
36+
name: "clone"
37+
args: AST_ARG_LIST
38+
0: AST_NAMED_ARG
39+
name: "a"
40+
expr: AST_VAR
41+
name: "foo"
42+
2: AST_CALL
43+
expr: AST_NAME
44+
flags: NAME_FQ (0)
45+
name: "clone"
46+
args: AST_ARG_LIST
47+
0: AST_UNPACK
48+
expr: AST_VAR
49+
name: "foo"
50+
3: AST_CALL
51+
expr: AST_NAME
52+
flags: NAME_FQ (0)
53+
name: "clone"
54+
args: AST_CALLABLE_CONVERT
55+
AST_STMT_LIST
56+
0: AST_CALL
57+
expr: AST_NAME
58+
flags: NAME_FQ (0)
59+
name: "clone"
60+
args: AST_ARG_LIST
61+
0: AST_VAR
62+
name: "foo"
63+
1: AST_VAR
64+
name: "bar"
65+
1: AST_CALL
66+
expr: AST_NAME
67+
flags: NAME_FQ (0)
68+
name: "clone"
69+
args: AST_ARG_LIST
70+
0: AST_NAMED_ARG
71+
name: "a"
72+
expr: AST_VAR
73+
name: "foo"
74+
2: AST_CALL
75+
expr: AST_NAME
76+
flags: NAME_FQ (0)
77+
name: "clone"
78+
args: AST_ARG_LIST
79+
0: AST_UNPACK
80+
expr: AST_VAR
81+
name: "foo"
82+
3: AST_CALL
83+
expr: AST_NAME
84+
flags: NAME_FQ (0)
85+
name: "clone"
86+
args: AST_CALLABLE_CONVERT

0 commit comments

Comments
 (0)