Fix support for PHP8 union types
This commit is contained in:
parent
7eca3a5970
commit
da68e66a99
@ -153,9 +153,26 @@ final class CheckTypeDeclarationsPass extends AbstractRecursivePass
|
||||
/**
|
||||
* @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 @@ final class CheckTypeDeclarationsPass extends AbstractRecursivePass
|
||||
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);
|
||||
|
@ -48,7 +48,7 @@ class Preloader
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ class Preloader
|
||||
|
||||
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 @@ class Preloader
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ class RegisterListenersPass implements CompilerPassInterface
|
||||
|| !($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
|
||||
|
@ -45,7 +45,7 @@ final class ArgumentMetadataFactory implements ArgumentMetadataFactoryInterface
|
||||
*/
|
||||
private function getType(\ReflectionParameter $parameter, \ReflectionFunctionAbstract $function): ?string
|
||||
{
|
||||
if (!$type = $parameter->getType()) {
|
||||
if (!($type = $parameter->getType()) instanceof \ReflectionNamedType) {
|
||||
return null;
|
||||
}
|
||||
$name = $type->getName();
|
||||
|
@ -99,7 +99,7 @@ class ErrorListener implements EventSubscriberInterface
|
||||
$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);
|
||||
|
@ -228,11 +228,24 @@ class MessengerPass implements CompilerPassInterface
|
||||
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)
|
||||
|
@ -201,7 +201,7 @@ class OptionsResolver implements Options
|
||||
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] = [];
|
||||
|
@ -87,7 +87,7 @@ trait ServiceLocatorTrait
|
||||
} 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) : '?';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ trait ServiceSubscriberTrait
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user