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 3670520

Browse filesBrowse files
committed
[DependencyInjection] ActionBundle integration: introduce _instanceof
1 parent 63b8d31 commit 3670520
Copy full SHA for 3670520

File tree

7 files changed

+208
-26
lines changed
Filter options

7 files changed

+208
-26
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
+46-6Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,9 @@ private function parseDefinitions(\DOMDocument $xml, $file)
125125
return;
126126
}
127127

128+
$instanceof = $xpath->query('//container:services/container:instanceof/container:service') ?: null;
128129
foreach ($services as $service) {
129-
if (null !== $definition = $this->parseDefinition($service, $file, $this->getServiceDefaults($xml, $file))) {
130+
if (null !== $definition = $this->parseDefinition($service, $file, $this->getServiceDefaults($xml, $file), $instanceof)) {
130131
$this->container->setDefinition((string) $service->getAttribute('id'), $definition);
131132
}
132133
}
@@ -181,14 +182,16 @@ private function getServiceDefaults(\DOMDocument $xml, $file)
181182
/**
182183
* Parses an individual Definition.
183184
*
184-
* @param \DOMElement $service
185-
* @param string $file
186-
* @param array $defaults
185+
* @param \DOMElement $service
186+
* @param string $file
187+
* @param array $defaults
188+
* @param \DOMNodeList|null $instanceof
187189
*
188190
* @return Definition|null
189191
*/
190-
private function parseDefinition(\DOMElement $service, $file, array $defaults = array())
192+
private function parseDefinition(\DOMElement $service, $file, array $defaults = array(), \DOMNodeList $instanceof = null)
191193
{
194+
$id = (string) $service->getAttribute('id');
192195
if ($alias = $service->getAttribute('alias')) {
193196
$this->validateAlias($service, $file);
194197

@@ -198,11 +201,48 @@ private function parseDefinition(\DOMElement $service, $file, array $defaults =
198201
} elseif (isset($defaults['public'])) {
199202
$public = $defaults['public'];
200203
}
201-
$this->container->setAlias((string) $service->getAttribute('id'), new Alias($alias, $public));
204+
$this->container->setAlias($id, new Alias($alias, $public));
202205

203206
return;
204207
}
205208

209+
$definition = $this->getDefinition($service, $file, $defaults);
210+
if (null === $instanceof) {
211+
return $definition;
212+
}
213+
214+
$className = $definition->getClass() ?: $id;
215+
$parentId = $definition instanceof ChildDefinition ? $definition->getParent() : null;
216+
foreach ($instanceof as $s) {
217+
$type = (string) $s->getAttribute('id');
218+
if (!is_a($className, $type, true)) {
219+
continue;
220+
}
221+
222+
if ($parentId) {
223+
$s->setAttribute('parent', $parentId);
224+
}
225+
// TODO: move the ID generation or maybe the whole block in the parent class
226+
$parentId = md5("$file.$type.$id");
227+
$parentDefinition = $this->getDefinition($s, $file);
228+
$parentDefinition->setAbstract(true);
229+
if ($parentDefinition instanceof ChildDefinition) {
230+
$definition->setInheritTags(true);
231+
}
232+
$this->container->setDefinition($parentId, $parentDefinition);
233+
}
234+
235+
if (null !== $parentId) {
236+
$service->setAttribute('parent', $parentId);
237+
$definition = $this->getDefinition($service, $file, $defaults);
238+
$definition->setInheritTags(true);
239+
}
240+
241+
return $definition;
242+
}
243+
244+
private function getDefinition(\DOMElement $service, $file, array $defaults = array())
245+
{
206246
if ($parent = $service->getAttribute('parent')) {
207247
$definition = new ChildDefinition($parent);
208248

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
+73-20Lines changed: 73 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,16 @@ private function parseDefinitions($content, $file)
159159
}
160160

161161
$defaults = $this->parseDefaults($content, $file);
162+
163+
if ($this->isUnderscoredParamValid($content, '_instanceof', $file)) {
164+
$instanceof = $content['services']['_instanceof'];
165+
unset($content['services']['_instanceof']);
166+
} else {
167+
$instanceof = array();
168+
}
169+
162170
foreach ($content['services'] as $id => $service) {
163-
$this->parseDefinition($id, $service, $file, $defaults);
171+
$this->parseDefinition($id, $service, $file, $defaults, $instanceof);
164172
}
165173
}
166174

@@ -174,20 +182,13 @@ private function parseDefinitions($content, $file)
174182
*/
175183
private function parseDefaults(array &$content, $file)
176184
{
177-
if (!isset($content['services']['_defaults'])) {
178-
return $content;
179-
}
180-
if (!is_array($defaults = $content['services']['_defaults'])) {
181-
throw new InvalidArgumentException(sprintf('Service defaults must be an array, "%s" given in "%s".', gettype($defaults), $file));
185+
if (!$this->isUnderscoredParamValid($content, '_defaults', $file)) {
186+
return array();
182187
}
183-
if (isset($defaults['alias']) || isset($defaults['class']) || isset($defaults['factory'])) {
184-
@trigger_error('Giving a service the "_defaults" name is deprecated since Symfony 3.3 and will be forbidden in 4.0. Rename your service.', E_USER_DEPRECATED);
185188

186-
return $content;
187-
}
188-
189-
$defaultKeys = array('public', 'tags', 'inherit_tags', 'autowire');
189+
$defaults = $content['services']['_defaults'];
190190
unset($content['services']['_defaults']);
191+
$defaultKeys = array('public', 'tags', 'inherit_tags', 'autowire');
191192

192193
foreach ($defaults as $key => $default) {
193194
if (!in_array($key, $defaultKeys)) {
@@ -226,17 +227,37 @@ private function parseDefaults(array &$content, $file)
226227
return $defaults;
227228
}
228229

230+
private function isUnderscoredParamValid($content, $name, $file)
231+
{
232+
if (!isset($content['services'][$name])) {
233+
return false;
234+
}
235+
236+
if (!is_array($underscoreParam = $content['services'][$name])) {
237+
throw new InvalidArgumentException(sprintf('Service "%s" key must be an array, "%s" given in "%s".', $name, gettype($underscoreParam), $file));
238+
}
239+
240+
if (isset($underscoreParam['alias']) || isset($underscoreParam['class']) || isset($underscoreParam['factory'])) {
241+
@trigger_error(sprintf('Giving a service the "%s" name is deprecated since Symfony 3.3 and will be forbidden in 4.0. Rename your service.', $name), E_USER_DEPRECATED);
242+
243+
return false;
244+
}
245+
246+
return true;
247+
}
248+
229249
/**
230250
* Parses a definition.
231251
*
232252
* @param string $id
233253
* @param array $service
234254
* @param string $file
235255
* @param array $defaults
256+
* @param array $instanceof
236257
*
237258
* @throws InvalidArgumentException When tags are invalid
238259
*/
239-
private function parseDefinition($id, $service, $file, array $defaults)
260+
private function parseDefinition($id, $service, $file, array $defaults, array $instanceof)
240261
{
241262
if (is_string($service) && 0 === strpos($service, '@')) {
242263
$public = isset($defaults['public']) ? $defaults['public'] : true;
@@ -249,12 +270,6 @@ private function parseDefinition($id, $service, $file, array $defaults)
249270
$service = array();
250271
}
251272

252-
if (!is_array($service)) {
253-
throw new InvalidArgumentException(sprintf('A service definition must be an array or a string starting with "@" but %s found for service "%s" in %s. Check your YAML syntax.', gettype($service), $id, $file));
254-
}
255-
256-
static::checkDefinition($id, $service, $file);
257-
258273
if (isset($service['alias'])) {
259274
$public = array_key_exists('public', $service) ? (bool) $service['public'] : (isset($defaults['public']) ? $defaults['public'] : true);
260275
$this->container->setAlias($id, new Alias($service['alias'], $public));
@@ -268,6 +283,44 @@ private function parseDefinition($id, $service, $file, array $defaults)
268283
return;
269284
}
270285

286+
$definition = $this->getDefinition($id, $service, $file, $defaults);
287+
$className = $definition->getClass() ?: $id;
288+
$parentId = $definition instanceof ChildDefinition ? $definition->getParent() : null;
289+
foreach ($instanceof as $type => $value) {
290+
if (!is_a($className, $type, true)) {
291+
continue;
292+
}
293+
294+
if ($parentId) {
295+
$value['parent'] = $parentId;
296+
}
297+
// TODO: move the ID generation or maybe the whole block in the parent class
298+
$parentId = md5("$file.$type.$id");
299+
$parentDefinition = $this->getDefinition($parentId, $value, $file, array());
300+
$parentDefinition->setAbstract(true);
301+
if ($parentDefinition instanceof ChildDefinition) {
302+
$definition->setInheritTags(true);
303+
}
304+
$this->container->setDefinition($parentId, $parentDefinition);
305+
}
306+
307+
if (null !== $parentId) {
308+
$service['parent'] = $parentId;
309+
$definition = $this->getDefinition($id, $service, $file, $defaults);
310+
$definition->setInheritTags(true);
311+
}
312+
313+
$this->container->setDefinition($id, $definition);
314+
}
315+
316+
private function getDefinition($id, $service, $file, array $defaults)
317+
{
318+
if (!is_array($service)) {
319+
throw new InvalidArgumentException(sprintf('A service definition must be an array or a string starting with "@" but %s found for service "%s" in %s. Check your YAML syntax.', gettype($service), $id, $file));
320+
}
321+
322+
static::checkDefinition($id, $service, $file);
323+
271324
if (isset($service['parent'])) {
272325
$definition = new ChildDefinition($service['parent']);
273326

@@ -426,7 +479,7 @@ private function parseDefinition($id, $service, $file, array $defaults)
426479
}
427480
}
428481

429-
$this->container->setDefinition($id, $definition);
482+
return $definition;
430483
}
431484

432485
/**

‎src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
<xsd:choice maxOccurs="unbounded">
5656
<xsd:element name="service" type="service" minOccurs="1" />
5757
<xsd:element name="defaults" type="defaults" minOccurs="0" maxOccurs="1" />
58+
<xsd:element name="instanceof" type="instanceof" minOccurs="0" maxOccurs="1" />
5859
</xsd:choice>
5960
</xsd:complexType>
6061

@@ -135,6 +136,12 @@
135136
<xsd:attribute name="inherit-tags" type="boolean" />
136137
</xsd:complexType>
137138

139+
<xsd:complexType name="instanceof">
140+
<xsd:choice maxOccurs="unbounded">
141+
<xsd:element name="service" type="service" minOccurs="1" />
142+
</xsd:choice>
143+
</xsd:complexType>
144+
138145
<xsd:complexType name="tag">
139146
<xsd:attribute name="name" type="xsd:string" use="required" />
140147
<xsd:anyAttribute namespace="##any" processContents="lax" />
+14Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
3+
<services>
4+
<instanceof>
5+
<service id="Symfony\Component\DependencyInjection\Tests\Loader\BarInterface" lazy="true">
6+
<autowire>__construct</autowire>
7+
<tag name="foo" />
8+
<tag name="bar" />
9+
</service>
10+
</instanceof>
11+
12+
<service id="Symfony\Component\DependencyInjection\Tests\Loader\Bar" class="Symfony\Component\DependencyInjection\Tests\Loader\Bar" />
13+
</services>
14+
</container>
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
services:
2+
_instanceof:
3+
Symfony\Component\DependencyInjection\Tests\Loader\FooInterface:
4+
autowire: true
5+
lazy: true
6+
tags:
7+
- { name: foo }
8+
- { name: bar }
9+
10+
Symfony\Component\DependencyInjection\Tests\Loader\Foo: ~

‎src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php

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

1414
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
15+
use Symfony\Component\DependencyInjection\ChildDefinition;
1516
use Symfony\Component\DependencyInjection\ContainerInterface;
1617
use Symfony\Component\DependencyInjection\ContainerBuilder;
1718
use Symfony\Component\DependencyInjection\Reference;
@@ -657,4 +658,32 @@ public function testDefaultsWithAutowiredMethods()
657658
$this->assertSame(array('setFoo'), $container->getDefinition('no_defaults_child')->getAutowiredMethods());
658659
$this->assertSame(array(), $container->getDefinition('with_defaults_child')->getAutowiredMethods());
659660
}
661+
662+
public function testInstanceof()
663+
{
664+
$container = new ContainerBuilder();
665+
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath . '/xml'));
666+
$loader->load('services32.xml');
667+
668+
$parentDefinition = $container->getDefinition('97c529c8fec609499e1a920f6f064edb');
669+
$this->assertNotInstanceOf(ChildDefinition::class, $parentDefinition);
670+
$this->assertTrue($parentDefinition->isAutowired());
671+
$this->assertTrue($parentDefinition->isLazy());
672+
$this->assertEquals(array('foo' => array(array()), 'bar' => array(array())), $parentDefinition->getTags());
673+
674+
$container->compile();
675+
676+
$definition = $container->getDefinition(Bar::class);
677+
$this->assertTrue($definition->isAutowired());
678+
$this->assertTrue($definition->isLazy());
679+
$this->assertEquals(array('foo' => array(array()), 'bar' => array(array())), $definition->getTags());
680+
}
681+
}
682+
683+
interface BarInterface
684+
{
685+
}
686+
687+
class Bar implements BarInterface
688+
{
660689
}

‎src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php

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

1414
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
15+
use Symfony\Component\DependencyInjection\ChildDefinition;
1516
use Symfony\Component\DependencyInjection\ContainerBuilder;
1617
use Symfony\Component\DependencyInjection\Reference;
1718
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
@@ -399,6 +400,26 @@ public function testDefaults()
399400
$this->assertFalse($container->getDefinition('no_defaults_child')->isAutowired());
400401
}
401402

403+
public function testInstanceof()
404+
{
405+
$container = new ContainerBuilder();
406+
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
407+
$loader->load('services32.yml');
408+
409+
$parentDefinition = $container->getDefinition('ab31379cf807a57ab10c13685f0ee619');
410+
$this->assertNotInstanceOf(ChildDefinition::class, $parentDefinition);
411+
$this->assertTrue($parentDefinition->isAutowired());
412+
$this->assertTrue($parentDefinition->isLazy());
413+
$this->assertEquals(array('foo' => array(array()), 'bar' => array(array())), $parentDefinition->getTags());
414+
415+
$container->compile();
416+
417+
$definition = $container->getDefinition(Foo::class);
418+
$this->assertTrue($definition->isAutowired());
419+
$this->assertTrue($definition->isLazy());
420+
$this->assertEquals(array('foo' => array(array()), 'bar' => array(array())), $definition->getTags());
421+
}
422+
402423
/**
403424
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
404425
* @expectedExceptionMessage The value of the "decorates" option for the "bar" service must be the id of the service without the "@" prefix (replace "@foo" with "foo").
@@ -409,3 +430,11 @@ public function testDecoratedServicesWithWrongSyntaxThrowsException()
409430
$loader->load('bad_decorates.yml');
410431
}
411432
}
433+
434+
interface FooInterface
435+
{
436+
}
437+
438+
class Foo implements FooInterface
439+
{
440+
}

0 commit comments

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