Skip to content

Commit 4e13f14

Browse files
committed
Preserve the original AST for closures in const-expressions
This is necessary when stringifying a `ReflectionAttribute` that has a closure as a parameter.
1 parent 5287c6b commit 4e13f14

File tree

7 files changed

+50
-10
lines changed

7 files changed

+50
-10
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
AST printing for closures in attributes at runtime
3+
--FILE--
4+
<?php
5+
6+
#[Attr(static function ($foo) {
7+
echo $foo;
8+
})]
9+
function foo() { }
10+
11+
$r = new ReflectionFunction('foo');
12+
foreach ($r->getAttributes() as $attribute) {
13+
echo $attribute;
14+
}
15+
16+
?>
17+
--EXPECT--
18+
Attribute [ Attr ] {
19+
- Arguments [1] {
20+
Argument #0 [ static function ($foo) {
21+
echo $foo;
22+
} ]
23+
}
24+
}

Zend/zend_ast.c

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,14 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_constant(zend_string *name, ze
100100
return (zend_ast *) ast;
101101
}
102102

103-
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_op_array(zend_op_array *op_array) {
103+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_op_array(zend_op_array *op_array, zend_ast *original_ast, zend_ast_attr attr) {
104104
zend_ast_op_array *ast;
105105

106106
ast = zend_ast_alloc(sizeof(zend_ast_op_array));
107107
ast->kind = ZEND_AST_OP_ARRAY;
108-
ast->attr = 0;
109-
ast->lineno = CG(zend_lineno);
108+
ast->attr = attr;
110109
ast->op_array = op_array;
110+
ast->ast = original_ast;
111111

112112
return (zend_ast *) ast;
113113
}
@@ -1091,7 +1091,7 @@ static size_t ZEND_FASTCALL zend_ast_tree_size(zend_ast *ast)
10911091
if (ast->kind == ZEND_AST_ZVAL || ast->kind == ZEND_AST_CONSTANT) {
10921092
size = sizeof(zend_ast_zval);
10931093
} else if (ast->kind == ZEND_AST_OP_ARRAY) {
1094-
size = sizeof(zend_ast_op_array);
1094+
size = sizeof(zend_ast_op_array) + zend_ast_tree_size(zend_ast_get_op_array(ast)->ast);
10951095
} else if (zend_ast_is_list(ast)) {
10961096
uint32_t i;
10971097
zend_ast_list *list = zend_ast_get_list(ast);
@@ -1161,9 +1161,10 @@ static void* ZEND_FASTCALL zend_ast_tree_copy(zend_ast *ast, void *buf)
11611161
zend_ast_op_array *new = (zend_ast_op_array*)buf;
11621162
new->kind = old->kind;
11631163
new->attr = old->attr;
1164-
new->lineno = old->lineno;
11651164
new->op_array = old->op_array;
11661165
buf = (void*)((char*)buf + sizeof(zend_ast_op_array));
1166+
new->ast = (zend_ast*)buf;
1167+
buf = zend_ast_tree_copy(old->ast, buf);
11671168
} else if (zend_ast_is_decl(ast)) {
11681169
zend_ast_decl *old = (zend_ast_decl*)ast;
11691170
zend_ast_decl *new = (zend_ast_decl*)buf;
@@ -1256,7 +1257,7 @@ ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast)
12561257
} else if (EXPECTED(ast->kind == ZEND_AST_CONSTANT)) {
12571258
zend_string_release_ex(zend_ast_get_constant_name(ast), 0);
12581259
} else if (EXPECTED(ast->kind == ZEND_AST_OP_ARRAY)) {
1259-
/* Nothing to do. */
1260+
zend_ast_destroy(zend_ast_get_op_array(ast)->ast);
12601261
} else if (zend_ast_is_decl(ast)) {
12611262
zend_ast_decl *decl = (zend_ast_decl *) ast;
12621263

@@ -1915,6 +1916,10 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
19151916
smart_str_appendl(str, ZSTR_VAL(name), ZSTR_LEN(name));
19161917
break;
19171918
}
1919+
case ZEND_AST_OP_ARRAY: {
1920+
zend_ast_export_ex(str, zend_ast_get_op_array(ast)->ast, priority, indent);
1921+
break;
1922+
}
19181923
case ZEND_AST_CONSTANT_CLASS:
19191924
smart_str_appendl(str, "__CLASS__", sizeof("__CLASS__")-1);
19201925
break;

Zend/zend_ast.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,8 @@ typedef struct _zend_op_array zend_op_array;
212212
typedef struct _zend_ast_op_array {
213213
zend_ast_kind kind;
214214
zend_ast_attr attr;
215-
uint32_t lineno;
216215
zend_op_array *op_array;
216+
zend_ast *ast;
217217
} zend_ast_op_array;
218218

219219
/* Separate structure for function and class declaration, as they need extra information. */
@@ -240,7 +240,7 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_zval_from_long(zend_long lval)
240240
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_constant(zend_string *name, zend_ast_attr attr);
241241
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_class_const_or_name(zend_ast *class_name, zend_ast *name);
242242

243-
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_op_array(zend_op_array *op_array);
243+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_op_array(zend_op_array *op_array, zend_ast *original_ast, zend_ast_attr attr);
244244

245245
#if ZEND_AST_SPEC
246246
# define ZEND_AST_SPEC_CALL(name, ...) \
@@ -388,6 +388,8 @@ static zend_always_inline uint32_t zend_ast_get_lineno(zend_ast *ast) {
388388
} else if (ast->kind == ZEND_AST_CONSTANT) {
389389
zval *zv = &((zend_ast_zval *) ast)->val;
390390
return Z_LINENO_P(zv);
391+
} else if (ast->kind == ZEND_AST_OP_ARRAY) {
392+
return zend_ast_get_op_array(ast)->ast->lineno;
391393
} else {
392394
return ast->lineno;
393395
}

Zend/zend_compile.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11222,8 +11222,7 @@ static void zend_compile_const_expr_closure(zend_ast **ast_ptr)
1122211222
znode node;
1122311223
zend_op_array *op = zend_compile_func_decl(&node, (zend_ast*)closure_ast, FUNC_DECL_LEVEL_CONSTEXPR);
1122411224

11225-
zend_ast_destroy(*ast_ptr);
11226-
*ast_ptr = zend_ast_create_op_array(op);
11225+
*ast_ptr = zend_ast_create_op_array(op, (zend_ast*)closure_ast, 0);
1122711226
}
1122811227

1122911228
static void zend_compile_const_expr_args(zend_ast **ast_ptr)

ext/opcache/zend_file_cache.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,11 @@ static void zend_file_cache_serialize_ast(zend_ast *ast,
366366
} else if (ast->kind == ZEND_AST_OP_ARRAY) {
367367
/* The op_array itself will be serialized as part of the dynamic_func_defs. */
368368
SERIALIZE_PTR(zend_ast_get_op_array(ast)->op_array);
369+
370+
SERIALIZE_PTR(zend_ast_get_op_array(ast)->ast);
371+
tmp = zend_ast_get_op_array(ast)->ast;
372+
UNSERIALIZE_PTR(tmp);
373+
zend_file_cache_serialize_ast(tmp, script, info, buf);
369374
} else if (zend_ast_is_decl(ast)) {
370375
zend_ast_decl *decl = (zend_ast_decl*)ast;
371376
for (i = 0; i < 5; i++) {
@@ -1258,6 +1263,9 @@ static void zend_file_cache_unserialize_ast(zend_ast *ast,
12581263
} else if (ast->kind == ZEND_AST_OP_ARRAY) {
12591264
/* The op_array itself will be unserialized as part of the dynamic_func_defs. */
12601265
UNSERIALIZE_PTR(zend_ast_get_op_array(ast)->op_array);
1266+
1267+
UNSERIALIZE_PTR(zend_ast_get_op_array(ast)->ast);
1268+
zend_file_cache_unserialize_ast(zend_ast_get_op_array(ast)->ast, script, buf);
12611269
} else if (zend_ast_is_decl(ast)) {
12621270
zend_ast_decl *decl = (zend_ast_decl*)ast;
12631271
for (i = 0; i < 5; i++) {

ext/opcache/zend_persist.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ static zend_ast *zend_persist_ast(zend_ast *ast)
194194
ZVAL_PTR(&z, copy->op_array);
195195
zend_persist_op_array(&z);
196196
copy->op_array = Z_PTR(z);
197+
copy->ast = zend_persist_ast(copy->ast);
197198
node = (zend_ast *) copy;
198199
} else if (zend_ast_is_decl(ast)) {
199200
zend_ast_decl *copy = zend_shared_memdup(ast, sizeof(zend_ast_decl));

ext/opcache/zend_persist_calc.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ static void zend_persist_ast_calc(zend_ast *ast)
9191
zval z;
9292
ZVAL_PTR(&z, zend_ast_get_op_array(ast)->op_array);
9393
zend_persist_op_array_calc(&z);
94+
zend_persist_ast_calc(zend_ast_get_op_array(ast)->ast);
9495
} else if (zend_ast_is_decl(ast)) {
9596
zend_ast_decl *decl = (zend_ast_decl*)ast;
9697
ADD_SIZE(sizeof(zend_ast_decl));

0 commit comments

Comments
 (0)