Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a53d832

Browse files
[DependencyInjection] Add #[AutowireMethodOf] attribute to autowire a method of a service as a callable
1 parent 66c856b commit a53d832

File tree

6 files changed

+28
-16
lines changed

6 files changed

+28
-16
lines changed

Controller/ArgumentResolver/ServiceValueResolver.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,10 @@ public function resolve(Request $request, ArgumentMetadata $argument): array
5555
try {
5656
return [$this->container->get($controller)->get($argument->getName())];
5757
} catch (RuntimeException $e) {
58-
$what = sprintf('argument $%s of "%s()"', $argument->getName(), $controller);
59-
$message = preg_replace('/service "\.service_locator\.[^"]++"/', $what, $e->getMessage());
58+
$what = 'argument $'.$argument->getName();
59+
$message = str_replace(sprintf('service "%s"', $argument->getName()), $what, $e->getMessage());
60+
$what .= sprintf(' of "%s()"', $controller);
61+
$message = preg_replace('/service "\.service_locator\.[^"]++"/', $what, $message);
6062

6163
if ($e->getMessage() === $message) {
6264
$message = sprintf('Cannot resolve %s: %s', $what, $message);

DependencyInjection/RegisterControllerArgumentLocatorsPass.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ public function process(ContainerBuilder $container): void
123123

124124
// create a per-method map of argument-names to service/type-references
125125
$args = [];
126+
$erroredIds = 0;
126127
foreach ($parameters as $p) {
127128
/** @var \ReflectionParameter $p */
128129
$type = preg_replace('/(^|[(|&])\\\\/', '\1', $target = ltrim(ProxyHelper::exportType($p) ?? '', '?'));
@@ -171,10 +172,8 @@ public function process(ContainerBuilder $container): void
171172
$value = $parameterBag->resolveValue($attribute->value);
172173

173174
if ($attribute instanceof AutowireCallable) {
174-
$value = $attribute->buildDefinition($value, $type, $p);
175-
}
176-
177-
if ($value instanceof Reference) {
175+
$args[$p->name] = $attribute->buildDefinition($value, $type, $p);
176+
} elseif ($value instanceof Reference) {
178177
$args[$p->name] = $type ? new TypedReference($value, $type, $invalidBehavior, $p->name) : new Reference($value, $invalidBehavior);
179178
} else {
180179
$args[$p->name] = new Reference('.value.'.$container->hash($value));
@@ -198,14 +197,15 @@ public function process(ContainerBuilder $container): void
198197
->addError($message);
199198

200199
$args[$p->name] = new Reference($erroredId, ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE);
200+
++$erroredIds;
201201
} else {
202202
$target = preg_replace('/(^|[(|&])\\\\/', '\1', $target);
203203
$args[$p->name] = $type ? new TypedReference($target, $type, $invalidBehavior, Target::parseName($p)) : new Reference($target, $invalidBehavior);
204204
}
205205
}
206206
// register the maps as a per-method service-locators
207207
if ($args) {
208-
$controllers[$id.'::'.$r->name] = ServiceLocatorTagPass::register($container, $args);
208+
$controllers[$id.'::'.$r->name] = ServiceLocatorTagPass::register($container, $args, \count($args) !== $erroredIds ? $id.'::'.$r->name.'()' : null);
209209

210210
foreach ($publicAliases[$id] ?? [] as $alias) {
211211
$controllers[$alias.'::'.$r->name] = clone $controllers[$id.'::'.$r->name];

DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ public function process(ContainerBuilder $container): void
2929
foreach ($controllers as $controller => $argumentRef) {
3030
$argumentLocator = $container->getDefinition((string) $argumentRef->getValues()[0]);
3131

32+
if ($argumentLocator->getFactory()) {
33+
$argumentLocator = $container->getDefinition($argumentLocator->getFactory()[0]);
34+
}
35+
3236
if (!$argumentLocator->getArgument(0)) {
3337
// remove empty argument locators
3438
$reason = sprintf('Removing service-argument resolver for controller "%s": no corresponding services exist for the referenced types.', $controller);

Tests/Controller/ArgumentResolver/ServiceValueResolverTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public function testControllerNameIsAnArray()
8989
public function testErrorIsTruncated()
9090
{
9191
$this->expectException(NearMissValueResolverException::class);
92-
$this->expectExceptionMessage('Cannot autowire argument $dummy of "Symfony\Component\HttpKernel\Tests\Controller\ArgumentResolver\DummyController::index()": it references class "Symfony\Component\HttpKernel\Tests\Controller\ArgumentResolver\DummyService" but no such service exists.');
92+
$this->expectExceptionMessage('Cannot autowire argument $dummy required by "Symfony\Component\HttpKernel\Tests\Controller\ArgumentResolver\DummyController::index()": it references class "Symfony\Component\HttpKernel\Tests\Controller\ArgumentResolver\DummyService" but no such service exists.');
9393
$container = new ContainerBuilder();
9494
$container->addCompilerPass(new RegisterControllerArgumentLocatorsPass());
9595

Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ public function testAllActions()
143143
$this->assertInstanceof(ServiceClosureArgument::class, $locator['foo::fooAction']);
144144

145145
$locator = $container->getDefinition((string) $locator['foo::fooAction']->getValues()[0]);
146+
$locator = $container->getDefinition((string) $locator->getFactory()[0]);
146147

147148
$this->assertSame(ServiceLocator::class, $locator->getClass());
148149
$this->assertFalse($locator->isPublic());
@@ -166,6 +167,7 @@ public function testExplicitArgument()
166167

167168
$locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
168169
$locator = $container->getDefinition((string) $locator['foo::fooAction']->getValues()[0]);
170+
$locator = $container->getDefinition((string) $locator->getFactory()[0]);
169171

170172
$expected = ['bar' => new ServiceClosureArgument(new TypedReference('bar', ControllerDummy::class, ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE))];
171173
$this->assertEquals($expected, $locator->getArgument(0));
@@ -185,6 +187,7 @@ public function testOptionalArgument()
185187

186188
$locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
187189
$locator = $container->getDefinition((string) $locator['foo::fooAction']->getValues()[0]);
190+
$locator = $container->getDefinition((string) $locator->getFactory()[0]);
188191

189192
$expected = ['bar' => new ServiceClosureArgument(new TypedReference('bar', ControllerDummy::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE))];
190193
$this->assertEquals($expected, $locator->getArgument(0));
@@ -306,8 +309,8 @@ public function testBindings($bindingName)
306309
$pass->process($container);
307310

308311
$locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
309-
310312
$locator = $container->getDefinition((string) $locator['foo::fooAction']->getValues()[0]);
313+
$locator = $container->getDefinition((string) $locator->getFactory()[0]);
311314

312315
$expected = ['bar' => new ServiceClosureArgument(new Reference('foo'))];
313316
$this->assertEquals($expected, $locator->getArgument(0));
@@ -372,7 +375,8 @@ public function testBindingsOnChildDefinitions()
372375
$locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
373376
$this->assertInstanceOf(ServiceClosureArgument::class, $locator['child::fooAction']);
374377

375-
$locator = $container->getDefinition((string) $locator['child::fooAction']->getValues()[0])->getArgument(0);
378+
$locator = $container->getDefinition((string) $locator['child::fooAction']->getValues()[0]);
379+
$locator = $container->getDefinition((string) $locator->getFactory()[0])->getArgument(0);
376380
$this->assertInstanceOf(ServiceClosureArgument::class, $locator['someArg']);
377381
$this->assertEquals(new Reference('parent'), $locator['someArg']->getValues()[0]);
378382
}
@@ -439,6 +443,7 @@ public function testBindWithTarget()
439443

440444
$locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
441445
$locator = $container->getDefinition((string) $locator['foo::fooAction']->getValues()[0]);
446+
$locator = $container->getDefinition((string) $locator->getFactory()[0]);
442447

443448
$expected = [
444449
'apiKey' => new ServiceClosureArgument(new Reference('the_api_key')),

Tests/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPassTest.php

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,22 +35,23 @@ public function testProcess()
3535
$pass->process($container);
3636

3737
$controllers = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
38+
$getLocator = fn ($controllers, $k) => $container->getDefinition((string) $container->getDefinition((string) $controllers[$k]->getValues()[0])->getFactory()[0])->getArgument(0);
3839

39-
$this->assertCount(2, $container->getDefinition((string) $controllers['c1::fooAction']->getValues()[0])->getArgument(0));
40-
$this->assertCount(1, $container->getDefinition((string) $controllers['c2::setTestCase']->getValues()[0])->getArgument(0));
41-
$this->assertCount(1, $container->getDefinition((string) $controllers['c2::fooAction']->getValues()[0])->getArgument(0));
40+
$this->assertCount(2, $getLocator($controllers, 'c1::fooAction'));
41+
$this->assertCount(1, $getLocator($controllers, 'c2::setTestCase'));
42+
$this->assertCount(1, $getLocator($controllers, 'c2::fooAction'));
4243

4344
(new ResolveInvalidReferencesPass())->process($container);
4445

45-
$this->assertCount(1, $container->getDefinition((string) $controllers['c2::setTestCase']->getValues()[0])->getArgument(0));
46-
$this->assertSame([], $container->getDefinition((string) $controllers['c2::fooAction']->getValues()[0])->getArgument(0));
46+
$this->assertCount(1, $getLocator($controllers, 'c2::setTestCase'));
47+
$this->assertSame([], $getLocator($controllers, 'c2::fooAction'));
4748

4849
(new RemoveEmptyControllerArgumentLocatorsPass())->process($container);
4950

5051
$controllers = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
5152

5253
$this->assertSame(['c1::fooAction', 'c1:fooAction'], array_keys($controllers));
53-
$this->assertSame(['bar'], array_keys($container->getDefinition((string) $controllers['c1::fooAction']->getValues()[0])->getArgument(0)));
54+
$this->assertSame(['bar'], array_keys($getLocator($controllers, 'c1::fooAction')));
5455

5556
$expectedLog = [
5657
'Symfony\Component\HttpKernel\DependencyInjection\RemoveEmptyControllerArgumentLocatorsPass: Removing service-argument resolver for controller "c2::fooAction": no corresponding services exist for the referenced types.',

0 commit comments

Comments
 (0)