Skip to content

Commit bbdb793

Browse files
committed
Reflection iterable BC for named types
1 parent cc0291c commit bbdb793

File tree

3 files changed

+101
-29
lines changed

3 files changed

+101
-29
lines changed

ext/reflection/php_reflection.c

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1330,7 +1330,7 @@ typedef enum {
13301330
} reflection_type_kind;
13311331

13321332
/* For backwards compatibility reasons, we need to return T|null style unions
1333-
* ~~and transformation from iterable to Traversable|array~~
1333+
* and transformation from iterable to Traversable|array
13341334
* as a ReflectionNamedType. Here we determine what counts as a union type and
13351335
* what doesn't. */
13361336
static reflection_type_kind get_type_kind(zend_type type) {
@@ -1346,11 +1346,9 @@ static reflection_type_kind get_type_kind(zend_type type) {
13461346

13471347
if (ZEND_TYPE_IS_COMPLEX(type)) {
13481348
/* BC support for 'iterable' type */
1349-
/*
13501349
if (UNEXPECTED(ZEND_TYPE_IS_ITERABLE_FALLBACK(type))) {
13511350
return NAMED_TYPE;
13521351
}
1353-
*/
13541352
if (type_mask_without_null != 0) {
13551353
return UNION_TYPE;
13561354
}
@@ -3018,9 +3016,21 @@ ZEND_METHOD(ReflectionType, allowsNull)
30183016
}
30193017
/* }}} */
30203018

3019+
/* For BC with iterable for named types */
3020+
static zend_string *zend_named_reflection_type_to_string(zend_type type) {
3021+
if (ZEND_TYPE_IS_ITERABLE_FALLBACK(type)) {
3022+
zend_string *iterable = ZSTR_KNOWN(ZEND_STR_ITERABLE);
3023+
if (ZEND_TYPE_FULL_MASK(type) & MAY_BE_NULL) {
3024+
return zend_string_concat2("?", strlen("?"), ZSTR_VAL(iterable), ZSTR_LEN(iterable));
3025+
}
3026+
return iterable;
3027+
}
3028+
return zend_type_to_string(type);
3029+
}
3030+
30213031
static zend_string *zend_type_to_string_without_null(zend_type type) {
30223032
ZEND_TYPE_FULL_MASK(type) &= ~MAY_BE_NULL;
3023-
return zend_type_to_string(type);
3033+
return zend_named_reflection_type_to_string(type);
30243034
}
30253035

30263036
/* {{{ Return the text of the type hint */
@@ -3034,7 +3044,7 @@ ZEND_METHOD(ReflectionType, __toString)
30343044
}
30353045
GET_REFLECTION_OBJECT_PTR(param);
30363046

3037-
RETURN_STR(zend_type_to_string(param->type));
3047+
RETURN_STR(zend_named_reflection_type_to_string(param->type));
30383048
}
30393049
/* }}} */
30403050

@@ -3052,7 +3062,7 @@ ZEND_METHOD(ReflectionNamedType, getName)
30523062
if (param->legacy_behavior) {
30533063
RETURN_STR(zend_type_to_string_without_null(param->type));
30543064
}
3055-
RETURN_STR(zend_type_to_string(param->type));
3065+
RETURN_STR(zend_named_reflection_type_to_string(param->type));
30563066
}
30573067
/* }}} */
30583068

@@ -3067,6 +3077,10 @@ ZEND_METHOD(ReflectionNamedType, isBuiltin)
30673077
}
30683078
GET_REFLECTION_OBJECT_PTR(param);
30693079

3080+
if (ZEND_TYPE_IS_ITERABLE_FALLBACK(param->type)) {
3081+
RETURN_TRUE;
3082+
}
3083+
30703084
/* Treat "static" as a class type for the purposes of reflection. */
30713085
RETVAL_BOOL(ZEND_TYPE_IS_ONLY_MASK(param->type)
30723086
&& !(ZEND_TYPE_FULL_MASK(param->type) & MAY_BE_STATIC));
@@ -3075,6 +3089,11 @@ ZEND_METHOD(ReflectionNamedType, isBuiltin)
30753089

30763090
static void append_type(zval *return_value, zend_type type) {
30773091
zval reflection_type;
3092+
/* Drop iterable BC bit for type list */
3093+
if (ZEND_TYPE_IS_ITERABLE_FALLBACK(type)) {
3094+
ZEND_TYPE_FULL_MASK(type) &= ~_ZEND_TYPE_ITERABLE_BIT;
3095+
}
3096+
30783097
reflection_type_factory(type, &reflection_type, 0);
30793098
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &reflection_type);
30803099
}

ext/reflection/tests/bug72661.phpt

Lines changed: 0 additions & 10 deletions
This file was deleted.

ext/reflection/tests/iterable_Reflection.phpt

Lines changed: 76 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,96 @@ iterable Type in Reflection
33
--FILE--
44
<?php
55

6-
$function = function(): iterable {};
6+
$function = function(iterable $arg): iterable {};
7+
8+
$paramType = (new ReflectionParameter($function, 0))->getType();
9+
var_dump($paramType::class);
10+
var_dump($paramType);
11+
var_dump($paramType->getName());
12+
var_dump((string) $paramType);
13+
var_dump($paramType->isBuiltin());
714

815
$reflectionFunc = new ReflectionFunction($function);
916
$returnType = $reflectionFunc->getReturnType();
1017
var_dump($returnType::class);
11-
foreach ($returnType->getTypes() as $type) {
12-
var_dump($type->getName());
13-
}
18+
var_dump($returnType);
19+
var_dump($returnType->getName());
20+
var_dump((string) $returnType);
21+
var_dump($returnType->isBuiltin());
1422

1523
class PropIterableTypeTest {
1624
public iterable $iterable;
25+
public ?iterable $nullableIterable;
26+
public array $control;
27+
public ?array $nullableControl;
1728
}
1829

1930
$reflector = new ReflectionClass(PropIterableTypeTest::class);
2031

21-
[$property] = $reflector->getProperties();
32+
[$property, $nullable, $control, $nullableControl] = $reflector->getProperties();
2233
$iterableType = $property->getType();
2334
var_dump($iterableType::class);
24-
foreach ($iterableType->getTypes() as $type) {
25-
var_dump($type->getName());
26-
}
35+
var_dump($iterableType);
36+
var_dump($iterableType->getName());
37+
var_dump((string) $iterableType);
38+
var_dump($iterableType->isBuiltin());
39+
40+
$nullableIterableType = $nullable->getType();
41+
var_dump($nullableIterableType::class);
42+
var_dump($nullableIterableType);
43+
var_dump($nullableIterableType->getName());
44+
var_dump((string) $nullableIterableType);
45+
var_dump($nullableIterableType->isBuiltin());
46+
47+
$controlType = $control->getType();
48+
var_dump($controlType::class);
49+
var_dump($controlType);
50+
var_dump($controlType->getName());
51+
var_dump((string) $controlType);
52+
var_dump($controlType->isBuiltin());
53+
54+
$nullableControlType = $nullableControl->getType();
55+
var_dump($nullableControlType::class);
56+
var_dump($nullableControlType);
57+
var_dump($nullableControlType->getName());
58+
var_dump((string) $nullableControlType);
59+
var_dump($nullableControlType->isBuiltin());
2760

2861
?>
29-
--EXPECT--
30-
string(19) "ReflectionUnionType"
31-
string(11) "Traversable"
62+
--EXPECTF--
63+
string(19) "ReflectionNamedType"
64+
object(ReflectionNamedType)#%d (0) {
65+
}
66+
string(8) "iterable"
67+
string(8) "iterable"
68+
bool(true)
69+
string(19) "ReflectionNamedType"
70+
object(ReflectionNamedType)#%d (0) {
71+
}
72+
string(8) "iterable"
73+
string(8) "iterable"
74+
bool(true)
75+
string(19) "ReflectionNamedType"
76+
object(ReflectionNamedType)#%d (0) {
77+
}
78+
string(8) "iterable"
79+
string(8) "iterable"
80+
bool(true)
81+
string(19) "ReflectionNamedType"
82+
object(ReflectionNamedType)#%d (0) {
83+
}
84+
string(8) "iterable"
85+
string(9) "?iterable"
86+
bool(true)
87+
string(19) "ReflectionNamedType"
88+
object(ReflectionNamedType)#%d (0) {
89+
}
90+
string(5) "array"
3291
string(5) "array"
33-
string(19) "ReflectionUnionType"
34-
string(11) "Traversable"
92+
bool(true)
93+
string(19) "ReflectionNamedType"
94+
object(ReflectionNamedType)#%d (0) {
95+
}
3596
string(5) "array"
97+
string(6) "?array"
98+
bool(true)

0 commit comments

Comments
 (0)