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

Appearance settings

Commit d159e0d

Browse filesBrowse files
committed
[DependencyInjection] Support local binding
1 parent 195e464 commit d159e0d
Copy full SHA for d159e0d

File tree

Expand file treeCollapse file tree

15 files changed

+360
-38
lines changed
Filter options
Expand file treeCollapse file tree

15 files changed

+360
-38
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ protected function processValue($value, $isRoot = false)
6262
$value->setArguments($this->processValue($value->getArguments()));
6363
$value->setProperties($this->processValue($value->getProperties()));
6464
$value->setMethodCalls($this->processValue($value->getMethodCalls()));
65+
$value->setBindings($this->processValue($value->getBindings()));
6566

6667
if ($v = $value->getFactory()) {
6768
$value->setFactory($this->processValue($v));

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ public function __construct()
5757
new CheckDefinitionValidityPass(),
5858
new RegisterServiceSubscribersPass(),
5959
new ResolveNamedArgumentsPass(),
60+
new ResolveBindingsPass(),
6061
new AutowirePass(),
6162
new ResolveReferencesToAliasesPass(),
6263
new ResolveInvalidReferencesPass(),
+123Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
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+
namespace Symfony\Component\DependencyInjection\Compiler;
13+
14+
use Symfony\Component\DependencyInjection\Definition;
15+
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
16+
use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper;
17+
18+
/**
19+
* @author Guilhem Niot <guilhem.niot@gmail.com>
20+
*/
21+
class ResolveBindingsPass extends AbstractRecursivePass
22+
{
23+
/**
24+
* {@inheritdoc}
25+
*/
26+
protected function processValue($value, $isRoot = false)
27+
{
28+
if (!$value instanceof Definition || empty($value->getBindings())) {
29+
return parent::processValue($value, $isRoot);
30+
}
31+
32+
$parameterBag = $this->container->getParameterBag();
33+
34+
if ($class = $value->getClass()) {
35+
$class = $parameterBag->resolveValue($class);
36+
}
37+
38+
$bindings = $value->getBindings();
39+
40+
$calls = $value->getMethodCalls();
41+
// constructor
42+
$calls[] = array(null, $value->getArguments());
43+
44+
foreach ($calls as $i => $call) {
45+
list($method, $arguments) = $call;
46+
$method = $parameterBag->resolveValue($method);
47+
$parameters = null;
48+
$resolvedArguments = array();
49+
50+
if (null === $reflectionMethod = $this->getReflectionMethod($class, $method)) {
51+
continue;
52+
}
53+
54+
foreach ($reflectionMethod->getParameters() as $key => $parameter) {
55+
if (isset($arguments[$key])) {
56+
continue;
57+
}
58+
59+
$typeHint = ProxyHelper::getTypeHint($reflectionMethod, $parameter, true);
60+
61+
if (!isset($bindings[$typeHint])) {
62+
continue;
63+
}
64+
65+
$arguments[$key] = $bindings[$typeHint];
66+
}
67+
68+
if ($arguments !== $call[1]) {
69+
ksort($arguments);
70+
$calls[$i][1] = $arguments;
71+
}
72+
}
73+
74+
list(, $arguments) = array_pop($calls);
75+
76+
if ($arguments !== $value->getArguments()) {
77+
$value->setArguments($arguments);
78+
}
79+
if ($calls !== $value->getMethodCalls()) {
80+
$value->setMethodCalls($calls);
81+
}
82+
83+
return parent::processValue($value, $isRoot);
84+
}
85+
86+
/**
87+
* @param string|null $class
88+
* @param string|null $method null if is the constructor
89+
*
90+
* @throws InvalidArgumentException
91+
*
92+
* @return \ReflectionMethod|null
93+
*/
94+
private function getReflectionMethod($class, $method)
95+
{
96+
if (!$class) {
97+
throw new InvalidArgumentException(sprintf('Unable to resolve service "%s": the class is not set.', $this->currentId));
98+
}
99+
100+
if (!$r = $this->container->getReflectionClass($class)) {
101+
throw new InvalidArgumentException(sprintf('Unable to resolve service "%s": class "%s" does not exist.', $this->currentId, $class));
102+
}
103+
104+
if (null === $method) {
105+
$method = $r->getConstructor();
106+
if (null === $method) {
107+
return;
108+
}
109+
} else {
110+
if (!$r->hasMethod($method)) {
111+
throw new InvalidArgumentException(sprintf('Unable to resolve service "%s": method "%s::%s" does not exist.', $this->currentId, $class, $method));
112+
}
113+
114+
$method = $r->getMethod($method);
115+
}
116+
117+
if (!$method->isPublic()) {
118+
throw new InvalidArgumentException(sprintf('Unable to resolve service "%s": method "%s::%s" must be public.', $this->currentId, $class, $method->name));
119+
}
120+
121+
return $method;
122+
}
123+
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Definition.php
+70Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class Definition
4242
private $decoratedService;
4343
private $autowired = 0;
4444
private $autowiringTypes = array();
45+
private $bindings = array();
4546

4647
protected $arguments;
4748

@@ -803,4 +804,73 @@ public function hasAutowiringType($type)
803804

804805
return isset($this->autowiringTypes[$type]);
805806
}
807+
808+
/**
809+
* Gets bindings.
810+
*
811+
* @return array
812+
*/
813+
public function getBindings()
814+
{
815+
return $this->bindings;
816+
}
817+
818+
/**
819+
* Sets bindings.
820+
*
821+
* @param array $bindings
822+
*
823+
* @return $this
824+
*/
825+
public function setBindings(array $bindings)
826+
{
827+
$this->bindings = array();
828+
829+
foreach ($bindings as $type => $binding) {
830+
$this->addBinding($type, $binding);
831+
}
832+
833+
return $this;
834+
}
835+
836+
/**
837+
* Adds a binding.
838+
*
839+
* @param string $type
840+
* @param Reference $service
841+
*
842+
* @return $this
843+
*/
844+
public function addBinding($type, Reference $binding)
845+
{
846+
$this->bindings[$type] = $binding;
847+
848+
return $this;
849+
}
850+
851+
/**
852+
* Removes a binding.
853+
*
854+
* @param string $type
855+
*
856+
* @return $this
857+
*/
858+
public function removeBinding($type)
859+
{
860+
unset($this->bindings[$type]);
861+
862+
return $this;
863+
}
864+
865+
/**
866+
* Does this definition has a bind for this type?
867+
*
868+
* @param string $type
869+
*
870+
* @return bool
871+
*/
872+
public function hasBinding($type)
873+
{
874+
return isset($this->bindings[$type]);
875+
}
806876
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
+22-1Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,13 +164,21 @@ private function getServiceDefaults(\DOMDocument $xml, $file)
164164
}
165165
$defaults = array(
166166
'tags' => $this->getChildren($defaultsNode, 'tag'),
167+
'bindings' => $this->getChildren($defaultsNode, 'binding'),
167168
);
168169

169170
foreach ($defaults['tags'] as $tag) {
170171
if ('' === $tag->getAttribute('name')) {
171172
throw new InvalidArgumentException(sprintf('The tag name for tag "<defaults>" in %s must be a non-empty string.', $file));
172173
}
173174
}
175+
176+
foreach ($defaults['bindings'] as $binding) {
177+
if (!$binding->hasAttribute('id')) {
178+
throw new InvalidArgumentException(sprintf('The binding service id for tag "<defaults>" in %s must be defined.', $file));
179+
}
180+
}
181+
174182
if ($defaultsNode->hasAttribute('autowire')) {
175183
$defaults['autowire'] = $this->getAutowired($defaultsNode->getAttribute('autowire'), $file);
176184
}
@@ -333,6 +341,19 @@ private function parseDefinition(\DOMElement $service, $file, array $defaults =
333341
$definition->addAutowiringType($type->textContent);
334342
}
335343

344+
$bindings = $this->getChildren($service, 'binding');
345+
if (empty($bindings) && !empty($defaults['bindings'])) {
346+
$bindings = $defaults['bindings'];
347+
}
348+
349+
foreach ($bindings as $binding) {
350+
if (!$binding->hasAttribute('id')) {
351+
throw new InvalidArgumentException(sprintf('The "id" attribute for binding type "%s" for service "%s" in %s must be defined.', $type, (string) $service->getAttribute('id'), $file));
352+
}
353+
354+
$definition->addBinding($binding->getAttribute('type'), new Reference($binding->getAttribute('id')));
355+
}
356+
336357
if ($value = $service->getAttribute('decorates')) {
337358
$renameId = $service->hasAttribute('decoration-inner-name') ? $service->getAttribute('decoration-inner-name') : null;
338359
$priority = $service->hasAttribute('decoration-priority') ? $service->getAttribute('decoration-priority') : 0;
@@ -379,7 +400,7 @@ private function processAnonymousServices(\DOMDocument $xml, $file)
379400
$xpath->registerNamespace('container', self::NS);
380401

381402
// anonymous services as arguments/properties
382-
if (false !== $nodes = $xpath->query('//container:argument[@type="service"][not(@id)]|//container:property[@type="service"][not(@id)]')) {
403+
if (false !== $nodes = $xpath->query('//container:argument[@type="service"][not(@id)]|//container:property[@type="service"][not(@id)]|//container:binding[not(@id)]')) {
383404
foreach ($nodes as $node) {
384405
// give it a unique name
385406
$id = sprintf('%d_%s', ++$count, hash('sha256', $file));

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
+53-18Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class YamlFileLoader extends FileLoader
5757
'decoration_priority' => 'decoration_priority',
5858
'autowire' => 'autowire',
5959
'autowiring_types' => 'autowiring_types',
60+
'bindings' => 'bindings',
6061
);
6162

6263
private static $prototypeKeywords = array(
@@ -75,6 +76,7 @@ class YamlFileLoader extends FileLoader
7576
'tags' => 'tags',
7677
'inherit_tags' => 'inherit_tags',
7778
'autowire' => 'autowire',
79+
'bindings' => 'bindings',
7880
);
7981

8082
private static $instanceofKeywords = array(
@@ -90,13 +92,15 @@ class YamlFileLoader extends FileLoader
9092
'calls' => 'calls',
9193
'tags' => 'tags',
9294
'autowire' => 'autowire',
95+
'bindings' => 'bindings',
9396
);
9497

9598
private static $defaultsKeywords = array(
9699
'public' => 'public',
97100
'tags' => 'tags',
98101
'inherit_tags' => 'inherit_tags',
99102
'autowire' => 'autowire',
103+
'bindings' => 'bindings',
100104
);
101105

102106
private $yamlParser;
@@ -249,31 +253,44 @@ private function parseDefaults(array &$content, $file)
249253
throw new InvalidArgumentException(sprintf('The configuration key "%s" cannot be used to define a default value in "%s". Allowed keys are "%s".', $key, $file, implode('", "', self::$defaultsKeywords)));
250254
}
251255
}
252-
if (!isset($defaults['tags'])) {
253-
return $defaults;
254-
}
255-
if (!is_array($tags = $defaults['tags'])) {
256-
throw new InvalidArgumentException(sprintf('Parameter "tags" in "_defaults" must be an array in %s. Check your YAML syntax.', $file));
257-
}
258256

259-
foreach ($tags as $tag) {
260-
if (!is_array($tag)) {
261-
$tag = array('name' => $tag);
257+
if (isset($defaults['tags'])) {
258+
if (!is_array($tags = $defaults['tags'])) {
259+
throw new InvalidArgumentException(sprintf('Parameter "tags" in "_defaults" must be an array in %s. Check your YAML syntax.', $file));
262260
}
263261

264-
if (!isset($tag['name'])) {
265-
throw new InvalidArgumentException(sprintf('A "tags" entry in "_defaults" is missing a "name" key in %s.', $file));
262+
foreach ($tags as $tag) {
263+
if (!is_array($tag)) {
264+
$tag = array('name' => $tag);
265+
}
266+
267+
if (!isset($tag['name'])) {
268+
throw new InvalidArgumentException(sprintf('A "tags" entry in "_defaults" is missing a "name" key in %s.', $file));
269+
}
270+
$name = $tag['name'];
271+
unset($tag['name']);
272+
273+
if (!is_string($name) || '' === $name) {
274+
throw new InvalidArgumentException(sprintf('The tag name in "_defaults" must be a non-empty string in %s.', $file));
275+
}
276+
277+
foreach ($tag as $attribute => $value) {
278+
if (!is_scalar($value) && null !== $value) {
279+
throw new InvalidArgumentException(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type in %s. Check your YAML syntax.', $name, $attribute, $file));
280+
}
281+
}
266282
}
267-
$name = $tag['name'];
268-
unset($tag['name']);
283+
}
269284

270-
if (!is_string($name) || '' === $name) {
271-
throw new InvalidArgumentException(sprintf('The tag name in "_defaults" must be a non-empty string in %s.', $file));
285+
if (isset($defaults['bindings'])) {
286+
if (!is_array($bindings = $defaults['bindings'])) {
287+
throw new InvalidArgumentException(sprintf('Parameter "bindings" in "_defaults" must be an array in %s. Check your YAML syntax.', $file));
272288
}
273289

274-
foreach ($tag as $attribute => $value) {
275-
if (!is_scalar($value) && null !== $value) {
276-
throw new InvalidArgumentException(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type in %s. Check your YAML syntax.', $name, $attribute, $file));
290+
$bindings = $this->resolveServices($bindings, $file);
291+
foreach ($bindings as $type => $binding) {
292+
if (!$binding instanceof Reference) {
293+
throw new InvalidArgumentException(sprintf('Binding of type "%s" in "_defaults" must be of a service reference. Check your YAML syntax.', $type, $file));
277294
}
278295
}
279296
}
@@ -522,6 +539,24 @@ private function parseDefinition($id, $service, $file, array $defaults)
522539
}
523540
}
524541

542+
if (isset($service['bindings'])) {
543+
$bindings = $service['bindings'];
544+
if (!is_array($bindings)) {
545+
throw new InvalidArgumentException(sprintf('Parameter "bindings" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file));
546+
}
547+
548+
$bindings = $this->resolveServices($bindings, $file);
549+
foreach ($bindings as $type => $binding) {
550+
if (!$binding instanceof Reference) {
551+
throw new InvalidArgumentException(sprintf('A "bindings" attribute must be a service reference for service "%s" in %s. Check your YAML syntax.', $id, $file));
552+
}
553+
554+
$definition->addBinding($type, $binding);
555+
}
556+
} elseif (isset($defaults['bindings'])) {
557+
$definition->addBindings($defaults['bindings']);
558+
}
559+
525560
if (array_key_exists('resource', $service)) {
526561
if (!is_string($service['resource'])) {
527562
throw new InvalidArgumentException(sprintf('A "resource" attribute must be of type string for service "%s" in %s. Check your YAML syntax.', $id, $file));

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.