Skip to content

Commit 616f6e7

Browse files
committed
Solve bound closure
1 parent dd3d9ee commit 616f6e7

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
@@ -1438,20 +1438,41 @@ static void reflection_type_factory(
14381438
}
14391439
}
14401440

1441-
/* {{{ reflection_function_factory */
1442-
static void reflection_function_factory(zend_function *function, zval *closure_object, zval *object)
1441+
/* copy of hidden zend_closure */
1442+
typedef struct _zend_closure {
1443+
zend_object std;
1444+
zend_function func;
1445+
zval this_ptr;
1446+
zend_class_entry *called_scope;
1447+
zif_handler orig_internal_handler;
1448+
} zend_closure;
1449+
1450+
static void reflection_function_create_common(zend_function *function, zend_object *closure_object, zval *object)
14431451
{
1444-
reflection_object *intern;
1445-
reflection_instantiate(reflection_function_ptr, object);
1446-
intern = Z_REFLECTION_P(object);
1452+
reflection_object *intern = Z_REFLECTION_P(object);
1453+
14471454
intern->ptr = function;
14481455
intern->ref_type = REF_TYPE_FUNCTION;
14491456
intern->ce = NULL;
14501457
if (closure_object) {
1451-
ZVAL_OBJ_COPY(&intern->obj, Z_OBJ_P(closure_object));
1458+
ZVAL_OBJ_COPY(&intern->obj, closure_object);
1459+
zend_closure *closure = (zend_closure*) closure_object;
1460+
/* if it is bound to a class, resolve to it */
1461+
if (closure->called_scope) {
1462+
intern->ce = closure->called_scope;
1463+
}
1464+
} else {
1465+
ZVAL_UNDEF(&intern->obj);
14521466
}
14531467
ZVAL_STR_COPY(reflection_prop_name(object), function->common.function_name);
14541468
}
1469+
1470+
/* {{{ reflection_function_factory */
1471+
static void reflection_function_factory(zend_function *function, zend_object *closure_object, zval *object)
1472+
{
1473+
reflection_instantiate(reflection_function_ptr, object);
1474+
reflection_function_create_common(function, closure_object, object);
1475+
}
14551476
/* }}} */
14561477

14571478
/* {{{ reflection_method_factory */
@@ -1603,27 +1624,18 @@ ZEND_METHOD(Reflection, getModifierNames)
16031624
/* {{{ Constructor. Throws an Exception in case the given function does not exist */
16041625
ZEND_METHOD(ReflectionFunction, __construct)
16051626
{
1606-
zval *object;
16071627
zend_object *closure_obj = NULL;
1608-
reflection_object *intern;
1628+
zend_string *fname = NULL;
16091629
zend_function *fptr;
1610-
zend_string *fname, *lcname;
1611-
1612-
object = ZEND_THIS;
1613-
intern = Z_REFLECTION_P(object);
16141630

16151631
ZEND_PARSE_PARAMETERS_START(1, 1)
16161632
Z_PARAM_OBJ_OF_CLASS_OR_STR(closure_obj, zend_ce_closure, fname)
16171633
ZEND_PARSE_PARAMETERS_END();
16181634

1619-
/* Set CE to NULL */
1620-
intern->ce = NULL;
16211635
if (closure_obj) {
16221636
fptr = (zend_function*)zend_get_closure_method_def(closure_obj);
1623-
//zend_object *tmp = NULL;
1624-
///* We use the get_closure object handler that always succeds to fetch the function and CE pointers */
1625-
//closure_obj->handlers->get_closure(closure_obj, &intern->ce, &fptr, /* obj_ptr */ &tmp, /* check_only */ false);
16261637
} else {
1638+
zend_string *lcname;
16271639
if (UNEXPECTED(ZSTR_VAL(fname)[0] == '\\')) {
16281640
/* Ignore leading "\" */
16291641
ALLOCA_FLAG(use_heap)
@@ -1644,19 +1656,15 @@ ZEND_METHOD(ReflectionFunction, __construct)
16441656
}
16451657
}
16461658

1647-
if (intern->ptr) {
1648-
zval_ptr_dtor(&intern->obj);
1649-
zval_ptr_dtor(reflection_prop_name(object));
1650-
}
1659+
// TODO How can this ever be hit?
1660+
//zval *object = ZEND_THIS;
1661+
//reflection_object *intern = Z_REFLECTION_P(object);
1662+
//if (intern->ptr) {
1663+
// zval_ptr_dtor(&intern->obj);
1664+
// zval_ptr_dtor(reflection_prop_name(object));
1665+
//}
16511666

1652-
ZVAL_STR_COPY(reflection_prop_name(object), fptr->common.function_name);
1653-
intern->ptr = fptr;
1654-
intern->ref_type = REF_TYPE_FUNCTION;
1655-
if (closure_obj) {
1656-
ZVAL_OBJ_COPY(&intern->obj, closure_obj);
1657-
} else {
1658-
ZVAL_UNDEF(&intern->obj);
1659-
}
1667+
reflection_function_create_common(fptr, closure_obj, ZEND_THIS);
16601668
}
16611669
/* }}} */
16621670

@@ -2360,9 +2368,7 @@ ZEND_METHOD(ReflectionGenerator, getFunction)
23602368
REFLECTION_CHECK_VALID_GENERATOR(ex)
23612369

23622370
if (ex->func->common.fn_flags & ZEND_ACC_CLOSURE) {
2363-
zval closure;
2364-
ZVAL_OBJ(&closure, ZEND_CLOSURE_OBJECT(ex->func));
2365-
reflection_function_factory(ex->func, &closure, return_value);
2371+
reflection_function_factory(ex->func, ZEND_CLOSURE_OBJECT(ex->func), return_value);
23662372
} else if (ex->func->op_array.scope) {
23672373
reflection_method_factory(ex->func->op_array.scope, ex->func, NULL, return_value);
23682374
} else {
@@ -2645,7 +2651,7 @@ ZEND_METHOD(ReflectionParameter, getDeclaringFunction)
26452651
GET_REFLECTION_OBJECT_PTR(param);
26462652

26472653
if (!param->fptr->common.scope) {
2648-
reflection_function_factory(_copy_function(param->fptr), Z_ISUNDEF(intern->obj)? NULL : &intern->obj, return_value);
2654+
reflection_function_factory(_copy_function(param->fptr), Z_ISUNDEF(intern->obj)? NULL : Z_OBJ(intern->obj), return_value);
26492655
} else {
26502656
reflection_method_factory(param->fptr->common.scope, _copy_function(param->fptr), Z_ISUNDEF(intern->obj)? NULL : &intern->obj, return_value);
26512657
}
@@ -3130,7 +3136,7 @@ ZEND_METHOD(ReflectionNamedType, isBuiltin)
31303136
}
31313137
/* }}} */
31323138

3133-
/* {{{ Returns whether type is a builtin type */
3139+
/* {{{ Resolve a relative class type to a proper named type */
31343140
ZEND_METHOD(ReflectionRelativeClassType, resolveToNamedType)
31353141
{
31363142
reflection_object *intern;
@@ -3166,9 +3172,19 @@ ZEND_METHOD(ReflectionRelativeClassType, resolveToNamedType)
31663172
}
31673173
resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(intern->ce->name, allows_null, /* extra flags */ 0);
31683174
} else {
3169-
ZEND_ASSERT(zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "self") || zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "parent"));
31703175
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(param->type));
3171-
resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(ZEND_TYPE_NAME(param->type), allows_null, /* extra flags */ 0);
3176+
ZEND_ASSERT(zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "self") || zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "parent"));
3177+
3178+
if (zend_string_equals_literal_ci(ZEND_TYPE_NAME(param->type), "self")) {
3179+
resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(intern->ce->name, allows_null, /* extra flags */ 0);
3180+
} else {
3181+
if (!intern->ce->parent) {
3182+
zend_throw_exception_ex(reflection_exception_ptr, 0,
3183+
"Cannot resolve \"parent\" type when class has no parent");
3184+
RETURN_THROWS();
3185+
}
3186+
resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(intern->ce->parent->name, allows_null, /* extra flags */ 0);
3187+
}
31723188
}
31733189

31743190
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)