Skip to content

[4.4] Fix support for PHP8 union types #37341

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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);
Expand Down
27 changes: 17 additions & 10 deletions src/Symfony/Component/DependencyInjection/Dumper/Preloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}
}

Expand All @@ -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);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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] = [];
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Contracts/Service/ServiceLocatorTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -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) : '?';
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Contracts/Service/ServiceSubscriberTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down