diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php index cf255640c2258..ae1a2ec204969 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php @@ -153,9 +153,26 @@ private function checkTypeDeclarations(Definition $checkedDefinition, \Reflectio /** * @throws InvalidParameterTypeException When a parameter is not compatible with the declared type */ - private function checkType(Definition $checkedDefinition, $value, \ReflectionParameter $parameter, ?string $envPlaceholderUniquePrefix): void + private function checkType(Definition $checkedDefinition, $value, \ReflectionParameter $parameter, ?string $envPlaceholderUniquePrefix, string $type = null): void { - $type = $parameter->getType()->getName(); + if (null === $type) { + $type = $parameter->getType(); + + if ($type instanceof \ReflectionUnionType) { + foreach ($type->getTypes() as $type) { + try { + $this->checkType($checkedDefinition, $value, $parameter, $envPlaceholderUniquePrefix, $type); + + return; + } catch (InvalidParameterTypeException $e) { + } + } + + throw new InvalidParameterTypeException($this->currentId, $e->getCode(), $parameter); + } + + $type = $type->getName(); + } if ($value instanceof Reference) { if (!$this->container->has($value = (string) $value)) { @@ -266,7 +283,7 @@ private function checkType(Definition $checkedDefinition, $value, \ReflectionPar return; } - $checkFunction = sprintf('is_%s', $parameter->getType()->getName()); + $checkFunction = sprintf('is_%s', $type); if (!$parameter->getType()->isBuiltin() || !$checkFunction($value)) { throw new InvalidParameterTypeException($this->currentId, \is_object($value) ? $class : \gettype($value), $parameter); diff --git a/src/Symfony/Component/DependencyInjection/Dumper/Preloader.php b/src/Symfony/Component/DependencyInjection/Dumper/Preloader.php index abb7d90ff52bc..f9d2716e7d64d 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/Preloader.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/Preloader.php @@ -48,7 +48,7 @@ public static function preload(array $classes) } } - private static function doPreload(string $class, array &$preloaded) + private static function doPreload(string $class, array &$preloaded): void { if (isset($preloaded[$class]) || \in_array($class, ['self', 'static', 'parent'], true)) { return; @@ -68,9 +68,7 @@ private static function doPreload(string $class, array &$preloaded) if (\PHP_VERSION_ID >= 70400) { foreach ($r->getProperties(\ReflectionProperty::IS_PUBLIC) as $p) { - if (($t = $p->getType()) && !$t->isBuiltin()) { - self::doPreload($t->getName(), $preloaded); - } + self::preloadType($p->getType(), $preloaded); } } @@ -84,17 +82,26 @@ private static function doPreload(string $class, array &$preloaded) } } - if (($t = $p->getType()) && !$t->isBuiltin()) { - self::doPreload($t->getName(), $preloaded); - } + self::preloadType($p->getType(), $preloaded); } - if (($t = $m->getReturnType()) && !$t->isBuiltin()) { - self::doPreload($t->getName(), $preloaded); - } + self::preloadType($p->getReturnType(), $preloaded); } } catch (\ReflectionException $e) { // ignore missing classes } } + + private static function preloadType(?\ReflectionType $t, array &$preloaded): void + { + if (!$t || $t->isBuiltin()) { + return; + } + + foreach ($t instanceof \ReflectionUnionType ? $t->getTypes() : [$t] as $t) { + if (!$t->isBuiltin()) { + self::doPreload($t instanceof \ReflectionNamedType ? $t->getName() : $t, $preloaded); + } + } + } } diff --git a/src/Symfony/Component/DependencyInjection/Exception/InvalidParameterTypeException.php b/src/Symfony/Component/DependencyInjection/Exception/InvalidParameterTypeException.php index 206561fa95a8a..05fdb4a779adb 100644 --- a/src/Symfony/Component/DependencyInjection/Exception/InvalidParameterTypeException.php +++ b/src/Symfony/Component/DependencyInjection/Exception/InvalidParameterTypeException.php @@ -21,6 +21,9 @@ class InvalidParameterTypeException extends InvalidArgumentException { public function __construct(string $serviceId, string $type, \ReflectionParameter $parameter) { - 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)); + $acceptedType = $parameter->getType(); + $acceptedType = $acceptedType instanceof \ReflectionNamedType ? $acceptedType->getName() : (string) $acceptedType; + + 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); } } diff --git a/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php b/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php index 3ae1136c4c976..4b27d3b23aef4 100644 --- a/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php +++ b/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php @@ -138,7 +138,7 @@ private function getEventFromTypeDeclaration(ContainerBuilder $container, string || !($r = $container->getReflectionClass($class, false)) || !$r->hasMethod($method) || 1 > ($m = $r->getMethod($method))->getNumberOfParameters() - || !($type = $m->getParameters()[0]->getType()) + || !($type = $m->getParameters()[0]->getType()) instanceof \ReflectionNamedType || $type->isBuiltin() || Event::class === ($name = $type->getName()) || LegacyEvent::class === $name diff --git a/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php b/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php index 9370174c253a0..6c192a6f5f177 100644 --- a/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php +++ b/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php @@ -45,7 +45,7 @@ public function createArgumentMetadata($controller): array */ private function getType(\ReflectionParameter $parameter, \ReflectionFunctionAbstract $function): ?string { - if (!$type = $parameter->getType()) { + if (!($type = $parameter->getType()) instanceof \ReflectionNamedType) { return null; } $name = $type->getName(); diff --git a/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php b/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php index 26c361f754e53..1ca6c9b458e41 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php @@ -99,7 +99,7 @@ public function onControllerArguments(ControllerArgumentsEvent $event) $r = new \ReflectionFunction(\Closure::fromCallable($event->getController())); $r = $r->getParameters()[$k] ?? null; - if ($r && (!$r->hasType() || \in_array($r->getType()->getName(), [FlattenException::class, LegacyFlattenException::class], true))) { + if ($r && (!($r = $r->getType()) instanceof \ReflectionNamedType || \in_array($r->getName(), [FlattenException::class, LegacyFlattenException::class], true))) { $arguments = $event->getArguments(); $arguments[$k] = FlattenException::createFromThrowable($e); $event->setArguments($arguments); diff --git a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php index bf62b4c87e953..888155dea994b 100644 --- a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php +++ b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php @@ -228,11 +228,24 @@ private function guessHandledClasses(\ReflectionClass $handlerClass, string $ser 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())); } + if ($type instanceof \ReflectionUnionType) { + $types = []; + foreach ($type->getTypes() as $type) { + if (!$type->isBuiltin()) { + $types[] = (string) $type; + } + } + + if ($types) { + return $types; + } + } + if ($type->isBuiltin()) { 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)); } - return [$parameters[0]->getType()->getName()]; + return [$type->getName()]; } private function registerReceivers(ContainerBuilder $container, array $busIds) diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index e40583dd5f03c..3fb3c06f92cd9 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -201,7 +201,7 @@ public function setDefault($option, $value) return $this; } - 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()))) { + 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()))) { // Store closure for later evaluation $this->nested[$option][] = $value; $this->defaults[$option] = []; diff --git a/src/Symfony/Contracts/Service/ServiceLocatorTrait.php b/src/Symfony/Contracts/Service/ServiceLocatorTrait.php index 0b4d60affa5e1..1737f50e997ab 100644 --- a/src/Symfony/Contracts/Service/ServiceLocatorTrait.php +++ b/src/Symfony/Contracts/Service/ServiceLocatorTrait.php @@ -87,7 +87,7 @@ public function getProvidedServices(): array } else { $type = (new \ReflectionFunction($factory))->getReturnType(); - $this->providedTypes[$name] = $type ? ($type->allowsNull() ? '?' : '').$type->getName() : '?'; + $this->providedTypes[$name] = $type ? ($type->allowsNull() ? '?' : '').($type instanceof \ReflectionNamedType ? $type->getName() : $type) : '?'; } } } diff --git a/src/Symfony/Contracts/Service/ServiceSubscriberTrait.php b/src/Symfony/Contracts/Service/ServiceSubscriberTrait.php index 5d9d456d0c6d1..82fb5ab361559 100644 --- a/src/Symfony/Contracts/Service/ServiceSubscriberTrait.php +++ b/src/Symfony/Contracts/Service/ServiceSubscriberTrait.php @@ -40,7 +40,7 @@ public static function getSubscribedServices(): array } if (self::class === $method->getDeclaringClass()->name && ($returnType = $method->getReturnType()) && !$returnType->isBuiltin()) { - $services[self::class.'::'.$method->name] = '?'.$returnType->getName(); + $services[self::class.'::'.$method->name] = '?'.($returnType instanceof \ReflectionNamedType ? $returnType->getName() : $type); } }
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: