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 5184336

Browse filesBrowse files
[DependencyInjection] Add "instanceof" section for local interface-defined configs
1 parent 0a3cd97 commit 5184336
Copy full SHA for 5184336

File tree

10 files changed

+287
-47
lines changed
Filter options

10 files changed

+287
-47
lines changed

‎src/Symfony/Component/DependencyInjection/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
3.3.0
55
-----
66

7+
* [EXPERIMENTAL] added "instanceof" section for local interface-defined configs
78
* [EXPERIMENTAL] added "service-locator" argument for lazy loading a set of identified values and services
89
* [EXPERIMENTAL] added prototype services for PSR4-based discovery and registration
910
* added `ContainerBuilder::getReflectionClass()` for retrieving and tracking reflection class info

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/ChildDefinition.php
+20Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,16 @@ public function setFile($file)
119119
return parent::setFile($file);
120120
}
121121

122+
/**
123+
* {@inheritdoc}
124+
*/
125+
public function setShared($boolean)
126+
{
127+
$this->changes['shared'] = true;
128+
129+
return parent::setShared($boolean);
130+
}
131+
122132
/**
123133
* {@inheritdoc}
124134
*/
@@ -139,6 +149,16 @@ public function setLazy($boolean)
139149
return parent::setLazy($boolean);
140150
}
141151

152+
/**
153+
* {@inheritdoc}
154+
*/
155+
public function setAbstract($boolean)
156+
{
157+
$this->changes['abstract'] = true;
158+
159+
return parent::setAbstract($boolean);
160+
}
161+
142162
/**
143163
* {@inheritdoc}
144164
*/

‎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
@@ -42,6 +42,7 @@ public function __construct()
4242
$this->beforeOptimizationPasses = array(
4343
100 => array(
4444
$resolveClassPass = new ResolveClassPass(),
45+
new ResolveDefinitionInheritancePass(),
4546
),
4647
);
4748

+108Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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\ChildDefinition;
15+
use Symfony\Component\DependencyInjection\Definition;
16+
use Symfony\Component\DependencyInjection\Exception\ExceptionInterface;
17+
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
18+
19+
/**
20+
* Applies tags and instanceof inheritance to definitions.
21+
*
22+
* @author Nicolas Grekas <p@tchwork.com>
23+
*/
24+
class ResolveDefinitionInheritancePass extends AbstractRecursivePass
25+
{
26+
protected function processValue($value, $isRoot = false)
27+
{
28+
if (!$value instanceof Definition) {
29+
return parent::processValue($value, $isRoot);
30+
}
31+
if ($value instanceof ChildDefinition) {
32+
$this->resolveDefinition($value);
33+
}
34+
$class = $value->getClass();
35+
if (!$class || false !== strpos($class, '%') || !$instanceof = $value->getInstanceofConditionals()) {
36+
return parent::processValue($value, $isRoot);
37+
}
38+
39+
foreach ($instanceof as $interface => $definition) {
40+
if ($interface !== $class && (!$this->container->getReflectionClass($interface) || !$this->container->getReflectionClass($class))) {
41+
continue;
42+
}
43+
if ($interface === $class || is_subclass_of($class, $interface)) {
44+
$this->mergeDefinition($value, $definition);
45+
}
46+
}
47+
48+
return parent::processValue($value, $isRoot);
49+
}
50+
51+
/**
52+
* Populates the class and tags from parent definitions.
53+
*/
54+
private function resolveDefinition(ChildDefinition $definition)
55+
{
56+
if (!$this->container->has($parent = $definition->getParent())) {
57+
return;
58+
}
59+
60+
$parentDef = $this->container->findDefinition($parent);
61+
if ($parentDef instanceof ChildDefinition) {
62+
$this->resolveDefinition($parentDef);
63+
}
64+
65+
if (!isset($definition->getChanges()['class'])) {
66+
$definition->setClass($parentDef->getClass());
67+
}
68+
69+
// append parent tags when inheriting is enabled
70+
if ($definition->getInheritTags()) {
71+
foreach ($parentDef->getTags() as $k => $v) {
72+
foreach ($v as $v) {
73+
$definition->addTag($k, $v);
74+
}
75+
}
76+
}
77+
78+
$definition->setInheritTags(false);
79+
}
80+
81+
private function mergeDefinition(Definition $def, ChildDefinition $definition)
82+
{
83+
$changes = $definition->getChanges();
84+
if (isset($changes['shared'])) {
85+
$def->setShared($definition->isShared());
86+
}
87+
if (isset($changes['abstract'])) {
88+
$def->setAbstract($definition->isAbstract());
89+
}
90+
if (isset($changes['autowired_calls'])) {
91+
$autowiredCalls = $def->getAutowiredCalls();
92+
}
93+
94+
ResolveDefinitionTemplatesPass::mergeDefinition($def, $definition);
95+
96+
// merge autowired calls
97+
if (isset($changes['autowired_calls'])) {
98+
$def->setAutowiredCalls(array_merge($autowiredCalls, $def->getAutowiredCalls()));
99+
}
100+
101+
// merge tags
102+
foreach ($definition->getTags() as $k => $v) {
103+
foreach ($v as $v) {
104+
$def->addTag($k, $v);
105+
}
106+
}
107+
}
108+
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php
+20-21Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,26 @@ private function doResolveDefinition(ChildDefinition $definition)
103103
$def->setLazy($parentDef->isLazy());
104104
$def->setAutowiredCalls($parentDef->getAutowiredCalls());
105105

106+
self::mergeDefinition($def, $definition);
107+
108+
// merge autowiring types
109+
foreach ($definition->getAutowiringTypes(false) as $autowiringType) {
110+
$def->addAutowiringType($autowiringType);
111+
}
112+
113+
// these attributes are always taken from the child
114+
$def->setAbstract($definition->isAbstract());
115+
$def->setShared($definition->isShared());
116+
$def->setTags($definition->getTags());
117+
118+
return $def;
119+
}
120+
121+
/**
122+
* @internal
123+
*/
124+
public static function mergeDefinition(Definition $def, ChildDefinition $definition)
125+
{
106126
// overwrite with values specified in the decorator
107127
$changes = $definition->getChanges();
108128
if (isset($changes['class'])) {
@@ -168,26 +188,5 @@ private function doResolveDefinition(ChildDefinition $definition)
168188
foreach ($definition->getOverriddenGetters() as $k => $v) {
169189
$def->setOverriddenGetter($k, $v);
170190
}
171-
172-
// merge autowiring types
173-
foreach ($definition->getAutowiringTypes(false) as $autowiringType) {
174-
$def->addAutowiringType($autowiringType);
175-
}
176-
177-
// these attributes are always taken from the child
178-
$def->setAbstract($definition->isAbstract());
179-
$def->setShared($definition->isShared());
180-
$def->setTags($definition->getTags());
181-
182-
// append parent tags when inheriting is enabled
183-
if ($definition->getInheritTags()) {
184-
foreach ($parentDef->getTags() as $k => $v) {
185-
foreach ($v as $v) {
186-
$def->addTag($k, $v);
187-
}
188-
}
189-
}
190-
191-
return $def;
192191
}
193192
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Definition.php
+28-3Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class Definition
3030
private $properties = array();
3131
private $calls = array();
3232
private $getters = array();
33+
private $instanceof = array();
3334
private $configurator;
3435
private $tags = array();
3536
private $public = true;
@@ -363,6 +364,32 @@ public function getOverriddenGetters()
363364
return $this->getters;
364365
}
365366

367+
/**
368+
* Sets the definition templates to conditionally apply on the current definition, keyed by parent interface/class.
369+
*
370+
* @param $instanceof ChildDefinition[]
371+
*
372+
* @experimental in version 3.3
373+
*/
374+
public function setInstanceofConditionals(array $instanceof)
375+
{
376+
$this->instanceof = $instanceof;
377+
378+
return $this;
379+
}
380+
381+
/**
382+
* Gets the definition templates to conditionally apply on the current definition, keyed by parent interface/class.
383+
*
384+
* @return ChildDefinition[]
385+
*
386+
* @experimental in version 3.3
387+
*/
388+
public function getInstanceofConditionals()
389+
{
390+
return $this->instanceof;
391+
}
392+
366393
/**
367394
* Sets tags for this definition.
368395
*
@@ -736,9 +763,7 @@ public function getAutowiredCalls()
736763
*/
737764
public function setAutowired($autowired)
738765
{
739-
$this->autowiredCalls = $autowired ? array('__construct') : array();
740-
741-
return $this;
766+
return $this->setAutowiredCalls($autowired ? array('__construct') : array());
742767
}
743768

744769
/**

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Loader/FileLoader.php
+19-1Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\DependencyInjection\Loader;
1313

1414
use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException;
15+
use Symfony\Component\DependencyInjection\ChildDefinition;
1516
use Symfony\Component\DependencyInjection\ContainerBuilder;
1617
use Symfony\Component\DependencyInjection\Definition;
1718
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
@@ -29,6 +30,8 @@
2930
abstract class FileLoader extends BaseFileLoader
3031
{
3132
protected $container;
33+
protected $isLoadingInstanceof = false;
34+
protected $instanceof = array();
3235

3336
/**
3437
* @param ContainerBuilder $container A ContainerBuilder instance
@@ -80,7 +83,22 @@ public function registerClasses(Definition $prototype, $namespace, $resource)
8083
$prototype = serialize($prototype);
8184

8285
foreach ($classes as $class) {
83-
$this->container->setDefinition($class, unserialize($prototype));
86+
$this->setDefinition($class, unserialize($prototype));
87+
}
88+
}
89+
90+
/**
91+
* @experimental in version 3.3
92+
*/
93+
protected function setDefinition($id, Definition $definition)
94+
{
95+
if ($this->isLoadingInstanceof) {
96+
if (!$definition instanceof ChildDefinition) {
97+
throw new InvalidArgumentException(sprintf('Invalid type definition "%s": ChildDefinition expected, %s given.', $id, get_class($definition)));
98+
}
99+
$this->instanceof[$id] = $definition;
100+
} else {
101+
$this->container->setDefinition($id, $definition->setInstanceofConditionals($this->instanceof));
84102
}
85103
}
86104

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
+19-5Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,11 @@ public function load($resource, $type = null)
5757
$this->loadFromExtensions($xml);
5858

5959
// services
60-
$this->parseDefinitions($xml, $path);
60+
try {
61+
$this->parseDefinitions($xml, $path);
62+
} finally {
63+
$this->instanceof = array();
64+
}
6165
}
6266

6367
/**
@@ -126,13 +130,21 @@ private function parseDefinitions(\DOMDocument $xml, $file)
126130
}
127131
$this->setCurrentDir(dirname($file));
128132

133+
$this->instanceof = array();
134+
$this->isLoadingInstanceof = true;
135+
$instanceof = $xpath->query('//container:services/container:instanceof');
136+
foreach ($instanceof as $service) {
137+
$this->setDefinition((string) $service->getAttribute('id'), $this->parseDefinition($service, $file, array()));
138+
}
139+
140+
$this->isLoadingInstanceof = false;
129141
$defaults = $this->getServiceDefaults($xml, $file);
130142
foreach ($services as $service) {
131143
if (null !== $definition = $this->parseDefinition($service, $file, $defaults)) {
132144
if ('prototype' === $service->tagName) {
133145
$this->registerClasses($definition, (string) $service->getAttribute('namespace'), (string) $service->getAttribute('resource'));
134146
} else {
135-
$this->container->setDefinition((string) $service->getAttribute('id'), $definition);
147+
$this->setDefinition((string) $service->getAttribute('id'), $definition);
136148
}
137149
}
138150
}
@@ -209,7 +221,9 @@ private function parseDefinition(\DOMElement $service, $file, array $defaults =
209221
return;
210222
}
211223

212-
if ($parent = $service->getAttribute('parent')) {
224+
if ($this->isLoadingInstanceof) {
225+
$definition = new ChildDefinition('');
226+
} elseif ($parent = $service->getAttribute('parent')) {
213227
$definition = new ChildDefinition($parent);
214228

215229
if ($value = $service->getAttribute('inherit-tags')) {
@@ -247,7 +261,7 @@ private function parseDefinition(\DOMElement $service, $file, array $defaults =
247261
$definition->setDeprecated(true, $deprecated[0]->nodeValue ?: null);
248262
}
249263

250-
$definition->setArguments($this->getArgumentsAsPhp($service, 'argument', false, (bool) $parent));
264+
$definition->setArguments($this->getArgumentsAsPhp($service, 'argument', false, $definition instanceof ChildDefinition));
251265
$definition->setProperties($this->getArgumentsAsPhp($service, 'property'));
252266
$definition->setOverriddenGetters($this->getArgumentsAsPhp($service, 'getter'));
253267

@@ -422,7 +436,7 @@ private function processAnonymousServices(\DOMDocument $xml, $file)
422436
uksort($definitions, 'strnatcmp');
423437
foreach (array_reverse($definitions) as $id => list($domElement, $file, $wild)) {
424438
if (null !== $definition = $this->parseDefinition($domElement, $file)) {
425-
$this->container->setDefinition($id, $definition);
439+
$this->setDefinition($id, $definition);
426440
}
427441

428442
if (true === $wild) {

0 commit comments

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