Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit9ac3a7e

Browse files
committed
feature#21383 [DependencyInjection] Add support for named arguments (dunglas, nicolas-grekas)
This PR was merged into the 3.3-dev branch.Discussion----------[DependencyInjection] Add support for named arguments| Q | A| ------------- | ---| Branch? | master| Bug fix? | no| New feature? | yes| BC breaks? | no| Deprecations? | yes| Tests pass? | yes| Fixed tickets | -| License | MIT| Doc PR | todoThis PR introduces named arguments for services definitions. It's especially useful to inject parameters in an autowired service. It is (at least partially) an alternative to#21376 and#20738.Usage:```ymlservices: _defaults: { autowire: true } Acme\NewsletterManager: { $apiKey: "%mandrill_api_key%" }# Alternative (traditional) syntaxservices: newsletter_manager: class: Acme\NewsletterManager arguments: $apiKey: "%mandrill_api_key%" autowire: true``````phpuse Doctrine\ORM\EntityManager;use Psr\Log\LoggerInterface;namespace Acme;class NewsletterManager{ private $logger; private $em; private $apiKey; public function __construct(LoggerInterface $logger, EntityManager $em, $apiKey) { $this->logger = $logger; $this->em = $em; $this->apiKey = $apiKey; }}```Commits-------8a126c8 [DI] Deprecate string keys in arguments2ce36a6 [DependencyInjection] Add a new pass to check arguments validity6e50129 [DependencyInjection] Add support for named arguments
2 parents17b4363 +8a126c8 commit9ac3a7e

14 files changed

+447
-12
lines changed

‎src/Symfony/Component/DependencyInjection/ChildDefinition.php‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,12 +213,14 @@ public function getArgument($index)
213213
*/
214214
publicfunctionreplaceArgument($index,$value)
215215
{
216-
if (!is_int($index)) {
216+
if (is_int($index)) {
217+
$this->arguments['index_'.$index] =$value;
218+
}elseif (0 ===strpos($index,'$')) {
219+
$this->arguments[$index] =$value;
220+
}else {
217221
thrownewInvalidArgumentException('$index must be an integer.');
218222
}
219223

220-
$this->arguments['index_'.$index] =$value;
221-
222224
return$this;
223225
}
224226
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespaceSymfony\Component\DependencyInjection\Compiler;
13+
14+
useSymfony\Component\DependencyInjection\Definition;
15+
useSymfony\Component\DependencyInjection\Exception\RuntimeException;
16+
17+
/**
18+
* Checks if arguments of methods are properly configured.
19+
*
20+
* @author Kévin Dunglas <dunglas@gmail.com>
21+
* @author Nicolas Grekas <p@tchwork.com>
22+
*/
23+
class CheckArgumentsValidityPassextends AbstractRecursivePass
24+
{
25+
/**
26+
* {@inheritdoc}
27+
*/
28+
protectedfunctionprocessValue($value,$isRoot =false)
29+
{
30+
if (!$valueinstanceof Definition) {
31+
returnparent::processValue($value,$isRoot);
32+
}
33+
34+
$i =0;
35+
foreach ($value->getArguments()as$k =>$v) {
36+
if ($k !==$i++) {
37+
if (!is_int($k)) {
38+
thrownewRuntimeException(sprintf('Invalid constructor argument for service "%s": integer expected but found string "%s". Check your service definition.',$this->currentId,$k));
39+
}
40+
41+
thrownewRuntimeException(sprintf('Invalid constructor argument %d for service "%s": argument %d must be defined before. Check your service definition.',1 +$k,$this->currentId,$i));
42+
}
43+
}
44+
45+
foreach ($value->getMethodCalls()as$methodCall) {
46+
$i =0;
47+
foreach ($methodCall[1]as$k =>$v) {
48+
if ($k !==$i++) {
49+
if (!is_int($k)) {
50+
thrownewRuntimeException(sprintf('Invalid argument for method call "%s" of service "%s": integer expected but found string "%s". Check your service definition.',$methodCall[0],$this->currentId,$k));
51+
}
52+
53+
thrownewRuntimeException(sprintf('Invalid argument %d for method call "%s" of service "%s": argument %d must be defined before. Check your service definition.',1 +$k,$methodCall[0],$this->currentId,$i));
54+
}
55+
}
56+
}
57+
}
58+
}

‎src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,14 @@ public function __construct()
5353
newResolveFactoryClassPass(),
5454
newFactoryReturnTypePass($resolveClassPass),
5555
newCheckDefinitionValidityPass(),
56+
newResolveNamedArgumentsPass(),
5657
newAutowirePass(),
5758
newResolveReferencesToAliasesPass(),
5859
newResolveInvalidReferencesPass(),
5960
newAnalyzeServiceReferencesPass(true),
6061
newCheckCircularReferencesPass(),
6162
newCheckReferenceValidityPass(),
63+
newCheckArgumentsValidityPass(),
6264
));
6365

6466
$this->removingPasses =array(array(

‎src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,12 @@ private function doResolveDefinition(ChildDefinition $definition)
145145
continue;
146146
}
147147

148-
if (0 !==strpos($k,'index_')) {
148+
if (0 ===strpos($k,'index_')) {
149+
$index = (int)substr($k,strlen('index_'));
150+
}elseif (0 !==strpos($k,'$')) {
149151
thrownewRuntimeException(sprintf('Invalid argument key "%s" found.',$k));
150152
}
151153

152-
$index = (int)substr($k,strlen('index_'));
153154
$def->replaceArgument($index,$v);
154155
}
155156

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespaceSymfony\Component\DependencyInjection\Compiler;
13+
14+
useSymfony\Component\DependencyInjection\Definition;
15+
useSymfony\Component\DependencyInjection\Exception\InvalidArgumentException;
16+
17+
/**
18+
* Resolves named arguments to their corresponding numeric index.
19+
*
20+
* @author Kévin Dunglas <dunglas@gmail.com>
21+
*/
22+
class ResolveNamedArgumentsPassextends AbstractRecursivePass
23+
{
24+
/**
25+
* {@inheritdoc}
26+
*/
27+
protectedfunctionprocessValue($value,$isRoot =false)
28+
{
29+
if (!$valueinstanceof Definition) {
30+
returnparent::processValue($value,$isRoot);
31+
}
32+
33+
$parameterBag =$this->container->getParameterBag();
34+
35+
if ($class =$value->getClass()) {
36+
$class =$parameterBag->resolveValue($class);
37+
}
38+
39+
$calls =$value->getMethodCalls();
40+
$calls[] =array('__construct',$value->getArguments());
41+
42+
foreach ($callsas$i =>$call) {
43+
list($method,$arguments) =$call;
44+
$method =$parameterBag->resolveValue($method);
45+
$parameters =null;
46+
$resolvedArguments =array();
47+
48+
foreach ($argumentsas$key =>$argument) {
49+
if (is_int($key) ||'' ===$key ||'$' !==$key[0]) {
50+
if (!is_int($key)) {
51+
@trigger_error(sprintf('Using key "%s" for defining arguments of method "%s" for service "%s" is deprecated since Symfony 3.3 and will throw an exception in 4.0. Use no keys or $named arguments instead.',$key,$method,$this->currentId),E_USER_DEPRECATED);
52+
}
53+
$resolvedArguments[] =$argument;
54+
continue;
55+
}
56+
57+
$parameters =null !==$parameters ?$parameters :$this->getParameters($class,$method);
58+
59+
foreach ($parametersas$j =>$p) {
60+
if ($key ==='$'.$p->name) {
61+
$resolvedArguments[$j] =$argument;
62+
63+
continue2;
64+
}
65+
}
66+
67+
thrownewInvalidArgumentException(sprintf('Unable to resolve service "%s": method "%s::%s" has no argument named "%s". Check your service definition.',$this->currentId,$class,$method,$key));
68+
}
69+
70+
if ($resolvedArguments !==$call[1]) {
71+
ksort($resolvedArguments);
72+
$calls[$i][1] =$resolvedArguments;
73+
}
74+
}
75+
76+
list(,$arguments) =array_pop($calls);
77+
78+
if ($arguments !==$value->getArguments()) {
79+
$value->setArguments($arguments);
80+
}
81+
if ($calls !==$value->getMethodCalls()) {
82+
$value->setMethodCalls($calls);
83+
}
84+
85+
returnparent::processValue($value,$isRoot);
86+
}
87+
88+
/**
89+
* @param string|null $class
90+
* @param string $method
91+
*
92+
* @throws InvalidArgumentException
93+
*
94+
* @return array
95+
*/
96+
privatefunctiongetParameters($class,$method)
97+
{
98+
if (!$class) {
99+
thrownewInvalidArgumentException(sprintf('Unable to resolve service "%s": the class is not set.',$this->currentId));
100+
}
101+
102+
if (!$r =$this->container->getReflectionClass($class)) {
103+
thrownewInvalidArgumentException(sprintf('Unable to resolve service "%s": class "%s" does not exist.',$this->currentId,$class));
104+
}
105+
106+
if (!$r->hasMethod($method)) {
107+
thrownewInvalidArgumentException(sprintf('Unable to resolve service "%s": method "%s::%s" does not exist.',$this->currentId,$class,$method));
108+
}
109+
110+
$method =$r->getMethod($method);
111+
if (!$method->isPublic()) {
112+
thrownewInvalidArgumentException(sprintf('Unable to resolve service "%s": method "%s::%s" must be public.',$this->currentId,$class,$method->name));
113+
}
114+
115+
return$method->getParameters();
116+
}
117+
}

‎src/Symfony/Component/DependencyInjection/Definition.php‎

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,8 @@ public function addArgument($argument)
190190
/**
191191
* Sets a specific argument.
192192
*
193-
* @param int $index
194-
* @param mixed $argument
193+
* @param int|string $index
194+
* @param mixed$argument
195195
*
196196
* @return $this
197197
*
@@ -203,10 +203,14 @@ public function replaceArgument($index, $argument)
203203
thrownewOutOfBoundsException('Cannot replace arguments if none have been configured yet.');
204204
}
205205

206-
if ($index<0 ||$index >count($this->arguments) -1) {
206+
if (is_int($index) && ($index<0 ||$index >count($this->arguments) -1)) {
207207
thrownewOutOfBoundsException(sprintf('The index "%d" is not in the range [0, %d].',$index,count($this->arguments) -1));
208208
}
209209

210+
if (!array_key_exists($index,$this->arguments)) {
211+
thrownewOutOfBoundsException(sprintf('The argument "%s" doesn\'t exist.',$index));
212+
}
213+
210214
$this->arguments[$index] =$argument;
211215

212216
return$this;
@@ -225,16 +229,16 @@ public function getArguments()
225229
/**
226230
* Gets an argument to pass to the service constructor/factory method.
227231
*
228-
* @param int $index
232+
* @param int|string $index
229233
*
230234
* @return mixed The argument value
231235
*
232236
* @throws OutOfBoundsException When the argument does not exist
233237
*/
234238
publicfunctiongetArgument($index)
235239
{
236-
if ($index <0 ||$index >count($this->arguments) -1) {
237-
thrownewOutOfBoundsException(sprintf('Theindex "%d" is not in the range [0, %d].',$index,count($this->arguments) -1));
240+
if (!array_key_exists($index,$this->arguments)) {
241+
thrownewOutOfBoundsException(sprintf('Theargument "%s" doesn\'t exist.',$index));
238242
}
239243

240244
return$this->arguments[$index];

‎src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php‎

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,22 @@ private function parseDefaults(array &$content, $file)
254254
return$defaults;
255255
}
256256

257+
/**
258+
* @param array $service
259+
*
260+
* @return bool
261+
*/
262+
privatefunctionisUsingShortSyntax(array$service)
263+
{
264+
foreach ($serviceas$key =>$value) {
265+
if (is_string($key) && ('' ===$key ||'$' !==$key[0])) {
266+
returnfalse;
267+
}
268+
}
269+
270+
returntrue;
271+
}
272+
257273
/**
258274
* Parses a definition.
259275
*
@@ -273,7 +289,7 @@ private function parseDefinition($id, $service, $file, array $defaults)
273289
return;
274290
}
275291

276-
if (is_array($service) &&array_values($service) ===$service) {
292+
if (is_array($service) &&$this->isUsingShortSyntax($service)) {
277293
$service =array('arguments' =>$service);
278294
}
279295

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespaceSymfony\Component\DependencyInjection\Tests\Compiler;
13+
14+
useSymfony\Component\DependencyInjection\Compiler\CheckArgumentsValidityPass;
15+
useSymfony\Component\DependencyInjection\ContainerBuilder;
16+
17+
/**
18+
* @author Kévin Dunglas <dunglas@gmail.com>
19+
*/
20+
class CheckArgumentsValidityPassTestextends \PHPUnit_Framework_TestCase
21+
{
22+
publicfunctiontestProcess()
23+
{
24+
$container =newContainerBuilder();
25+
$definition =$container->register('foo');
26+
$definition->setArguments(array(null,1,'a'));
27+
$definition->setMethodCalls(array(
28+
array('bar',array('a','b')),
29+
array('baz',array('c','d')),
30+
));
31+
32+
$pass =newCheckArgumentsValidityPass();
33+
$pass->process($container);
34+
35+
$this->assertEquals(array(null,1,'a'),$container->getDefinition('foo')->getArguments());
36+
$this->assertEquals(array(
37+
array('bar',array('a','b')),
38+
array('baz',array('c','d')),
39+
),$container->getDefinition('foo')->getMethodCalls());
40+
}
41+
42+
/**
43+
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
44+
* @dataProvider definitionProvider
45+
*/
46+
publicfunctiontestException(array$arguments,array$methodCalls)
47+
{
48+
$container =newContainerBuilder();
49+
$definition =$container->register('foo');
50+
$definition->setArguments($arguments);
51+
$definition->setMethodCalls($methodCalls);
52+
53+
$pass =newCheckArgumentsValidityPass();
54+
$pass->process($container);
55+
}
56+
57+
publicfunctiondefinitionProvider()
58+
{
59+
returnarray(
60+
array(array(null,'a' =>'a'),array()),
61+
array(array(1 =>1),array()),
62+
array(array(),array(array('baz',array(null,'a' =>'a')))),
63+
array(array(),array(array('baz',array(1 =>1)))),
64+
);
65+
}
66+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp