Skip to content

Commit 343ba93

Browse files
committed
Add partial support for first-class callable syntax in constant expressions
1 parent 822881b commit 343ba93

File tree

3 files changed

+73
-3
lines changed

3 files changed

+73
-3
lines changed

Zend/tests/constexpr/callable.phpt

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
First-class callable syntax in constant expressions
3+
--FILE--
4+
<?php
5+
6+
static $staticFn = strlen(...);
7+
8+
#[Attribute]
9+
class MyAttribute {
10+
public function __construct(public readonly Closure $fn) {}
11+
}
12+
13+
#[MyAttribute(fn: strlen(...))]
14+
class X {
15+
const FN = strlen(...);
16+
}
17+
18+
$ref = new ReflectionClass(X::class);
19+
$attr = $ref->getAttributes()[0]->newInstance();
20+
21+
var_dump($staticFn('test123'));
22+
var_dump(($attr->fn)('test123'));
23+
var_dump((X::FN)('test123'));
24+
?>
25+
--EXPECT--
26+
int(7)
27+
int(7)
28+
int(7)

Zend/zend_ast.c

+22
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "zend_exceptions.h"
2626
#include "zend_constants.h"
2727
#include "zend_enum.h"
28+
#include "zend_closures.h"
2829

2930
ZEND_API zend_ast_process_t zend_ast_process = NULL;
3031

@@ -976,6 +977,27 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_ex(zval *result, zend_ast *as
976977
zval_ptr_dtor_nogc(&op2);
977978
return SUCCESS;
978979
}
980+
case ZEND_AST_CALL:
981+
{
982+
zend_function *function_ptr;
983+
zval *function_name;
984+
985+
ZEND_ASSERT(ast->child[1]->kind == ZEND_AST_CALLABLE_CONVERT);
986+
987+
function_name = zend_ast_get_zval(ast->child[0]);
988+
989+
zend_fcall_info_cache fcc;
990+
char *error = NULL;
991+
if (!zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) {
992+
return FAILURE;
993+
}
994+
995+
function_ptr = fcc.function_handler;
996+
997+
zend_create_fake_closure(result, function_ptr, function_ptr->common.scope, fcc.called_scope, NULL);
998+
999+
return SUCCESS;
1000+
}
9791001
default:
9801002
zend_throw_error(NULL, "Unsupported constant expression");
9811003
ret = FAILURE;

Zend/zend_compile.c

+23-3
Original file line numberDiff line numberDiff line change
@@ -9750,8 +9750,13 @@ static void zend_compile_magic_const(znode *result, zend_ast *ast) /* {{{ */
97509750
}
97519751
/* }}} */
97529752

9753-
static bool zend_is_allowed_in_const_expr(zend_ast_kind kind) /* {{{ */
9753+
static bool zend_is_allowed_in_const_expr(zend_ast *ast) /* {{{ */
97549754
{
9755+
zend_ast_kind kind = ast->kind;
9756+
if (kind == ZEND_AST_CALL && ast->child[1]->kind == ZEND_AST_CALLABLE_CONVERT) {
9757+
return true;
9758+
}
9759+
97559760
return kind == ZEND_AST_ZVAL || kind == ZEND_AST_BINARY_OP
97569761
|| kind == ZEND_AST_GREATER || kind == ZEND_AST_GREATER_EQUAL
97579762
|| kind == ZEND_AST_AND || kind == ZEND_AST_OR
@@ -9766,7 +9771,8 @@ static bool zend_is_allowed_in_const_expr(zend_ast_kind kind) /* {{{ */
97669771
|| kind == ZEND_AST_CONST_ENUM_INIT
97679772
|| kind == ZEND_AST_NEW || kind == ZEND_AST_ARG_LIST
97689773
|| kind == ZEND_AST_NAMED_ARG
9769-
|| kind == ZEND_AST_PROP || kind == ZEND_AST_NULLSAFE_PROP;
9774+
|| kind == ZEND_AST_PROP || kind == ZEND_AST_NULLSAFE_PROP
9775+
|| kind == ZEND_AST_CALLABLE_CONVERT;
97709776
}
97719777
/* }}} */
97729778

@@ -9930,7 +9936,7 @@ static void zend_compile_const_expr(zend_ast **ast_ptr, void *context) /* {{{ */
99309936
return;
99319937
}
99329938

9933-
if (!zend_is_allowed_in_const_expr(ast->kind)) {
9939+
if (!zend_is_allowed_in_const_expr(ast)) {
99349940
zend_error_noreturn(E_COMPILE_ERROR, "Constant expression contains invalid operations");
99359941
}
99369942

@@ -10634,6 +10640,20 @@ static void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
1063410640
zend_eval_const_expr(&ast->child[0]);
1063510641
zend_eval_const_expr(&ast->child[1]);
1063610642
return;
10643+
case ZEND_AST_CALL:
10644+
{
10645+
zend_ast *func_ast;
10646+
10647+
if (ast->child[1]->kind != ZEND_AST_CALLABLE_CONVERT) {
10648+
zend_error_noreturn(E_COMPILE_ERROR, "Constant expression contains invalid operations");
10649+
}
10650+
10651+
zend_eval_const_expr(&ast->child[0]);
10652+
func_ast = ast->child[0];
10653+
10654+
ZEND_ASSERT(func_ast->kind == ZEND_AST_ZVAL);
10655+
return;
10656+
}
1063710657
default:
1063810658
return;
1063910659
}

0 commit comments

Comments
 (0)