Skip to content

Commit 5c4c79e

Browse files
committed
first class callable conversion
1 parent 7bc8f2c commit 5c4c79e

17 files changed

+730
-520
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
First Class Callable from Internal
3+
--FILE--
4+
<?php
5+
$sprintf = sprintf(...);
6+
7+
echo $sprintf("Hello %s", "World");
8+
?>
9+
--EXPECT--
10+
Hello World
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
First Class Callable from User
3+
--FILE--
4+
<?php
5+
function foo() {
6+
return "OK";
7+
}
8+
9+
$foo = foo(...);
10+
11+
echo $foo();
12+
?>
13+
--EXPECT--
14+
OK
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
First Class Callable from Method
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public function bar() {
7+
echo "OK";
8+
}
9+
}
10+
11+
$foo = new Foo;
12+
$bar = $foo->bar(...);
13+
14+
echo $bar();
15+
?>
16+
--EXPECT--
17+
OK
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
First Class Callable from Private Scope
3+
--FILE--
4+
<?php
5+
class Foo {
6+
private function method() {
7+
return "OK";
8+
}
9+
10+
public function bar() {
11+
return $this->method(...);
12+
}
13+
}
14+
15+
$foo = new Foo;
16+
$bar = $foo->bar(...);
17+
18+
echo ($bar())();
19+
?>
20+
--EXPECT--
21+
OK
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
First Class Callable from Magic
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public function __call($method, $args) {
7+
return "OK CALL\n";
8+
}
9+
10+
public static function __callStatic($method, $args) {
11+
return "OK CALL STATIC\n";
12+
}
13+
}
14+
15+
$foo = new Foo;
16+
$bar = $foo->anything(...);
17+
18+
echo $bar();
19+
20+
$qux = Foo::anything(...);
21+
22+
echo $qux();
23+
?>
24+
--EXPECT--
25+
OK CALL
26+
OK CALL STATIC
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
First Class Callable from Closure
3+
--FILE--
4+
<?php
5+
$foo = function(){};
6+
$bar = $foo(...);
7+
8+
if ($foo === $bar) {
9+
echo "OK";
10+
}
11+
?>
12+
--EXPECT--
13+
OK
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
First Class Callable from Special Compiler Function
3+
--FILE--
4+
<?php
5+
$strlen = strlen(...);
6+
7+
if ($strlen("Hello World") === 11) {
8+
echo "OK";
9+
}
10+
?>
11+
--EXPECT--
12+
OK

Zend/zend_ast.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ enum _zend_ast_kind {
7070
ZEND_AST_MAGIC_CONST = 0 << ZEND_AST_NUM_CHILDREN_SHIFT,
7171
ZEND_AST_TYPE,
7272
ZEND_AST_CONSTANT_CLASS,
73+
ZEND_AST_CALLABLE_CONVERT,
7374

7475
/* 1 child node */
7576
ZEND_AST_VAR = 1 << ZEND_AST_NUM_CHILDREN_SHIFT,

Zend/zend_closures.c

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,8 @@ static zend_object *zend_closure_clone(zend_object *zobject) /* {{{ */
486486

487487
int zend_closure_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only) /* {{{ */
488488
{
489-
zend_closure *closure = (zend_closure *)obj;
489+
zend_closure *closure = (zend_closure*)obj;
490+
490491
*fptr_ptr = &closure->func;
491492
*ce_ptr = closure->called_scope;
492493

@@ -734,6 +735,36 @@ ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_clas
734735
}
735736
/* }}} */
736737

738+
void zend_closure_from_frame(zval *return_value, zend_execute_data *call) { /* {{{ */
739+
zval instance;
740+
zend_internal_function trampoline;
741+
zend_function *mptr = call->func;
742+
743+
if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) {
744+
RETURN_OBJ(ZEND_CLOSURE_OBJECT(mptr));
745+
}
746+
747+
if (mptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) {
748+
memset(&trampoline, 0, sizeof(zend_internal_function));
749+
trampoline.type = ZEND_INTERNAL_FUNCTION;
750+
trampoline.fn_flags = mptr->common.fn_flags & ZEND_ACC_STATIC;
751+
trampoline.handler = zend_closure_call_magic;
752+
trampoline.function_name = mptr->common.function_name;
753+
trampoline.scope = mptr->common.scope;
754+
755+
zend_free_trampoline(mptr);
756+
mptr = (zend_function *) &trampoline;
757+
}
758+
759+
if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_THIS) {
760+
ZVAL_OBJ(&instance, Z_OBJ(call->This));
761+
762+
zend_create_fake_closure(return_value, mptr, mptr->common.scope, mptr->common.scope, &instance);
763+
} else {
764+
zend_create_fake_closure(return_value, mptr, mptr->common.scope, mptr->common.scope, NULL);
765+
}
766+
} /* }}} */
767+
737768
void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var) /* {{{ */
738769
{
739770
zend_closure *closure = (zend_closure *) Z_OBJ_P(closure_zv);

Zend/zend_closures.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ BEGIN_EXTERN_C()
2929
void zend_register_closure_ce(void);
3030
void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var);
3131
void zend_closure_bind_var_ex(zval *closure_zv, uint32_t offset, zval *val);
32+
void zend_closure_from_frame(zval *closure_zv, zend_execute_data *frame);
3233

3334
extern ZEND_API zend_class_entry *zend_ce_closure;
3435

0 commit comments

Comments
 (0)