Skip to content

Commit b335041

Browse files
krakjoenikic
authored andcommitted
first class callable conversion
1 parent d550c84 commit b335041

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
@@ -71,6 +71,7 @@ enum _zend_ast_kind {
7171
ZEND_AST_MAGIC_CONST = 0 << ZEND_AST_NUM_CHILDREN_SHIFT,
7272
ZEND_AST_TYPE,
7373
ZEND_AST_CONSTANT_CLASS,
74+
ZEND_AST_CALLABLE_CONVERT,
7475

7576
/* 1 child node */
7677
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
@@ -518,7 +518,8 @@ static zend_object *zend_closure_clone(zend_object *zobject) /* {{{ */
518518

519519
int zend_closure_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only) /* {{{ */
520520
{
521-
zend_closure *closure = (zend_closure *)obj;
521+
zend_closure *closure = (zend_closure*)obj;
522+
522523
*fptr_ptr = &closure->func;
523524
*ce_ptr = closure->called_scope;
524525

@@ -776,6 +777,36 @@ ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_clas
776777
}
777778
/* }}} */
778779

780+
void zend_closure_from_frame(zval *return_value, zend_execute_data *call) { /* {{{ */
781+
zval instance;
782+
zend_internal_function trampoline;
783+
zend_function *mptr = call->func;
784+
785+
if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) {
786+
RETURN_OBJ(ZEND_CLOSURE_OBJECT(mptr));
787+
}
788+
789+
if (mptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) {
790+
memset(&trampoline, 0, sizeof(zend_internal_function));
791+
trampoline.type = ZEND_INTERNAL_FUNCTION;
792+
trampoline.fn_flags = mptr->common.fn_flags & ZEND_ACC_STATIC;
793+
trampoline.handler = zend_closure_call_magic;
794+
trampoline.function_name = mptr->common.function_name;
795+
trampoline.scope = mptr->common.scope;
796+
797+
zend_free_trampoline(mptr);
798+
mptr = (zend_function *) &trampoline;
799+
}
800+
801+
if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_THIS) {
802+
ZVAL_OBJ(&instance, Z_OBJ(call->This));
803+
804+
zend_create_fake_closure(return_value, mptr, mptr->common.scope, mptr->common.scope, &instance);
805+
} else {
806+
zend_create_fake_closure(return_value, mptr, mptr->common.scope, mptr->common.scope, NULL);
807+
}
808+
} /* }}} */
809+
779810
void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var) /* {{{ */
780811
{
781812
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)