Skip to content

Commit e98a5b4

Browse files
committed
Support for resolving self/parent/static in ReflectionType
This is only possible if the type is resolvable.
1 parent 8f9e080 commit e98a5b4

14 files changed

+960
-56
lines changed

ext/reflection/php_reflection.c

Lines changed: 107 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ PHPAPI zend_class_entry *reflection_generator_ptr;
8282
PHPAPI zend_class_entry *reflection_parameter_ptr;
8383
PHPAPI zend_class_entry *reflection_type_ptr;
8484
PHPAPI zend_class_entry *reflection_named_type_ptr;
85+
PHPAPI zend_class_entry *reflection_relative_class_type_ptr;
8586
PHPAPI zend_class_entry *reflection_intersection_type_ptr;
8687
PHPAPI zend_class_entry *reflection_union_type_ptr;
8788
PHPAPI zend_class_entry *reflection_class_ptr;
@@ -1337,6 +1338,7 @@ static void reflection_parameter_factory(zend_function *fptr, zval *closure_obje
13371338

13381339
typedef enum {
13391340
NAMED_TYPE = 0,
1341+
RELATIVE_TYPE = 3,
13401342
UNION_TYPE = 1,
13411343
INTERSECTION_TYPE = 2
13421344
} reflection_type_kind;
@@ -1364,6 +1366,11 @@ static reflection_type_kind get_type_kind(zend_type type) {
13641366
if (type_mask_without_null != 0) {
13651367
return UNION_TYPE;
13661368
}
1369+
1370+
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(type));
1371+
if (ZEND_TYPE_IS_RELATIVE_SELF(type) || ZEND_TYPE_IS_RELATIVE_PARENT(type)) {
1372+
return RELATIVE_TYPE;
1373+
}
13671374
return NAMED_TYPE;
13681375
}
13691376
if (type_mask_without_null == MAY_BE_BOOL || ZEND_TYPE_PURE_MASK(type) == MAY_BE_ANY) {
@@ -1373,12 +1380,22 @@ static reflection_type_kind get_type_kind(zend_type type) {
13731380
if ((type_mask_without_null & (type_mask_without_null - 1)) != 0) {
13741381
return UNION_TYPE;
13751382
}
1383+
1384+
/* "static" is a relative type */
1385+
if (type_mask_without_null == MAY_BE_STATIC) {
1386+
return RELATIVE_TYPE;
1387+
}
13761388
return NAMED_TYPE;
13771389
}
13781390

1379-
/* {{{ reflection_type_factory */
1380-
static void reflection_type_factory(zend_type type, zval *object, bool legacy_behavior)
1381-
{
1391+
/* ReflectionType private constructor
1392+
* The object_ce is used to be able to resolve back the "self", "parent", and "static" ReflectionNamedTypes
1393+
* This can be NULL, e.g. when constructing types of a free function
1394+
*/
1395+
static void reflection_type_factory(
1396+
zend_type type, zval *object, bool legacy_behavior,
1397+
zend_class_entry *object_ce
1398+
) {
13821399
reflection_object *intern;
13831400
type_reference *reference;
13841401
reflection_type_kind type_kind = get_type_kind(type);
@@ -1395,6 +1412,9 @@ static void reflection_type_factory(zend_type type, zval *object, bool legacy_be
13951412
case NAMED_TYPE:
13961413
reflection_instantiate(reflection_named_type_ptr, object);
13971414
break;
1415+
case RELATIVE_TYPE:
1416+
reflection_instantiate(reflection_relative_class_type_ptr, object);
1417+
break;
13981418
EMPTY_SWITCH_DEFAULT_CASE();
13991419
}
14001420

@@ -1404,6 +1424,7 @@ static void reflection_type_factory(zend_type type, zval *object, bool legacy_be
14041424
reference->legacy_behavior = legacy_behavior && type_kind == NAMED_TYPE && !is_mixed && !is_only_null;
14051425
intern->ptr = reference;
14061426
intern->ref_type = REF_TYPE_TYPE;
1427+
intern->ce = object_ce;
14071428

14081429
/* Property types may be resolved during the lifetime of the ReflectionType.
14091430
* If we reference a string, make sure it doesn't get released. However, only
@@ -1414,7 +1435,6 @@ static void reflection_type_factory(zend_type type, zval *object, bool legacy_be
14141435
zend_string_addref(ZEND_TYPE_NAME(type));
14151436
}
14161437
}
1417-
/* }}} */
14181438

14191439
/* {{{ reflection_function_factory */
14201440
static void reflection_function_factory(zend_function *function, zval *closure_object, zval *object)
@@ -2669,17 +2689,14 @@ ZEND_METHOD(ReflectionParameter, getClass)
26692689
* TODO: Think about moving these checks to the compiler or some sort of
26702690
* lint-mode.
26712691
*/
2672-
zend_string *class_name;
2673-
2674-
class_name = ZEND_TYPE_NAME(param->arg_info->type);
2675-
if (zend_string_equals_literal_ci(class_name, "self")) {
2692+
if (ZEND_TYPE_IS_RELATIVE_SELF(param->arg_info->type)) {
26762693
ce = param->fptr->common.scope;
26772694
if (!ce) {
26782695
zend_throw_exception_ex(reflection_exception_ptr, 0,
26792696
"Parameter uses \"self\" as type but function is not a class member");
26802697
RETURN_THROWS();
26812698
}
2682-
} else if (zend_string_equals_literal_ci(class_name, "parent")) {
2699+
} else if (ZEND_TYPE_IS_RELATIVE_PARENT(param->arg_info->type)) {
26832700
ce = param->fptr->common.scope;
26842701
if (!ce) {
26852702
zend_throw_exception_ex(reflection_exception_ptr, 0,
@@ -2693,6 +2710,7 @@ ZEND_METHOD(ReflectionParameter, getClass)
26932710
}
26942711
ce = ce->parent;
26952712
} else {
2713+
zend_string *class_name = ZEND_TYPE_NAME(param->arg_info->type);
26962714
ce = zend_lookup_class(class_name);
26972715
if (!ce) {
26982716
zend_throw_exception_ex(reflection_exception_ptr, 0,
@@ -2734,7 +2752,7 @@ ZEND_METHOD(ReflectionParameter, getType)
27342752
if (!ZEND_TYPE_IS_SET(param->arg_info->type)) {
27352753
RETURN_NULL();
27362754
}
2737-
reflection_type_factory(param->arg_info->type, return_value, 1);
2755+
reflection_type_factory(param->arg_info->type, return_value, /* legacy_behavior */ true, intern->ce);
27382756
}
27392757
/* }}} */
27402758

@@ -3106,19 +3124,64 @@ ZEND_METHOD(ReflectionNamedType, isBuiltin)
31063124
}
31073125
/* }}} */
31083126

3109-
static void append_type(zval *return_value, zend_type type) {
3127+
/* {{{ Returns whether type is a builtin type */
3128+
ZEND_METHOD(ReflectionRelativeClassType, resolveToNamedType)
3129+
{
3130+
reflection_object *intern;
3131+
type_reference *param;
3132+
3133+
if (zend_parse_parameters_none() == FAILURE) {
3134+
RETURN_THROWS();
3135+
}
3136+
GET_REFLECTION_OBJECT_PTR(param);
3137+
3138+
/* Unbound closures can use relative class types */
3139+
if (!intern->ce) {
3140+
zend_throw_exception_ex(reflection_exception_ptr, 0,
3141+
"Cannot resolve relative class name for a closure");
3142+
RETURN_THROWS();
3143+
}
3144+
3145+
if (intern->ce->ce_flags & ZEND_ACC_TRAIT) {
3146+
zend_throw_exception_ex(reflection_exception_ptr, 0,
3147+
"Cannot resolve relative class name for a trait");
3148+
RETURN_THROWS();
3149+
}
3150+
3151+
/* Support for legacy behaviour of nullable types and ReflectionNamedType */
3152+
bool allows_null = ZEND_TYPE_PURE_MASK(param->type) & MAY_BE_NULL;
3153+
zend_type resolved_type;
3154+
/* For static resolved name is the name of the class */
3155+
if (ZEND_TYPE_PURE_MASK(param->type) & MAY_BE_STATIC) {
3156+
if (intern->ce->ce_flags & ZEND_ACC_INTERFACE) {
3157+
zend_throw_exception_ex(reflection_exception_ptr, 0,
3158+
"Cannot resolve \"static\" type of an interface");
3159+
RETURN_THROWS();
3160+
}
3161+
resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(intern->ce->name, allows_null, /*extra flags */ 0);
3162+
} else {
3163+
ZEND_ASSERT(ZEND_TYPE_IS_RELATIVE_SELF(param->type) || ZEND_TYPE_IS_RELATIVE_PARENT(param->type));
3164+
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(param->type));
3165+
resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(ZEND_TYPE_NAME(param->type), allows_null, /*extra flags */ 0);
3166+
}
3167+
3168+
reflection_type_factory(resolved_type, return_value, /* legacy_behavior */ true, intern->ce);
3169+
}
3170+
/* }}} */
3171+
3172+
static void append_type(zval *return_value, zend_type type, zend_class_entry *object_ce) {
31103173
zval reflection_type;
31113174
/* Drop iterable BC bit for type list */
31123175
if (ZEND_TYPE_IS_ITERABLE_FALLBACK(type)) {
31133176
ZEND_TYPE_FULL_MASK(type) &= ~_ZEND_TYPE_ITERABLE_BIT;
31143177
}
31153178

3116-
reflection_type_factory(type, &reflection_type, 0);
3179+
reflection_type_factory(type, &reflection_type, /* legacy_behavior */ false, object_ce);
31173180
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &reflection_type);
31183181
}
31193182

3120-
static void append_type_mask(zval *return_value, uint32_t type_mask) {
3121-
append_type(return_value, (zend_type) ZEND_TYPE_INIT_MASK(type_mask));
3183+
static void append_type_mask(zval *return_value, uint32_t type_mask, zend_class_entry *object_ce) {
3184+
append_type(return_value, (zend_type) ZEND_TYPE_INIT_MASK(type_mask), object_ce);
31223185
}
31233186

31243187
/* {{{ Returns the types that are part of this union type */
@@ -3137,46 +3200,53 @@ ZEND_METHOD(ReflectionUnionType, getTypes)
31373200
if (ZEND_TYPE_HAS_LIST(param->type)) {
31383201
zend_type *list_type;
31393202
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(param->type), list_type) {
3140-
append_type(return_value, *list_type);
3203+
append_type(return_value, *list_type, /* object_ce */ intern->ce);
31413204
} ZEND_TYPE_LIST_FOREACH_END();
31423205
} else if (ZEND_TYPE_HAS_NAME(param->type)) {
31433206
zend_string *name = ZEND_TYPE_NAME(param->type);
3144-
append_type(return_value, (zend_type) ZEND_TYPE_INIT_CLASS(name, 0, 0));
3207+
uint32_t type_flags = 0;
3208+
if (ZEND_TYPE_IS_RELATIVE_SELF(param->type)) {
3209+
type_flags = _ZEND_TYPE_SELF_BIT;
3210+
}
3211+
if (ZEND_TYPE_IS_RELATIVE_PARENT(param->type)) {
3212+
type_flags = _ZEND_TYPE_PARENT_BIT;
3213+
}
3214+
append_type(return_value, (zend_type) ZEND_TYPE_INIT_CLASS(name, /* allow_null */ false, /* extra flags */ type_flags), /* object_ce */ intern->ce);
31453215
}
31463216

31473217
type_mask = ZEND_TYPE_PURE_MASK(param->type);
31483218
ZEND_ASSERT(!(type_mask & MAY_BE_VOID));
31493219
ZEND_ASSERT(!(type_mask & MAY_BE_NEVER));
31503220
if (type_mask & MAY_BE_STATIC) {
3151-
append_type_mask(return_value, MAY_BE_STATIC);
3221+
append_type_mask(return_value, MAY_BE_STATIC, /* object_ce */ intern->ce);
31523222
}
31533223
if (type_mask & MAY_BE_CALLABLE) {
3154-
append_type_mask(return_value, MAY_BE_CALLABLE);
3224+
append_type_mask(return_value, MAY_BE_CALLABLE, /* object_ce */ NULL);
31553225
}
31563226
if (type_mask & MAY_BE_OBJECT) {
3157-
append_type_mask(return_value, MAY_BE_OBJECT);
3227+
append_type_mask(return_value, MAY_BE_OBJECT, /* object_ce */ NULL);
31583228
}
31593229
if (type_mask & MAY_BE_ARRAY) {
3160-
append_type_mask(return_value, MAY_BE_ARRAY);
3230+
append_type_mask(return_value, MAY_BE_ARRAY, /* object_ce */ NULL);
31613231
}
31623232
if (type_mask & MAY_BE_STRING) {
3163-
append_type_mask(return_value, MAY_BE_STRING);
3233+
append_type_mask(return_value, MAY_BE_STRING, /* object_ce */ NULL);
31643234
}
31653235
if (type_mask & MAY_BE_LONG) {
3166-
append_type_mask(return_value, MAY_BE_LONG);
3236+
append_type_mask(return_value, MAY_BE_LONG, /* object_ce */ NULL);
31673237
}
31683238
if (type_mask & MAY_BE_DOUBLE) {
3169-
append_type_mask(return_value, MAY_BE_DOUBLE);
3239+
append_type_mask(return_value, MAY_BE_DOUBLE, /* object_ce */ NULL);
31703240
}
31713241
if ((type_mask & MAY_BE_BOOL) == MAY_BE_BOOL) {
3172-
append_type_mask(return_value, MAY_BE_BOOL);
3242+
append_type_mask(return_value, MAY_BE_BOOL, /* object_ce */ NULL);
31733243
} else if (type_mask & MAY_BE_TRUE) {
3174-
append_type_mask(return_value, MAY_BE_TRUE);
3244+
append_type_mask(return_value, MAY_BE_TRUE, /* object_ce */ NULL);
31753245
} else if (type_mask & MAY_BE_FALSE) {
3176-
append_type_mask(return_value, MAY_BE_FALSE);
3246+
append_type_mask(return_value, MAY_BE_FALSE, /* object_ce */ NULL);
31773247
}
31783248
if (type_mask & MAY_BE_NULL) {
3179-
append_type_mask(return_value, MAY_BE_NULL);
3249+
append_type_mask(return_value, MAY_BE_NULL, /* object_ce */ NULL);
31803250
}
31813251
}
31823252
/* }}} */
@@ -3197,7 +3267,7 @@ ZEND_METHOD(ReflectionIntersectionType, getTypes)
31973267

31983268
array_init(return_value);
31993269
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(param->type), list_type) {
3200-
append_type(return_value, *list_type);
3270+
append_type(return_value, *list_type, /* object_ce */ intern->ce);
32013271
} ZEND_TYPE_LIST_FOREACH_END();
32023272
}
32033273
/* }}} */
@@ -3620,7 +3690,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getReturnType)
36203690
RETURN_NULL();
36213691
}
36223692

3623-
reflection_type_factory(fptr->common.arg_info[-1].type, return_value, 1);
3693+
reflection_type_factory(fptr->common.arg_info[-1].type, return_value, /* legacy_behavior */ true, intern->ce);
36243694
}
36253695
/* }}} */
36263696

@@ -3656,7 +3726,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getTentativeReturnType)
36563726
RETURN_NULL();
36573727
}
36583728

3659-
reflection_type_factory(fptr->common.arg_info[-1].type, return_value, 1);
3729+
reflection_type_factory(fptr->common.arg_info[-1].type, return_value, /* legacy_behavior */ true, intern->ce);
36603730
}
36613731
/* }}} */
36623732

@@ -3877,7 +3947,7 @@ ZEND_METHOD(ReflectionClassConstant, getType)
38773947
RETURN_NULL();
38783948
}
38793949

3880-
reflection_type_factory(ref->type, return_value, 1);
3950+
reflection_type_factory(ref->type, return_value, /* legacy_behavior */ true, intern->ce);
38813951
}
38823952

38833953
/* Returns whether class constant has a type */
@@ -5897,7 +5967,7 @@ ZEND_METHOD(ReflectionProperty, getType)
58975967
RETURN_NULL();
58985968
}
58995969

5900-
reflection_type_factory(ref->prop->type, return_value, 1);
5970+
reflection_type_factory(ref->prop->type, return_value, /* legacy_behavior */ true, intern->ce);
59015971
}
59025972
/* }}} */
59035973

@@ -6986,7 +7056,7 @@ ZEND_METHOD(ReflectionEnum, getBackingType)
69867056
RETURN_NULL();
69877057
} else {
69887058
zend_type type = ZEND_TYPE_INIT_CODE(ce->enum_backing_type, 0, 0);
6989-
reflection_type_factory(type, return_value, 0);
7059+
reflection_type_factory(type, return_value, /* legacy_behavior */ false, /* object_ce */ NULL);
69907060
}
69917061
}
69927062

@@ -7246,6 +7316,10 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */
72467316
reflection_named_type_ptr->create_object = reflection_objects_new;
72477317
reflection_named_type_ptr->default_object_handlers = &reflection_object_handlers;
72487318

7319+
reflection_relative_class_type_ptr = register_class_ReflectionRelativeClassType(reflection_named_type_ptr);
7320+
reflection_relative_class_type_ptr->create_object = reflection_objects_new;
7321+
reflection_relative_class_type_ptr->default_object_handlers = &reflection_object_handlers;
7322+
72497323
reflection_union_type_ptr = register_class_ReflectionUnionType(reflection_type_ptr);
72507324
reflection_union_type_ptr->create_object = reflection_objects_new;
72517325
reflection_union_type_ptr->default_object_handlers = &reflection_object_handlers;

ext/reflection/php_reflection.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ extern PHPAPI zend_class_entry *reflection_function_ptr;
3535
extern PHPAPI zend_class_entry *reflection_parameter_ptr;
3636
extern PHPAPI zend_class_entry *reflection_type_ptr;
3737
extern PHPAPI zend_class_entry *reflection_named_type_ptr;
38+
extern PHPAPI zend_class_entry *reflection_relative_class_type_ptr;
3839
extern PHPAPI zend_class_entry *reflection_class_ptr;
3940
extern PHPAPI zend_class_entry *reflection_object_ptr;
4041
extern PHPAPI zend_class_entry *reflection_method_ptr;

ext/reflection/php_reflection.stub.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,11 @@ public function getName(): string {}
661661
public function isBuiltin(): bool {}
662662
}
663663

664+
class ReflectionRelativeClassType extends ReflectionNamedType
665+
{
666+
public function resolveToNamedType(): ReflectionNamedType {}
667+
}
668+
664669
class ReflectionUnionType extends ReflectionType
665670
{
666671
public function getTypes(): array {}

ext/reflection/php_reflection_arginfo.h

Lines changed: 20 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)