10000 Fix support for PHP8 union types · symfony/symfony@0110a47 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0110a47

Browse files
Fix support for PHP8 union types
1 parent 943c630 commit 0110a47

File tree

16 files changed

+123
-63
lines changed

16 files changed

+123
-63
lines changed

src/Symfony/Component/Config/Resource/ReflectionClassResource.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ private function generateSignature(\ReflectionClass $class): iterable
167167
if (!$parametersWithUndefinedConstants) {
168168
yield preg_replace('/^ @@.*/m', '', $m);
169169
} else {
170+
$t = $m->getReturnType();
170171
$stack = [
171172
$m->getDocComment(),
172173
$m->getName(),
@@ -177,15 +178,16 @@ private function generateSignature(\ReflectionClass $class): iterable
177178
$m->isPrivate(),
178179
$m->isProtected(),
179180
$m->returnsReference(),
180-
$m->hasReturnType() ? $m->getReturnType()->getName() : '',
181+
$t instanceof \ReflectionNamedType ? ((string) $t->allowsNull()).$t->getName() : (string) $t,
181182
];
182183

183184
foreach ($m->getParameters() as $p) {
184185
if (!isset($parametersWithUndefinedConstants[$p->name])) {
185186
$stack[] = (string) $p;
186187
} else {
188+
$t = $p->getType();
187189
$stack[] = $p->isOptional();
188-
$stack[] = $p->hasType() ? $p->getType()->getName() : '';
190+
$stack[] = $t instanceof \ReflectionNamedType ? ((string) $t->allowsNull()).$t->getName() : (string) $t;
189191
$stack[] = $p->isPassedByReference();
190192
$stack[] = $p->isVariadic();
191193
$stack[] = $p->getName();

src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,25 @@ private function checkTypeDeclarations(Definition $checkedDefinition, \Reflectio
153153
/**
154154
* @throws InvalidParameterTypeException When a parameter is not compatible with the declared type
155155
*/
156-
private function checkType(Definition $checkedDefinition, $value, \ReflectionParameter $parameter, ?string $envPlaceholderUniquePrefix): void
156+
private function checkType(Definition $checkedDefinition, $value, \ReflectionParameter $parameter, ?string $envPlaceholderUniquePrefix, string $type = null): void
157157
{
158-
$type = $parameter->getType()->getName();
158+
if (null === $type) {
159+
$type = $parameter->getType();
160+
161+
if ($type instanceof \ReflectionUnionType) {
162+
foreach ($type->getTypes() as $type) {
163+
try {
164+
$this->checkType($checkedDefinition, $value, $parameter, $envPlaceholderUniquePrefix, $type);
165+
return;
166+
} catch (InvalidParameterTypeException $e) {
167+
}
168+
}
169+
170+
throw new InvalidParameterTypeException($this->currentId, $e->getCode(), $parameter);
171+
}
172+
173+
$type = $type->getName();
174+
}
159175

160176
if ($value instanceof Reference) {
161177
if (!$this->container->has($value = (string) $value)) {
@@ -266,7 +282,7 @@ private function checkType(Definition $checkedDefinition, $value, \ReflectionPar
266282
return;
267283
}
268284

269-
$checkFunction = sprintf('is_%s', $parameter->getType()->getName());
285+
$checkFunction = sprintf('is_%s', $type);
270286

271287
if (!$parameter->getType()->isBuiltin() || !$checkFunction($value)) {
272288
throw new InvalidParameterTypeException($this->currentId, \is_object($value) ? $class : \gettype($value), $parameter);

src/Symfony/Component/DependencyInjection/Dumper/Preloader.php

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public static function preload(array $classes)
4848
}
4949
}
5050

51-
private static function doPreload(string $class, array &$preloaded)
51+
private static function doPreload(string $class, array &$preloaded): void
5252
{
5353
if (isset($preloaded[$class]) || \in_array($class, ['self', 'static', 'parent'], true)) {
5454
return;
@@ -68,9 +68,7 @@ private static function doPreload(string $class, array &$preloaded)
6868

6969
if (\PHP_VERSION_ID >= 70400) {
7070
foreach ($r->getProperties(\ReflectionProperty::IS_PUBLIC) as $p) {
71-
if (($t = $p->getType()) && !$t->isBuiltin()) {
72-
self::doPreload($t->getName(), $preloaded);
73-
}
71+
self::preloadType($p->getType(), $preloaded);
7472
}
7573
}
7674

@@ -84,17 +82,26 @@ private static function doPreload(string $class, array &$preloaded)
8482
}
8583
}
8684

87-
if (($t = $p->getType()) && !$t->isBuiltin()) {
88-
self::doPreload($t->getName(), $preloaded);
89-
}
85+
self::preloadType($p->getType(), $preloaded);
9086
}
9187

92-
if (($t = $m->getReturnType()) && !$t->isBuiltin()) {
93-
self::doPreload($t->getName(), $preloaded);
94-
}
88+
self::preloadType($p->getReturnType(), $preloaded);
9589
}
9690
} catch (\ReflectionException $e) {
9791
// ignore missing classes
9892
}
9993
}
94+
95+
private static function preloadType(?\ReflectionType $t, array &$preloaded): void
96+
{
97+
if (!$t || $t->isBuiltin()) {
98+
return;
99+
}
100+
101+
foreach ($t instanceof \ReflectionUnionType ? $t->getTypes() : [$t] as $t) {
102+
if (!$t->isBuiltin()) {
103+
self::doPreload($t instanceof \ReflectionNamedType ? $t->getName() : $t, $preloaded);
104+
}
105+
}
106+
}
100107
}

src/Symfony/Component/DependencyInjection/Exception/InvalidParameterTypeException.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ class InvalidParameterTypeException extends InvalidArgumentException
2121
{
2222
public function __construct(string $serviceId, string $type, \ReflectionParameter $parameter)
2323
{
24-
parent::__construct(sprintf('Invalid definition for service "%s": argument %d of "%s::%s" accepts "%s", "%s" passed.', $serviceId, 1 + $parameter->getPosition(), $parameter->getDeclaringClass()->getName(), $parameter->getDeclaringFunction()->getName(), $parameter->getType()->getName(), $type));
24+
$acceptedType = $parameter->getType();
25+
$acceptedType = $acceptedType instanceof \ReflectionNamedType ? $acceptedType->getName() : (string) $acceptedType;
26+
27+
parent::__construct(sprintf('Invalid definition for service "%s": argument %d of "%s::%s" accepts "%s", "%s" passed.', $serviceId, 1 + $parameter->getPosition(), $parameter->getDeclaringClass()->getName(), $parameter->getDeclaringFunction()->getName(), $acceptedType, $type), $type);
2528
}
2629
}

src/Symfony/Component/DependencyInjection/LazyProxy/ProxyHelper.php

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,26 +31,36 @@ public static function getTypeHint(\ReflectionFunctionAbstract $r, \ReflectionPa
3131
if (!$type) {
3232
return null;
3333
}
34-
if (!\is_string($type)) {
35-
$name = $type->getName();
34+
35+
$types = [];
36+
37+
foreach ($type instanceof \ReflectionUnionType ? $type->getTypes() : [$type] as $type) {
38+
$name = $type instanceof \ReflectionNamedType ? $type->getName() : (string) $type;
3639

3740
if ($type->isBuiltin()) {
38-
return $noBuiltin ? null : $name;
41+
if (!$noBuiltin) {
42+
$types[] = $name;
43+
}
44+
continue;
3945
}
40-
}
41-
$lcName = strtolower($name);
42-
$prefix = $noBuiltin ? '' : '\\';
4346

44-
if ('self' !== $lcName && 'parent' !== $lcName) {
45-
return $prefix.$name;
46-
}
47-
if (!$r instanceof \ReflectionMethod) {
48-
return null;
49-
}
50-
if ('self' === $lcName) {
51-
return $prefix.$r->getDeclaringClass()->name;
47+
$lcName = strtolower($name);
48+
$prefix = $noBuiltin ? '' : '\\';
49+
50+
if ('self' !== $lcName && 'parent' !== $lcName) {
51+
$types[] = '' !== $prefix ? $prefix.$name : $name;
52+
continue;
53+
}
54+
if (!$r instanceof \ReflectionMethod) {
55+
continue;
56+
}
57+
if ('self' === $lcName) {
58+
$types[] = $prefix.$r->getDeclaringClass()->name;
59+
} else {
60+
$types[] = ($parent = $r->getDeclaringClass()->getParentClass()) ? $prefix.$parent->name : null;
61+
}
5262
}
5363

54-
return ($parent = $r->getDeclaringClass()->getParentClass()) ? $prefix.$parent->name : null;
64+
return $types ? implode('|', $types) : null;
5565
}
5666
}

src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ private function getEventFromTypeDeclaration(ContainerBuilder $container, string
138138
|| !($r = $container->getReflectionClass($class, false))
139139
|| !$r->hasMethod($method)
140140
|| 1 > ($m = $r->getMethod($method))->getNumberOfParameters()
141-
|| !($type = $m->getParameters()[0]->getType())
141+
|| !($type = $m->getParameters()[0]->getType()) instanceof \ReflectionNamedType
142142
|| $type->isBuiltin()
143143
|| Event::class === ($name = $type->getName())
144144
|| LegacyEvent::class === $name

src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public function createArgumentMetadata($controller): array
4545
*/
4646
private function getType(\ReflectionParameter $parameter, \ReflectionFunctionAbstract $function): ?string
4747
{
48-
if (!$type = $parameter->getType()) {
48+
if (!($type = $parameter->getType()) instanceof \ReflectionNamedType) {
4949
return null;
5050
}
5151
$name = $type->getName();

src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public function onControllerArguments(ControllerArgumentsEvent $event)
9999
$r = new \ReflectionFunction(\Closure::fromCallable($event->getController()));
100100
$r = $r->getParameters()[$k] ?? null;
101101

102-
if ($r && (!$r->hasType() || \in_array($r->getType()->getName(), [FlattenException::class, LegacyFlattenException::class], true))) {
102+
if ($r && (!($r = $r->getType()) instanceof \ReflectionNamedType || \in_array($r->getName(), [FlattenException::class, LegacyFlattenException::class], true))) {
103103
$arguments = $event->getArguments();
104104
$arguments[$k] = FlattenException::createFromThrowable($e);
105105
$event->setArguments($arguments);

src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,11 +228,24 @@ private function guessHandledClasses(\ReflectionClass $handlerClass, string $ser
228228
throw new RuntimeException(sprintf('Invalid handler service "%s": argument "$%s" of method "%s::__invoke()" must have a type-hint corresponding to the message class it handles.', $serviceId, $parameters[0]->getName(), $handlerClass->getName()));
229229
}
230230

231+
if ($type instanceof \ReflectionUnionType) {
232+
$types = [];
233+
foreach ($type->getTypes() as $type) {
234+
if (!$type->isBuiltin()) {
235+
$types[] = (string) $type;
236+
}
237+
}
238+
239+
if ($types) {
240+
return $types;
241+
}
242+
}
243+
231244
if ($type->isBuiltin()) {
232245
throw new RuntimeException(sprintf('Invalid handler service "%s": type-hint of argument "$%s" in method "%s::__invoke()" must be a class , "%s" given.', $serviceId, $parameters[0]->getName(), $handlerClass->getName(), $type instanceof \ReflectionNamedType ? $type->getName() : (string) $type));
233246
}
234247

235-
return [$parameters[0]->getType()->getName()];
248+
return [$type->getName()];
236249
}
237250

238251
private function registerReceivers(ContainerBuilder $container, array $busIds)

src/Symfony/Component/OptionsResolver/OptionsResolver.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ public function setDefault($option, $value)
201201
return $this;
202202
}
203203

204-
if (isset($params[0]) && null !== ($type = $params[0]->getType()) && self::class === $type->getName() && (!isset($params[1]) || (null !== ($type = $params[1]->getType()) && Options::class === $type->getName()))) {
204+
if (isset($params[0]) && null !== ($type = $params[0]->getType()) && self::class === $type->getName() && (!isset($params[1]) || (($type = $params[1]->getType()) instanceof \ReflectionNamedType && Options::class === $type->getName()))) {
205205
// Store closure for later evaluation
206206
$this->nested[$option][] = $value;
207207
$this->defaults[$option] = [];
@@ -1220,7 +1220,7 @@ private function formatOptions(array $options): string
12201220

12211221
private function getParameterClassName(\ReflectionParameter $parameter): ?string
12221222
{
1223-
if (!($type = $parameter->getType()) || $type->isBuiltin()) {
1223+
if (!($type = $parameter->getType()) instanceof \ReflectionNamedType || $type->isBuiltin()) {
12241224
return null;
12251225
}
12261226

src/Symfony/Component/PropertyAccess/PropertyAccessor.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,8 +432,9 @@ private function readProperty(array $zval, string $property, bool $ignoreInvalid
432432
// handle uninitialized properties in PHP >= 7.4
433433
if (\PHP_VERSION_ID >= 70400 && preg_match('/^Typed property ([\w\\\]+)::\$(\w+) must not be accessed before initialization$/', $e->getMessage(), $matches)) {
434434
$r = new \ReflectionProperty($matches[1], $matches[2]);
435+
$type = ($type = $r->getType()) instanceof \ReflectionNamedType ? $type->getName() : (string) $type;
435436

436-
throw new AccessException(sprintf('The property "%s::$%s" is not readable because it is typed "%s". You should initialize it or declare a default value instead.', $r->getDeclaringClass()->getName(), $r->getName(), $r->getType()->getName()), 0, $e);
437+
throw new AccessException(sprintf('The property "%s::$%s" is not readable because it is typed "%s". You should initialize it or declare a default value instead.', $r->getDeclaringClass()->getName(), $r->getName(), $type), 0, $e);
437438
}
438439

439440
throw $e;

src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -235,11 +235,11 @@ private function extractFromMutator(string $class, string $property): ?array
235235
}
236236
$type = $this->extractFromReflectionType($reflectionType, $reflectionMethod);
237237

238-
if (\in_array($prefix, $this->arrayMutatorPrefixes)) {
239-
$type = new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $type);
238+
if (1 === \count($type) && \in_array($prefix, $this->arrayMutatorPrefixes)) {
239+
$type = [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $type[0])];
240240
}
241241

242-
return [$type];
242+
return $type;
243243
}
244244

245245
/**
@@ -255,7 +255,7 @@ private function extractFromAccessor(string $class, string $property): ?array
255255
}
256256

257257
if ($reflectionType = $reflectionMethod->getReturnType()) {
258-
return [$this->extractFromReflectionType($reflectionType, $reflectionMethod)];
258+
return $this->extractFromReflectionType($reflectionType, $reflectionMethod);
259259
}
260260

261261
if (\in_array($prefix, ['is', 'can', 'has'])) {
@@ -290,7 +290,7 @@ private function extractFromConstructor(string $class, string $property): ?array
290290
}
291291
$reflectionType = $parameter->getType();
292292

293-
return $reflectionType ? [$this->extractFromReflectionType($reflectionType, $constructor)] : null;
293+
return $reflectionType ? $this->extractFromReflectionType($reflectionType, $constructor) : null;
294294
}
295295

296296
if ($parentClass = $reflectionClass->getParentClass()) {
@@ -319,22 +319,26 @@ private function extractFromDefaultValue(string $class, string $property): ?arra
319319
return [new Type(static::MAP_TYPES[$type] ?? $type)];
320320
}
321321

322-
private function extractFromReflectionType(\ReflectionType $reflectionType, \ReflectionMethod $reflectionMethod): Type
322+
private function extractFromReflectionType(\ReflectionType $reflectionType, \ReflectionMethod $reflectionMethod): array
323323
{
324-
$phpTypeOrClass = $reflectionType->getName();
324+
$types = [];
325325
$nullable = $reflectionType->allowsNull();
326326

327-
if (Type::BUILTIN_TYPE_ARRAY === $phpTypeOrClass) {
328-
$type = new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true);
329-
} elseif ('void' === $phpTypeOrClass) {
330-
$type = new Type(Type::BUILTIN_TYPE_NULL, $nullable);
331-
} elseif ($reflectionType->isBuiltin()) {
332-
$type = new Type($phpTypeOrClass, $nullable);
333-
} else {
334-
$type = new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $this->resolveTypeName($phpTypeOrClass, $reflectionMethod));
327+
foreach ($reflectionType instanceof \ReflectionUnionType ? $reflectionType->getTypes() : [$reflectionType] as $type) {
328+
$phpTypeOrClass = $reflectionType instanceof \ReflectionNamedType ? $reflectionType->getName() : (string) $type;
329+
330+
if (Type::BUILTIN_TYPE_ARRAY === $phpTypeOrClass) {
331+
$types[] = new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true);
332+
} elseif ('void' === $phpTypeOrClass || 'null' === $phpTypeOrClass) {
333+
$types[] = new Type(Type::BUILTIN_TYPE_NULL, $nullable);
334+
} elseif ($reflectionType->isBuiltin()) {
335+
$types[] = new Type($phpTypeOrClass, $nullable);
336+
} else {
337+
$types[] = new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $this->resolveTypeName($phpTypeOrClass, $reflectionMethod));
338+
}
335339
}
336340

337-
return $type;
341+
return $types;
338342
}
339343

340344
private function resolveTypeName(string $name, \ReflectionMethod $reflectionMethod): string

src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,7 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref
522522
protected function denormalizeParameter(\ReflectionClass $class, \ReflectionParameter $parameter, $parameterName, $parameterData, array $context, $format = null)
523523
{
524524
try {
525-
if (($parameterType = $parameter->getType()) && !$parameterType->isBuiltin()) {
525+
if (($parameterType = $parameter->getType()) instanceof \ReflectionNamedType && !$parameterType->isBuiltin()) {
526526
$parameterClass = $parameterType->getName();
527527
new \ReflectionClass($parameterClass); // throws a \ReflectionException if the class doesn't exist
528528

src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public static function castType(\ReflectionType $c, array $a, Stub $stub, $isNes
9797
$prefix = Caster::PREFIX_VIRTUAL;
9898

9999
$a += [
100-
$prefix.'name' => $c->getName(),
100+
$prefix.'name' => $c instanceof \ReflectionNamedType ? $c->getName() : (string) $c,
101101
$prefix.'allowsNull' => $c->allowsNull(),
102102
$prefix.'isBuiltin' => $c->isBuiltin(),
103103
];
@@ -182,7 +182,7 @@ public static function castFunctionAbstract(\ReflectionFunctionAbstract $c, arra
182182

183183
if (isset($a[$prefix.'returnType'])) {
184184
$v = $a[$prefix.'returnType'];
185-
$v = $v->getName();
185+
$v = $v instanceof \ReflectionNamedType ? $v->getName() : (string) $v;
186186
$a[$prefix.'returnType'] = new ClassStub($a[$prefix.'returnType']->allowsNull() ? '?'.$v : $v, [class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', '']);
187187
}
188188
if (isset($a[$prefix.'class'])) {
@@ -244,7 +244,7 @@ public static function castParameter(\ReflectionParameter $c, array $a, Stub $st
244244
]);
245245

246246
if ($v = $c->getType()) {
247-
$a[$prefix.'typeHint'] = $v->getName();
247+
$a[$prefix.'typeHint'] = $v instanceof \ReflectionNamedType ? $v->getName() : (string) $v;
248248
}
249249

250250
if (isset($a[$prefix.'typeHint'])) {
@@ -320,10 +320,14 @@ public static function getSignature(array $a)
320320
foreach ($a[$prefix.'parameters']->value as $k => $param) {
321321
$signature .= ', ';
322322
if ($type = $param->getType()) {
323-
if (!$param->isOptional() && $param->allowsNull()) {
324-
$signature .= '?';
323+
if (!$type instanceof \ReflectionNamedType) {
324+
$signature .= $type.' ';
325+
} else {
326+
if (!$param->isOptional() && $param->allowsNull()) {
327 B2AB +
$signature .= '?';
328+
}
329+
$signature .= substr(strrchr('\\'.$type->getName(), '\\'), 1);
325330
}
326-
$signature .= substr(strrchr('\\'.$type->getName(), '\\'), 1).' ';
327331
}
328332
$signature .= $k;
329333

src/Symfony/Contracts/Service/ServiceLocatorTrait.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public function getProvidedServices(): array
8787
} else {
8888
$type = (new \ReflectionFunction($factory))->getReturnType();
8989

90-
$this->providedTypes[$name] = $type ? ($type->allowsNull() ? '?' : '').$type->getName() : '?';
90+
$this->providedTypes[$name] = $type ? ($type->allowsNull() ? '?' : '').($type instanceof \ReflectionNamedType ? $type->getName() : $type) : '?';
9191
}
9292
}
9393
}

0 commit comments

Comments
 (0)
0