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

Skip to content

Commit 3670520

Browse files
committed
[DependencyInjection] ActionBundle integration: introduce _instanceof
1 parent 63b8d31 commit 3670520

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
@@ -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

Lines 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

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;
@@ -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

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;
@@ -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