Skip to content

Commit 409ea6c

Browse files
committed
Support for resolving self/parent/static in ReflectionType
This is only possible if the type is resolvable.
1 parent eb3afff commit 409ea6c

14 files changed

+961
-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;
@@ -1328,6 +1329,7 @@ static void reflection_parameter_factory(zend_function *fptr, zval *closure_obje
13281329

13291330
typedef enum {
13301331
NAMED_TYPE = 0,
1332+
RELATIVE_TYPE = 3,
13311333
UNION_TYPE = 1,
13321334
INTERSECTION_TYPE = 2
13331335
} reflection_type_kind;
@@ -1355,6 +1357,11 @@ static reflection_type_kind get_type_kind(zend_type type) {
13551357
if (type_mask_without_null != 0) {
13561358
return UNION_TYPE;
13571359
}
1360+
1361+
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(type));
1362+
if (ZEND_TYPE_IS_RELATIVE_SELF(type) || ZEND_TYPE_IS_RELATIVE_PARENT(type)) {
1363+
return RELATIVE_TYPE;
1364+
}
13581365
return NAMED_TYPE;
13591366
}
13601367
if (type_mask_without_null == MAY_BE_BOOL || ZEND_TYPE_PURE_MASK(type) == MAY_BE_ANY) {
@@ -1364,12 +1371,22 @@ static reflection_type_kind get_type_kind(zend_type type) {
13641371
if ((type_mask_without_null & (type_mask_without_null - 1)) != 0) {
13651372
return UNION_TYPE;
13661373
}
1374+
1375+
/* "static" is a relative type */
1376+
if (type_mask_without_null == MAY_BE_STATIC) {
1377+
return RELATIVE_TYPE;
1378+
}
13671379
return NAMED_TYPE;
13681380
}
13691381

1370-
/* {{{ reflection_type_factory */
1371-
static void reflection_type_factory(zend_type type, zval *object, bool legacy_behavior)
1372-
{
1382+
/* ReflectionType private constructor
1383+
* The object_ce is used to be able to resolve back the "self", "parent", and "static" ReflectionNamedTypes
1384+
* This can be NULL, e.g. when constructing types of a free function
1385+
*/
1386+
static void reflection_type_factory(
1387+
zend_type type, zval *object, bool legacy_behavior,
1388+
zend_class_entry *object_ce
1389+
) {
13731390
reflection_object *intern;
13741391
type_reference *reference;
13751392
reflection_type_kind type_kind = get_type_kind(type);
@@ -1386,6 +1403,9 @@ static void reflection_type_factory(zend_type type, zval *object, bool legacy_be
13861403
case NAMED_TYPE:
13871404
reflection_instantiate(reflection_named_type_ptr, object);
13881405
break;
1406+
case RELATIVE_TYPE:
1407+
reflection_instantiate(reflection_relative_class_type_ptr, object);
1408+
break;
13891409
EMPTY_SWITCH_DEFAULT_CASE();
13901410
}
13911411

@@ -1395,6 +1415,7 @@ static void reflection_type_factory(zend_type type, zval *object, bool legacy_be
13951415
reference->legacy_behavior = legacy_behavior && type_kind == NAMED_TYPE && !is_mixed && !is_only_null;
13961416
intern->ptr = reference;
13971417
intern->ref_type = REF_TYPE_TYPE;
1418+
intern->ce = object_ce;
13981419

13991420
/* Property types may be resolved during the lifetime of the ReflectionType.
14001421
* If we reference a string, make sure it doesn't get released. However, only
@@ -1405,7 +1426,6 @@ static void reflection_type_factory(zend_type type, zval *object, bool legacy_be
14051426
zend_string_addref(ZEND_TYPE_NAME(type));
14061427
}
14071428
}
1408-
/* }}} */
14091429

14101430
/* {{{ reflection_function_factory */
14111431
static void reflection_function_factory(zend_function *function, zval *closure_object, zval *object)
@@ -2653,17 +2673,14 @@ ZEND_METHOD(ReflectionParameter, getClass)
26532673
* TODO: Think about moving these checks to the compiler or some sort of
26542674
* lint-mode.
26552675
*/
2656-
zend_string *class_name;
2657-
2658-
class_name = ZEND_TYPE_NAME(param->arg_info->type);
2659-
if (zend_string_equals_literal_ci(class_name, "self")) {
2676+
if (ZEND_TYPE_IS_RELATIVE_SELF(param->arg_info->type)) {
26602677
ce = param->fptr->common.scope;
26612678
if (!ce) {
26622679
zend_throw_exception_ex(reflection_exception_ptr, 0,
26632680
"Parameter uses \"self\" as type but function is not a class member");
26642681
RETURN_THROWS();
26652682
}
2666-
} else if (zend_string_equals_literal_ci(class_name, "parent")) {
2683+
} else if (ZEND_TYPE_IS_RELATIVE_PARENT(param->arg_info->type)) {
26672684
ce = param->fptr->common.scope;
26682685
if (!ce) {
26692686
zend_throw_exception_ex(reflection_exception_ptr, 0,
@@ -2677,6 +2694,7 @@ ZEND_METHOD(ReflectionParameter, getClass)
26772694
}
26782695
ce = ce->parent;
26792696
} else {
2697+
zend_string *class_name = ZEND_TYPE_NAME(param->arg_info->type);
26802698
ce = zend_lookup_class(class_name);
26812699
if (!ce) {
26822700
zend_throw_exception_ex(reflection_exception_ptr, 0,
@@ -2718,7 +2736,7 @@ ZEND_METHOD(ReflectionParameter, getType)
27182736
if (!ZEND_TYPE_IS_SET(param->arg_info->type)) {
27192737
RETURN_NULL();
27202738
}
2721-
reflection_type_factory(param->arg_info->type, return_value, 1);
2739+
reflection_type_factory(param->arg_info->type, return_value, /* legacy_behavior */ true, intern->ce);
27222740
}
27232741
/* }}} */
27242742

@@ -3090,19 +3108,64 @@ ZEND_METHOD(ReflectionNamedType, isBuiltin)
30903108
}
30913109
/* }}} */
30923110

3093-
static void append_type(zval *return_value, zend_type type) {
3111+
/* {{{ Returns whether type is a builtin type */
3112+
ZEND_METHOD(ReflectionRelativeClassType, resolveToNamedType)
3113+
{
3114+
reflection_object *intern;
3115+
type_reference *param;
3116+
3117+
if (zend_parse_parameters_none() == FAILURE) {
3118+
RETURN_THROWS();
3119+
}
3120+
GET_REFLECTION_OBJECT_PTR(param);
3121+
3122+
/* Unbound closures can use relative class types */
3123+
if (!intern->ce) {
3124+
zend_throw_exception_ex(reflection_exception_ptr, 0,
3125+
"Cannot resolve relative class name for a closure");
3126+
RETURN_THROWS();
3127+
}
3128+
3129+
if (intern->ce->ce_flags & ZEND_ACC_TRAIT) {
3130+
zend_throw_exception_ex(reflection_exception_ptr, 0,
3131+
"Cannot resolve relative class name for a trait");
3132+
RETURN_THROWS();
3133+
}
3134+
3135+
/* Support for legacy behaviour of nullable types and ReflectionNamedType */
3136+
bool allows_null = ZEND_TYPE_PURE_MASK(param->type) & MAY_BE_NULL;
3137+
zend_type resolved_type;
3138+
/* For static resolved name is the name of the class */
3139+
if (ZEND_TYPE_PURE_MASK(param->type) & MAY_BE_STATIC) {
3140+
if (intern->ce->ce_flags & ZEND_ACC_INTERFACE) {
3141+
zend_throw_exception_ex(reflection_exception_ptr, 0,
3142+
"Cannot resolve \"static\" type of an interface");
3143+
RETURN_THROWS();
3144+
}
3145+
resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(intern->ce->name, allows_null, /*extra flags */ 0);
3146+
} else {
3147+
ZEND_ASSERT(ZEND_TYPE_IS_RELATIVE_SELF(param->type) || ZEND_TYPE_IS_RELATIVE_PARENT(param->type));
3148+
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(param->type));
3149+
resolved_type = (zend_type) ZEND_TYPE_INIT_CLASS(ZEND_TYPE_NAME(param->type), allows_null, /*extra flags */ 0);
3150+
}
3151+
3152+
reflection_type_factory(resolved_type, return_value, /* legacy_behavior */ true, intern->ce);
3153+
}
3154+
/* }}} */
3155+
3156+
static void append_type(zval *return_value, zend_type type, zend_class_entry *object_ce) {
30943157
zval reflection_type;
30953158
/* Drop iterable BC bit for type list */
30963159
if (ZEND_TYPE_IS_ITERABLE_FALLBACK(type)) {
30973160
ZEND_TYPE_FULL_MASK(type) &= ~_ZEND_TYPE_ITERABLE_BIT;
30983161
}
30993162

3100-
reflection_type_factory(type, &reflection_type, 0);
3163+
reflection_type_factory(type, &reflection_type, /* legacy_behavior */ false, object_ce);
31013164
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &reflection_type);
31023165
}
31033166

3104-
static void append_type_mask(zval *return_value, uint32_t type_mask) {
3105-
append_type(return_value, (zend_type) ZEND_TYPE_INIT_MASK(type_mask));
3167+
static void append_type_mask(zval *return_value, uint32_t type_mask, zend_class_entry *object_ce) {
3168+
append_type(return_value, (zend_type) ZEND_TYPE_INIT_MASK(type_mask), object_ce);
31063169
}
31073170

31083171
/* {{{ Returns the types that are part of this union type */
@@ -3121,46 +3184,53 @@ ZEND_METHOD(ReflectionUnionType, getTypes)
31213184
if (ZEND_TYPE_HAS_LIST(param->type)) {
31223185
zend_type *list_type;
31233186
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(param->type), list_type) {
3124-
append_type(return_value, *list_type);
3187+
append_type(return_value, *list_type, /* object_ce */ intern->ce);
31253188
} ZEND_TYPE_LIST_FOREACH_END();
31263189
} else if (ZEND_TYPE_HAS_NAME(param->type)) {
31273190
zend_string *name = ZEND_TYPE_NAME(param->type);
3128-
append_type(return_value, (zend_type) ZEND_TYPE_INIT_CLASS(name, 0, 0));
3191+
uint32_t type_flags = 0;
3192+
if (ZEND_TYPE_IS_RELATIVE_SELF(param->type)) {
3193+
type_flags = _ZEND_TYPE_SELF_BIT;
3194+
}
3195+
if (ZEND_TYPE_IS_RELATIVE_PARENT(param->type)) {
3196+
type_flags = _ZEND_TYPE_PARENT_BIT;
3197+
}
3198+
append_type(return_value, (zend_type) ZEND_TYPE_INIT_CLASS(name, /* allow_null */ false, /* extra flags */ type_flags), /* object_ce */ intern->ce);
31293199
}
31303200

31313201
type_mask = ZEND_TYPE_PURE_MASK(param->type);
31323202
ZEND_ASSERT(!(type_mask & MAY_BE_VOID));
31333203
ZEND_ASSERT(!(type_mask & MAY_BE_NEVER));
31343204
if (type_mask & MAY_BE_STATIC) {
3135-
append_type_mask(return_value, MAY_BE_STATIC);
3205+
append_type_mask(return_value, MAY_BE_STATIC, /* object_ce */ intern->ce);
31363206
}
31373207
if (type_mask & MAY_BE_CALLABLE) {
3138-
append_type_mask(return_value, MAY_BE_CALLABLE);
3208+
append_type_mask(return_value, MAY_BE_CALLABLE, /* object_ce */ NULL);
31393209
}
31403210
if (type_mask & MAY_BE_OBJECT) {
3141-
append_type_mask(return_value, MAY_BE_OBJECT);
3211+
append_type_mask(return_value, MAY_BE_OBJECT, /* object_ce */ NULL);
31423212
}
31433213
if (type_mask & MAY_BE_ARRAY) {
3144-
append_type_mask(return_value, MAY_BE_ARRAY);
3214+
append_type_mask(return_value, MAY_BE_ARRAY, /* object_ce */ NULL);
31453215
}
31463216
if (type_mask & MAY_BE_STRING) {
3147-
append_type_mask(return_value, MAY_BE_STRING);
3217+
append_type_mask(return_value, MAY_BE_STRING, /* object_ce */ NULL);
31483218
}
31493219
if (type_mask & MAY_BE_LONG) {
3150-
append_type_mask(return_value, MAY_BE_LONG);
3220+
append_type_mask(return_value, MAY_BE_LONG, /* object_ce */ NULL);
31513221
}
31523222
if (type_mask & MAY_BE_DOUBLE) {
3153-
append_type_mask(return_value, MAY_BE_DOUBLE);
3223+
append_type_mask(return_value, MAY_BE_DOUBLE, /* object_ce */ NULL);
31543224
}
31553225
if ((type_mask & MAY_BE_BOOL) == MAY_BE_BOOL) {
3156-
append_type_mask(return_value, MAY_BE_BOOL);
3226+
append_type_mask(return_value, MAY_BE_BOOL, /* object_ce */ NULL);
31573227
} else if (type_mask & MAY_BE_TRUE) {
3158-
append_type_mask(return_value, MAY_BE_TRUE);
3228+
append_type_mask(return_value, MAY_BE_TRUE, /* object_ce */ NULL);
31593229
} else if (type_mask & MAY_BE_FALSE) {
3160-
append_type_mask(return_value, MAY_BE_FALSE);
3230+
append_type_mask(return_value, MAY_BE_FALSE, /* object_ce */ NULL);
31613231
}
31623232
if (type_mask & MAY_BE_NULL) {
3163-
append_type_mask(return_value, MAY_BE_NULL);
3233+
append_type_mask(return_value, MAY_BE_NULL, /* object_ce */ NULL);
31643234
}
31653235
}
31663236
/* }}} */
@@ -3181,7 +3251,7 @@ ZEND_METHOD(ReflectionIntersectionType, getTypes)
31813251

31823252
array_init(return_value);
31833253
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(param->type), list_type) {
3184-
append_type(return_value, *list_type);
3254+
append_type(return_value, *list_type, /* object_ce */ intern->ce);
31853255
} ZEND_TYPE_LIST_FOREACH_END();
31863256
}
31873257
/* }}} */
@@ -3578,7 +3648,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getReturnType)
35783648
RETURN_NULL();
35793649
}
35803650

3581-
reflection_type_factory(fptr->common.arg_info[-1].type, return_value, 1);
3651+
reflection_type_factory(fptr->common.arg_info[-1].type, return_value, /* legacy_behavior */ true, intern->ce);
35823652
}
35833653
/* }}} */
35843654

@@ -3614,7 +3684,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getTentativeReturnType)
36143684
RETURN_NULL();
36153685
}
36163686

3617-
reflection_type_factory(fptr->common.arg_info[-1].type, return_value, 1);
3687+
reflection_type_factory(fptr->common.arg_info[-1].type, return_value, /* legacy_behavior */ true, intern->ce);
36183688
}
36193689
/* }}} */
36203690

@@ -3835,7 +3905,7 @@ ZEND_METHOD(ReflectionClassConstant, getType)
38353905
RETURN_NULL();
38363906
}
38373907

3838-
reflection_type_factory(ref->type, return_value, 1);
3908+
reflection_type_factory(ref->type, return_value, /* legacy_behavior */ true, intern->ce);
38393909
}
38403910

38413911
/* Returns whether class constant has a type */
@@ -5839,7 +5909,7 @@ ZEND_METHOD(ReflectionProperty, getType)
58395909
RETURN_NULL();
58405910
}
58415911

5842-
reflection_type_factory(ref->prop->type, return_value, 1);
5912+
reflection_type_factory(ref->prop->type, return_value, /* legacy_behavior */ true, intern->ce);
58435913
}
58445914
/* }}} */
58455915

@@ -6929,7 +6999,7 @@ ZEND_METHOD(ReflectionEnum, getBackingType)
69296999
RETURN_NULL();
69307000
} else {
69317001
zend_type type = ZEND_TYPE_INIT_CODE(ce->enum_backing_type, 0, 0);
6932-
reflection_type_factory(type, return_value, 0);
7002+
reflection_type_factory(type, return_value, /* legacy_behavior */ false, /* object_ce */ NULL);
69337003
}
69347004
}
69357005

@@ -7189,6 +7259,10 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */
71897259
reflection_named_type_ptr->create_object = reflection_objects_new;
71907260
reflection_named_type_ptr->default_object_handlers = &reflection_object_handlers;
71917261

7262+
reflection_relative_class_type_ptr = register_class_ReflectionRelativeClassType(reflection_named_type_ptr);
7263+
reflection_relative_class_type_ptr->create_object = reflection_objects_new;
7264+
reflection_relative_class_type_ptr->default_object_handlers = &reflection_object_handlers;
7265+
71927266
reflection_union_type_ptr = register_class_ReflectionUnionType(reflection_type_ptr);
71937267
reflection_union_type_ptr->create_object = reflection_objects_new;
71947268
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
@@ -717,6 +717,11 @@ public function getName(): string {}
717717
public function isBuiltin(): bool {}
718718
}
719719

720+
class ReflectionRelativeClassType extends ReflectionNamedType
721+
{
722+
public function resolveToNamedType(): ReflectionNamedType {}
723+
}
724+
720725
class ReflectionUnionType extends ReflectionType
721726
{
722727
public function getTypes(): array {}

ext/reflection/php_reflection_arginfo.h

Lines changed: 21 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)