8000 [DependencyInjection] ActionBundle integration: introduce _instanceof · symfony/symfony@0a48b9e · GitHub
[go: up one dir, main page]

Skip to content

Commit 0a48b9e

Browse files
committed
[DependencyInjection] ActionBundle integration: introduce _instanceof
1 parent 37c5997 commit 0a48b9e

File tree

7 files changed

+208
-26
lines changed

7 files changed

+208
-26
lines changed

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

Lines 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 6DAF = $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

Lines 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& F987 #39;])) {
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

Lines 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" />
Lines 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>
Lines 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

Lines 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

Lines 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;
@@ - 741A 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