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 0a48b9e

Browse filesBrowse files
committed
[DependencyInjection] ActionBundle integration: introduce _instanceof
1 parent 37c5997 commit 0a48b9e
Copy full SHA for 0a48b9e

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
@@ -126,8 +126,9 @@ private function parseDefinitions(\DOMDocument $xml, $file)
126126
}
127127

128128
$defaults = $this->getServiceDefaults($xml, $file);
129+
$instanceof = $xpath->query('//container:services/container:instanceof/container:service') ?: null;
129130
foreach ($services as $service) {
130-
if (null !== $definition = $this->parseDefinition($service, $file, $defaults)) {
131+
if (null !== $definition = $this->parseDefinition($service, $file, $defaults, $instanceof)) {
131132
$this->container->setDefinition((string) $service->getAttribute('id'), $definition);
132133
}
133134
}
@@ -182,14 +183,16 @@ private function getServiceDefaults(\DOMDocument $xml, $file)
182183
/**
183184
* Parses an individual Definition.
184185
*
185-
* @param \DOMElement $service
186-
* @param string $file
187-
* @param array $defaults
186+
* @param \DOMElement $service
187+
* @param string $file
188+
* @param array $defaults
189+
* @param \DOMNodeList|null $instanceof
188190
*
189191
* @return Definition|null
190192
*/
191-
private function parseDefinition(\DOMElement $service, $file, array $defaults = array())
193+
private function parseDefinition(\DOMElement $service, $file, array $defaults = array(), \DOMNodeList $instanceof = null)
192194
{
195+
$id = (string) $service->getAttribute('id');
193196
if ($alias = $service->getAttribute('alias')) {
194197
$this->validateAlias($service, $file);
195198

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

204207
return;
205208
}
206209

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

‎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(array $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(array $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|string $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;
@@ -253,12 +274,6 @@ private function parseDefinition($id, $service, $file, array $defaults)
253274
$service = array();
254275
}
255276

256-
if (!is_array($service)) {
257-
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));
258-
}
259-
260-
static::checkDefinition($id, $service, $file);
261-
262277
if (isset($service['alias'])) {
263278
$public = array_key_exists('public', $service) ? (bool) $service['public'] : (isset($defaults['public']) ? $defaults['public'] : true);
264279
$this->container->setAlias($id, new Alias($service['alias'], $public));
@@ -272,6 +287,44 @@ private function parseDefinition($id, $service, $file, array $defaults)
272287
return;
273288
}
274289

290+
$definition = $this->getDefinition($id, $service, $file, $defaults);
291+
$className = $definition->getClass() ?: $id;
292+
$parentId = $definition instanceof ChildDefinition ? $definition->getParent() : null;
293+
foreach ($instanceof as $type => $value) {
294+
if (!is_a($className, $type, true)) {
295+
continue;
296+
}
297+
298+
if ($parentId) {
299+
$value['parent'] = $parentId;
300+
}
301+
// TODO: move the ID generation or maybe the whole block in the parent class
302+
$parentId = md5("$file.$type.$id");
303+
$parentDefinition = $this->getDefinition($parentId, $value, $file, array());
304+
$parentDefinition->setAbstract(true);
305+
if ($parentDefinition instanceof ChildDefinition) {
306+
$definition->setInheritTags(true);
307+
}
308+
$this->container->setDefinition($parentId, $parentDefinition);
309+
}
310+
311+
if (null !== $parentId) {
312+
$service['parent'] = $parentId;
313+
$definition = $this->getDefinition($id, $service, $file, $defaults);
314+
$definition->setInheritTags(true);
315+
}
316+
317+
$this->container->setDefinition($id, $definition);
318+
}
319+
320+
private function getDefinition($id, $service, $file, array $defaults)
321+
{
322+
if (!is_array($service)) {
323+
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));
324+
}
325+
326+
static::checkDefinition($id, $service, $file);
327+
275328
if (isset($service['parent'])) {
276329
$definition = new ChildDefinition($service['parent']);
277330

@@ -430,7 +483,7 @@ private function parseDefinition($id, $service, $file, array $defaults)
430483
}
431484
}
432485

433-
$this->container->setDefinition($id, $definition);
486+
return $definition;
434487
}
435488

436489
/**

‎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;
@@ -658,4 +659,32 @@ public function testDefaultsWithAutowiredMethods()
658659
$this->assertSame(array('setFoo'), $container->getDefinition('no_defaults_child')->getAutowiredMethods());
659660
$this->assertSame(array(), $container->getDefinition('with_defaults_child')->getAutowiredMethods());
660661
}
662+
663+
public function testInstanceof()
664+
{
665+
$container = new ContainerBuilder();
666+
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath . '/xml'));
667+
$loader->load('services32.xml');
668+
669+
$parentDefinition = $container->getDefinition('97c529c8fec609499e1a920f6f064edb');
670+
$this->assertNotInstanceOf(ChildDefinition::class, $parentDefinition);
671+
$this->assertTrue($parentDefinition->isAutowired());
672+
$this->assertTrue($parentDefinition->isLazy());
673+
$this->assertEquals(array('foo' => array(array()), 'bar' => array(array())), $parentDefinition->getTags());
674+
675+
$container->compile();
676+
677+
$definition = $container->getDefinition(Bar::class);
678+
$this->assertTrue($definition->isAutowired());
679+
$this->assertTrue($definition->isLazy());
680+
$this->assertEquals(array('foo' => array(array()), 'bar' => array(array())), $definition->getTags());
681+
}
682+
}
683+
684+
interface BarInterface
685+
{
686+
}
687+
688+
class Bar implements BarInterface
689+
{
661690
}

‎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;
@@ -405,6 +406,26 @@ public function testDefaults()
405406
$this->assertFalse($container->getDefinition('no_defaults_child')->isAutowired());
406407
}
407408

409+
public function testInstanceof()
410+
{
411+
$container = new ContainerBuilder();
412+
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
413+
$loader->load('services32.yml');
414+
415+
$parentDefinition = $container->getDefinition('ab31379cf807a57ab10c13685f0ee619');
416+
$this->assertNotInstanceOf(ChildDefinition::class, $parentDefinition);
417+
$this->assertTrue($parentDefinition->isAutowired());
418+
$this->assertTrue($parentDefinition->isLazy());
419+
$this->assertEquals(array('foo' => array(array()), 'bar' => array(array())), $parentDefinition->getTags());
420+
421+
$container->compile();
422+
423+
$definition = $container->getDefinition(Foo::class);
424+
$this->assertTrue($definition->isAutowired());
425+
$this->assertTrue($definition->isLazy());
426+
$this->assertEquals(array('foo' => array(array()), 'bar' => array(array())), $definition->getTags());
427+
}
428+
408429
/**
409430
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
410431
* @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").
@@ -415,3 +436,11 @@ public function testDecoratedServicesWithWrongSyntaxThrowsException()
415436
$loader->load('bad_decorates.yml');
416437
}
417438
}
439+
440+
interface FooInterface
441+
{
442+
}
443+
444+
class Foo implements FooInterface
445+
{
446+
}

0 commit comments

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