From 69a115c2544936becf8e0d6647b02ac5cc2db68d Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 30 Nov 2023 19:26:02 +0100 Subject: [PATCH] [DependencyInjection] Fix parsing named autowiring aliases that contain underscores --- .../DependencyInjection/Attribute/Target.php | 6 ++++-- .../Compiler/AutowirePass.php | 20 ++++++++++++++----- .../Compiler/ResolveBindingsPass.php | 13 +++++++----- .../Tests/Compiler/AutowirePassTest.php | 12 +++++++++++ .../RegisterServiceSubscribersPassTest.php | 2 +- .../Fixtures/includes/autowiring_classes.php | 8 ++++++++ ...RegisterControllerArgumentLocatorsPass.php | 8 +++++++- 7 files changed, 55 insertions(+), 14 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Attribute/Target.php b/src/Symfony/Component/DependencyInjection/Attribute/Target.php index c3f22127bc847..6fbb3ad42b6a4 100644 --- a/src/Symfony/Component/DependencyInjection/Attribute/Target.php +++ b/src/Symfony/Component/DependencyInjection/Attribute/Target.php @@ -36,10 +36,12 @@ public function getParsedName(): string return lcfirst(str_replace(' ', '', ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $this->name)))); } - public static function parseName(\ReflectionParameter $parameter, self &$attribute = null): string + public static function parseName(\ReflectionParameter $parameter, self &$attribute = null, string &$parsedName = null): string { $attribute = null; if (!$target = $parameter->getAttributes(self::class)[0] ?? null) { + $parsedName = (new self($parameter->name))->getParsedName(); + return $parameter->name; } @@ -57,6 +59,6 @@ public static function parseName(\ReflectionParameter $parameter, self &$attribu throw new InvalidArgumentException(sprintf('Invalid #[Target] name "%s" on parameter "$%s" of "%s()": the first character must be a letter.', $name, $parameter->name, $function)); } - return $parsedName; + return preg_match('/^[a-zA-Z0-9_\x7f-\xff]++$/', $name) ? $name : $parsedName; } } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index ee3ba948b5126..9786ec4de2082 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -454,20 +454,30 @@ private function getAutowiredReference(TypedReference $reference, bool $filterTy $name = $target = (array_filter($reference->getAttributes(), static fn ($a) => $a instanceof Target)[0] ?? null)?->name; if (null !== $name ??= $reference->getName()) { + if ($this->container->has($alias = $type.' $'.$name) && !$this->container->findDefinition($alias)->isAbstract()) { + return new TypedReference($alias, $type, $reference->getInvalidBehavior()); + } + + if (null !== ($alias = $this->getCombinedAlias($type, $name)) && !$this->container->findDefinition($alias)->isAbstract()) { + return new TypedReference($alias, $type, $reference->getInvalidBehavior()); + } + $parsedName = (new Target($name))->getParsedName(); if ($this->container->has($alias = $type.' $'.$parsedName) && !$this->container->findDefinition($alias)->isAbstract()) { return new TypedReference($alias, $type, $reference->getInvalidBehavior()); } - if (null !== ($alias = $this->getCombinedAlias($type, $parsedName) ?? null) && !$this->container->findDefinition($alias)->isAbstract()) { + if (null !== ($alias = $this->getCombinedAlias($type, $parsedName)) && !$this->container->findDefinition($alias)->isAbstract()) { return new TypedReference($alias, $type, $reference->getInvalidBehavior()); } - if ($this->container->has($name) && !$this->container->findDefinition($name)->isAbstract()) { + if (($this->container->has($n = $name) && !$this->container->findDefinition($n)->isAbstract()) + || ($this->container->has($n = $parsedName) && !$this->container->findDefinition($n)->isAbstract()) + ) { foreach ($this->container->getAliases() as $id => $alias) { - if ($name === (string) $alias && str_starts_with($id, $type.' $')) { - return new TypedReference($name, $type, $reference->getInvalidBehavior()); + if ($n === (string) $alias && str_starts_with($id, $type.' $')) { + return new TypedReference($n, $type, $reference->getInvalidBehavior()); } } } @@ -481,7 +491,7 @@ private function getAutowiredReference(TypedReference $reference, bool $filterTy return new TypedReference($type, $type, $reference->getInvalidBehavior()); } - if (null !== ($alias = $this->getCombinedAlias($type) ?? null) && !$this->container->findDefinition($alias)->isAbstract()) { + if (null !== ($alias = $this->getCombinedAlias($type)) && !$this->container->findDefinition($alias)->isAbstract()) { return new TypedReference($alias, $type, $reference->getInvalidBehavior()); } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php index 68835d52aaec9..2fa7db848c599 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php @@ -190,16 +190,19 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed $typeHint = ltrim(ProxyHelper::exportType($parameter) ?? '', '?'); - $name = Target::parseName($parameter); + $name = Target::parseName($parameter, parsedName: $parsedName); - if ($typeHint && \array_key_exists($k = preg_replace('/(^|[(|&])\\\\/', '\1', $typeHint).' $'.$name, $bindings)) { + if ($typeHint && ( + \array_key_exists($k = preg_replace('/(^|[(|&])\\\\/', '\1', $typeHint).' $'.$name, $bindings) + || \array_key_exists($k = preg_replace('/(^|[(|&])\\\\/', '\1', $typeHint).' $'.$parsedName, $bindings) + )) { $arguments[$key] = $this->getBindingValue($bindings[$k]); continue; } - if (\array_key_exists('$'.$name, $bindings)) { - $arguments[$key] = $this->getBindingValue($bindings['$'.$name]); + if (\array_key_exists($k = '$'.$name, $bindings) || \array_key_exists($k = '$'.$parsedName, $bindings)) { + $arguments[$key] = $this->getBindingValue($bindings[$k]); continue; } @@ -210,7 +213,7 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed continue; } - if (isset($bindingNames[$name]) || isset($bindingNames[$parameter->name])) { + if (isset($bindingNames[$name]) || isset($bindingNames[$parsedName]) || isset($bindingNames[$parameter->name])) { $bindingKey = array_search($binding, $bindings, true); $argumentType = substr($bindingKey, 0, strpos($bindingKey, ' ')); $this->errorMessages[] = sprintf('Did you forget to add the type "%s" to argument "$%s" of method "%s::%s()"?', $argumentType, $parameter->name, $reflectionMethod->class, $reflectionMethod->name); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index fc8cba8e83cb6..3588a5b2a594a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -1301,6 +1301,18 @@ public function testAutowireWithNamedArgs() $this->assertEquals([new TypedReference(A::class, A::class), 'abc'], $container->getDefinition('foo')->getArguments()); } + public function testAutowireUnderscoreNamedArgument() + { + $container = new ContainerBuilder(); + + $container->autowire(\DateTimeImmutable::class.' $now_datetime', \DateTimeImmutable::class); + $container->autowire('foo', UnderscoreNamedArgument::class)->setPublic(true); + + (new AutowirePass())->process($container); + + $this->assertInstanceOf(\DateTimeImmutable::class, $container->get('foo')->now_datetime); + } + public function testAutowireDefaultValueParametersLike() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php index 972f8d8169d15..d9e3e921eab5c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php @@ -403,7 +403,7 @@ public static function getSubscribedServices(): array $expected = [ 'some.service' => new ServiceClosureArgument(new TypedReference('stdClass $someService', 'stdClass')), - 'some_service' => new ServiceClosureArgument(new TypedReference('stdClass $someService', 'stdClass')), + 'some_service' => new ServiceClosureArgument(new TypedReference('stdClass $some_service', 'stdClass')), 'another_service' => new ServiceClosureArgument(new TypedReference('stdClass $anotherService', 'stdClass')), ]; $this->assertEquals($expected, $container->getDefinition((string) $locator->getFactory()[0])->getArgument(0)); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php index d75b20bb77315..a8e71068775b4 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php @@ -227,6 +227,14 @@ public function __construct(A $a, Lille $lille, $foo = 'some_val') } } +class UnderscoreNamedArgument +{ + public function __construct( + public \DateTimeImmutable $now_datetime, + ) { + } +} + /* * Classes used for testing createResourceForClass */ diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php index d43c6a3aef11f..f2cf2422b0689 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php @@ -128,6 +128,8 @@ public function process(ContainerBuilder $container) $type = preg_replace('/(^|[(|&])\\\\/', '\1', $target = ltrim(ProxyHelper::exportType($p) ?? '', '?')); $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; $autowireAttributes = $autowire ? $emptyAutowireAttributes : []; + $parsedName = $p->name; + $k = null; if (isset($arguments[$r->name][$p->name])) { $target = $arguments[$r->name][$p->name]; @@ -138,7 +140,11 @@ public function process(ContainerBuilder $container) } elseif ($p->allowsNull() && !$p->isOptional()) { $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; } - } elseif (isset($bindings[$bindingName = $type.' $'.$name = Target::parseName($p)]) || isset($bindings[$bindingName = '$'.$name]) || isset($bindings[$bindingName = $type])) { + } elseif (isset($bindings[$bindingName = $type.' $'.$name = Target::parseName($p, $k, $parsedName)]) + || isset($bindings[$bindingName = $type.' $'.$parsedName]) + || isset($bindings[$bindingName = '$'.$name]) + || isset($bindings[$bindingName = $type]) + ) { $binding = $bindings[$bindingName]; [$bindingValue, $bindingId, , $bindingType, $bindingFile] = $binding->getValues(); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

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:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy