[php-src] Issue #19155: zend_ast_tree_copy cannot copy closure with a default argument in zend_ast_process hook

From: Date: Thu, 17 Jul 2025 05:10:20 +0000
Subject: [php-src] Issue #19155: zend_ast_tree_copy cannot copy closure with a default argument in zend_ast_process hook
Groups: php.bugs 
Request: Send a blank email to php-bugs+get-250447@lists.php.net to get a copy of this message
Issue: https://github.com/php/php-src/issues/19155 Author: KaseyJenkins ### Description [zend_ast_copy](https://github.com/php/php-src/blob/php-8.5.0alpha1/Zend/zend_ast.h#L340) seems to be an API function to go when one wants to copy, let's say, function parameters. Under the hood the function of interest is [zend_ast_tree_copy](https://github.com/php/php-src/blob/php-8.5.0alpha1/Zend/zend_ast.c#L1276). We use the zend_ast_process hook to modify the AST after it is parsed and created. So far [zend_ast_tree_copy](https://github.com/php/php-src/blob/php-8.5.0alpha1/Zend/zend_ast.c#L1276) seems to have behaved correctly in such cases, however, with the advent of PHP 8.5.0alpha1 in general and '[Support for Closures in constant expressions](https://wiki.php.net/rfc/closures_in_const_expr)' in particular it appears to no longer properly copy AST nodes. The culprit is ostensibly [ZEND_AST_OP_ARRAY](https://github.com/php/php-src/blob/php-8.5.0alpha1/Zend/zend_ast.h#L39), which does not seem to exist at the time of the zend_ast_process hook and appears only after zend_compile_const_expr -> zend_compile_const_expr_closure, which come later on: ``` zend_const_expr_to_zval (result=result@entry=0x7fffffff9d68, ast_ptr=ast_ptr@entry=0x7ffff448c1f0, allow_dynamic=allow_dynamic@entry=true) at php-src-php-8.5.0alpha1/Zend/zend_compile.c:11517 (gdb) p **ast_ptr {kind = 69, attr = 0, lineno = 25, child = {0x1000000019}} // ZEND_AST_CLOSURE zend_compile_const_expr(ast_ptr, &context); p **ast_ptr {kind = 66, attr = 0, lineno = 25, child = {0x7ffff4405320}} // ZEND_AST_OP_ARRAY ZVAL_AST(&ast_zv, zend_ast_copy(*ast_ptr)); ``` When one tries to use zend_ast_copy (or rather a function mimicking [zend_ast_tree_copy](https://github.com/php/php-src/blob/php-8.5.0alpha1/Zend/zend_ast.c#L1276)'s functionality) on a function parameter that is a [Closure with a default argument](https://github.com/php/php-src/blob/php-8.5.0alpha1/Zend/tests/closures/closure_const_expr/default_args.phpt) it seems to fail to copy the [ZEND_AST_CLOSURE](https://github.com/php/php-src/blob/php-8.5.0alpha1/Zend/zend_ast.h#L44) node since [zend_ast_get_num_children](https://github.com/php/php-src/blob/php-8.5.0alpha1/Zend/zend_ast.c#L1330) will detect it as [zend_ast_is_special](https://github.com/php/php-src/blob/php-8.5.0alpha1/Zend/zend_ast.h#L390) (or it will just return 0 according to [ast->kind >> ZEND_AST_NUM_CHILDREN_SHIFT](https://github.com/php/php-src/blob/php-8.5.0alpha1/Zend/zend_ast.h#L392)), hence no children will be copied (not to mention that the childless node created will be of (zend_ast*) type and not (zend_ast_decl*). How should one copy function parameters (especially [Closures with a default argument](https://github.com/php/php-src/blob/php-8.5.0alpha1/Zend/tests/closures/closure_const_expr/default_args.phpt)) when it comes to PHP 8.5 zend_ast_process hook? Should one no longer use zend_ast_copy (or functions mimicking [zend_ast_tree_copy](https://github.com/php/php-src/blob/php-8.5.0alpha1/Zend/zend_ast.c#L1276)'s functionality) or it was never intended to be used in such a way? Thanks.. ### PHP Version ```plain PHP 8.5.0alpha1 (cli) (built: Jul 4 2025 10:17:12) (NTS) Copyright (c) The PHP Group Zend Engine v4.5.0-dev, Copyright (c) Zend Technologies ``` ### Operating System _No response_

« previous php.bugs (#250447) next »