Skip to content

Commit f40e9bf

Browse files
committed
Solve bound closure
1 parent d67ba9a commit f40e9bf

File tree

3 files changed

+106
-49
lines changed

3 files changed

+106
-49
lines changed

ext/reflection/php_reflection.c

Lines changed: 52 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1467,20 +1467,41 @@ static void reflection_type_factory(
14671467
}
14681468
}
14691469

1470-
/* {{{ reflection_function_factory */
1471-
static void reflection_function_factory(zend_function *function, zval *closure_object, zval *object)
1470+
/* copy of hidden zend_closure */
1471+
typedef struct _zend_closure {
1472+
zend_object std;
1473+
zend_function func;
1474+
zval this_ptr;
1475+
zend_class_entry *called_scope;
1476+
zif_handler orig_internal_handler;
1477+
} zend_closure;
1478+
1479+
static void reflection_function_create_common(zend_function *function, zend_object *closure_object, zval *object)
14721480
{
1473-
reflection_object *intern;
1474-
reflection_instantiate(reflection_function_ptr, object);
1475-
intern = Z_REFLECTION_P(object);
1481+
reflection_object *intern = Z_REFLECTION_P(object);
1482+
14761483
intern->ptr = function;
14771484
intern->ref_type = REF_TYPE_FUNCTION;
14781485
intern->ce = NULL;
14791486
if (closure_object) {
1480-
ZVAL_OBJ_COPY(&intern->obj, Z_OBJ_P(closure_object));
1487+
ZVAL_OBJ_COPY(&intern->obj, closure_object);
1488+
zend_closure *closure = (zend_closure*) closure_object;
1489+
/* if it is bound to a class, resolve to it */
1490+
if (closure->called_scope) {
1491+
intern->ce = closure->called_scope;
1492+
}
1493+
} else {
1494+
ZVAL_UNDEF(&intern->obj);
14811495
}
14821496
ZVAL_STR_COPY(reflection_prop_name(object), function->common.function_name);
14831497
}
1498+
1499+
/* {{{ reflection_function_factory */
1500+
static void reflection_function_factory(zend_function *function, zend_object *closure_object, zval *object)
1501+
{
1502+
reflection_instantiate(reflection_function_ptr, object);
1503+
reflection_function_create_common(function, closure_object, object);
1504+
}
14841505
/* }}} */
14851506

14861507
/* {{{ reflection_method_factory */
@@ -1632,27 +1653,18 @@ ZEND_METHOD(Reflection, getModifierNames)
16321653
/* {{{ Constructor. Throws an Exception in case the given function does not exist */
16331654
ZEND_METHOD(ReflectionFunction, __construct)
16341655
{
1635-
zval *object;
16361656
zend_object *closure_obj = NULL;
1637-
reflection_object *intern;
1657+
zend_string *fname = NULL;
16381658
zend_function *fptr;
1639-
zend_string *fname, *lcname;
1640-
1641-
object = ZEND_THIS;
1642-
intern = Z_REFLECTION_P(object);
16431659

16441660
ZEND_PARSE_PARAMETERS_START(1, 1)
16451661
Z_PARAM_OBJ_OF_CLASS_OR_STR(closure_obj, zend_ce_closure, fname)
16461662
ZEND_PARSE_PARAMETERS_END();
16471663

1648-
/* Set CE to NULL */
1649-
intern->ce = NULL;
16501664
if (closure_obj) {
16511665
fptr = (zend_function*)zend_get_closure_method_def(closure_obj);
1652-
//zend_object *tmp = NULL;
1653-
///* We use the get_closure object handler that always succeds to fetch the function and CE pointers */
1654-
//closure_obj->handlers->get_closure(closure_obj, &intern->ce, &fptr, /* obj_ptr */ &tmp, /* check_only */ false);
16551666
} else {
1667+
zend_string *lcname;
16561668
if (UNEXPECTED(ZSTR_VAL(fname)[0] == '\\')) {
16571669
/* Ignore leading "\" */
16581670
ALLOCA_FLAG(use_heap)
@@ -1673,19 +1685,15 @@ ZEND_METHOD(ReflectionFunction, __construct)
16731685
}
16741686
}
16751687

1676-
if (intern->ptr) {
1677-
zval_ptr_dtor(&intern->obj);
1678-
zval_ptr_dtor(reflection_prop_name(object));
1679-
}
1688+
// TODO How can this ever be hit?
1689+
//zval *object = ZEND_THIS;
1690+
//reflection_object *intern = Z_REFLECTION_P(object);
1691+
//if (intern->ptr) {
1692+
// zval_ptr_dtor(&intern->obj);
1693+
// zval_ptr_dtor(reflection_prop_name(object));
1694+
//}
16801695

1681-
ZVAL_STR_COPY(reflection_prop_name(object), fptr->common.function_name);
1682-
intern->ptr = fptr;
1683-
intern->ref_type = REF_TYPE_FUNCTION;
1684-
if (closure_obj) {
1685-
ZVAL_OBJ_COPY(&intern->obj, closure_obj);
1686-
} else {
1687-
ZVAL_UNDEF(&intern->obj);
1688-
}
1696+
reflection_function_create_common(fptr, closure_obj, ZEND_THIS);
16891697
}
16901698
/* }}} */
16911699

@@ -2389,9 +2397,7 @@ ZEND_METHOD(ReflectionGenerator, getFunction)
23892397
REFLECTION_CHECK_VALID_GENERATOR(ex)
23902398

23912399
if (ex->func->common.fn_flags & ZEND_ACC_CLOSURE) {
2392-
zval closure;
2393-
ZVAL_OBJ(&closure, ZEND_CLOSURE_OBJECT(ex->func));
2394-
reflection_function_factory(ex->func, &closure, return_value);
2400+
reflection_function_factory(ex->func, ZEND_CLOSURE_OBJECT(ex->func), return_value);
23952401
} else if (ex->func->op_array.scope) {
23962402
reflection_method_factory(ex->func->op_array.scope, ex->func, NULL, return_value);
23972403
} else {
@@ -2674,7 +2680,7 @@ ZEND_METHOD(ReflectionParameter, getDeclaringFunction)
26742680
GET_REFLECTION_OBJECT_PTR(param);
26752681

26762682
if (!param->fptr->common.scope) {
2677-
reflection_function_factory(_copy_function(param->fptr), Z_ISUNDEF(intern->obj)? NULL : &intern->obj, return_value);
2683+
reflection_function_factory(_copy_function(param->fptr), Z_ISUNDEF(intern->obj)? NULL : Z_OBJ(intern->obj), return_value);
26782684
} else {
26792685
reflection_method_factory(param->fptr->common.scope, _copy_function(param->fptr), Z_ISUNDEF(intern->obj)? NULL : &intern->obj, return_value);
26802686
}
@@ -3159,7 +3165,7 @@ ZEND_METHOD(ReflectionNamedType, isBuiltin)
31593165
}
31603166
/* }}} */
31613167

3162-
/* {{{ Returns whether type is a builtin type */
3168+
/* {{{ Resolve a relative class type to a proper named type */
31633169
ZEND_METHOD(ReflectionRelativeClassType, resolveToNamedType)
31643170
{
31653171
reflection_object *intern;
@@ -3195,9 +3201,19 @@ ZEND_METHOD(ReflectionRelativeClassType, resolveToNamedType)
31953201
}
31963202
resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(intern->ce->name, allows_null, /* extra flags */ 0);
31973203
} else {
3198-
ZEND_ASSERT(zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "self") || zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "parent"));
31993204
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(param->type));
3200-
resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(ZEND_TYPE_NAME(param->type), allows_null, /* extra flags */ 0);
3205+
ZEND_ASSERT(zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "self") || zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "parent"));
3206+
3207+
if (zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "self")) {
3208+
resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(intern->ce->name, allows_null, /* extra flags */ 0);
3209+
} else {
3210+
if (!intern->ce->parent) {
3211+
zend_throw_exception_ex(reflection_exception_ptr, 0,
3212+
"Cannot resolve \"parent\" type when class has no parent");
3213+
RETURN_THROWS();
3214+
}
3215+
resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(intern->ce->parent->name, allows_null, /* extra flags */ 0);
3216+
}
32013217
}
32023218

32033219
reflection_type_factory(resolved_type, return_value, /* legacy_behavior */ true, intern->ce);
Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
--TEST--
2-
ReflectionTypes of relative class types (self, parent) from traits in classes, variadic parameter DNF types
3-
--XFAIL--
4-
I'm confused why it not possible to chope the bound scope via Reflection
2+
ReflectionTypes of relative class types (self, parent) in Closure bound to class with parent
53
--FILE--
64
<?php
75

@@ -20,24 +18,36 @@ $instances = [
2018

2119
$b = new B();
2220

23-
// TODO Method getClosureScopeClass() seems useful
24-
// TODO Need to pass scope to: ZEND_METHOD(ReflectionFunctionAbstract, getParameters) ?
25-
// TODO Need to pass scope to: ZEND_METHOD(ReflectionFunctionAbstract, getReturnType) ?
2621
foreach ($instances as $instance) {
27-
$instance->bindTo($b);
28-
$rc = new ReflectionFunction($instance);
29-
echo "\tBound to: ", $rc->getClosureScopeClass()->name, PHP_EOL;
22+
$fn = $instance->bindTo($b);
23+
$rc = new ReflectionFunction($fn);
24+
echo "Bound to: ", $rc->getClosureCalledClass()->name, PHP_EOL;
3025
$type = $rc->getReturnType();
31-
echo "\tType: ", $type, PHP_EOL;
32-
echo "\tInstance of: ", $type::class, PHP_EOL;
26+
echo "Type: ", $type, PHP_EOL;
27+
echo "Instance of: ", $type::class, PHP_EOL;
3328
try {
3429
$resolvedType = $type->resolveToNamedType();
35-
echo "\t\tResolved Type: ", $resolvedType, PHP_EOL;
36-
echo "\t\tInstance of: ", $resolvedType::class, PHP_EOL;
30+
echo "\tResolved Type: ", $resolvedType, PHP_EOL;
31+
echo "\tInstance of: ", $resolvedType::class, PHP_EOL;
3732
} catch (\Throwable $e) {
3833
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
3934
}
4035
}
4136

4237
?>
4338
--EXPECT--
39+
Bound to: B
40+
Type: self
41+
Instance of: ReflectionRelativeClassType
42+
Resolved Type: B
43+
Instance of: ReflectionNamedType
44+
Bound to: B
45+
Type: parent
46+
Instance of: ReflectionRelativeClassType
47+
Resolved Type: A
48+
Instance of: ReflectionNamedType
49+
Bound to: B
50+
Type: static
51+
Instance of: ReflectionRelativeClassType
52+
Resolved Type: B
53+
Instance of: ReflectionNamedType
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
ReflectionTypes of relative class type parent in Closure bound to class with no parent
3+
--FILE--
4+
<?php
5+
6+
class A {}
7+
8+
$fnParent = function (): parent {};
9+
10+
$a = new A();
11+
12+
$fn = $fnParent->bindTo($a);
13+
$rc = new ReflectionFunction($fn);
14+
echo "Bound to: ", $rc->getClosureCalledClass()->name, PHP_EOL;
15+
$type = $rc->getReturnType();
16+
echo "Type: ", $type, PHP_EOL;
17+
echo "Instance of: ", $type::class, PHP_EOL;
18+
try {
19+
$resolvedType = $type->resolveToNamedType();
20+
echo "\tResolved Type: ", $resolvedType, PHP_EOL;
21+
echo "\tInstance of: ", $resolvedType::class, PHP_EOL;
22+
} catch (\Throwable $e) {
23+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
24+
}
25+
26+
?>
27+
--EXPECT--
28+
Bound to: A
29+
Type: parent
30+
Instance of: ReflectionRelativeClassType
31+
ReflectionException: Cannot resolve "parent" type when class has no parent

0 commit comments

Comments
 (0)