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 c76bab0

Browse filesBrowse files
author
Grégory SURACI
committed
[DependencyInjection] added auto decoration using DecoratorInterface or DecoratorPriorityAwareInterface
1 parent 81833b6 commit c76bab0
Copy full SHA for c76bab0

10 files changed

+360
-0
lines changed
+71Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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\ContainerBuilder;
15+
use Symfony\Component\DependencyInjection\DecoratorInterface;
16+
use Symfony\Component\DependencyInjection\DecoratorPriorityAwareInterface;
17+
18+
/**
19+
* Adds decorated service to definition of services implementing DecoratorInterface.
20+
*
21+
* @author Grégory SURACI <gregory.suraci@free.fr>
22+
*/
23+
class AutoDecorationServicePass implements CompilerPassInterface
24+
{
25+
private $throwOnException;
26+
27+
public function __construct(bool $throwOnException = true)
28+
{
29+
$this->throwOnException = $throwOnException;
30+
}
31+
32+
/**
33+
* {@inheritdoc}
34+
*/
35+
public function process(ContainerBuilder $container)
36+
{
37+
foreach ($container->getDefinitions() as $definition) {
38+
$className = $definition->getClass();
39+
40+
if (null === $className) {
41+
continue;
42+
}
43+
44+
try {
45+
$classInterfaces = class_implements($className);
46+
47+
if (!\in_array(DecoratorInterface::class, $classInterfaces)) {
48+
continue;
49+
}
50+
51+
if (\in_array(DecoratorPriorityAwareInterface::class, $classInterfaces)) {
52+
$definition->setDecoratedService(
53+
$className::getDecoratedServiceId(),
54+
null,
55+
$className::getDecorationPriority()
56+
);
57+
58+
continue;
59+
}
60+
61+
$definition->setDecoratedService($className::getDecoratedServiceId());
62+
} catch (\Throwable $e) {
63+
if ($this->throwOnException) {
64+
throw $e;
65+
}
66+
67+
continue;
68+
}
69+
}
70+
}
71+
}

‎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
@@ -61,6 +61,7 @@ public function __construct()
6161
new AutowireRequiredPropertiesPass(),
6262
new ResolveBindingsPass(),
6363
new ServiceLocatorTagPass(),
64+
new AutoDecorationServicePass(false),
6465
new DecoratorServicePass(),
6566
new CheckDefinitionValidityPass(),
6667
new AutowirePass(false),
+25Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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;
13+
14+
/**
15+
* DecoratorInterface defines a decorator class without configuration.
16+
*
17+
* @author Grégory SURACI <gregory.suraci@free.fr>
18+
*/
19+
interface DecoratorInterface
20+
{
21+
/**
22+
* @return string the serviceId/FQCN that will be decorated by this interface's implementation
23+
*/
24+
public static function getDecoratedServiceId(): string;
25+
}
+25Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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;
13+
14+
/**
15+
* DecoratorPriorityAwareInterface sets the decoration_priority value for a class implementing DecoratorInterface.
16+
*
17+
* @author Grégory SURACI <gregory.suraci@free.fr>
18+
*/
19+
interface DecoratorPriorityAwareInterface
20+
{
21+
/**
22+
* @return int the decoration priority
23+
*/
24+
public static function getDecorationPriority(): int;
25+
}
+100Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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\Tests\Compiler;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\DependencyInjection\Compiler\AutoDecorationServicePass;
16+
use Symfony\Component\DependencyInjection\Compiler\DecoratorServicePass;
17+
use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass;
18+
use Symfony\Component\DependencyInjection\ContainerBuilder;
19+
use Symfony\Component\DependencyInjection\Tests\Fixtures\Dummy;
20+
use Symfony\Component\DependencyInjection\Tests\Fixtures\DummyDecorator1;
21+
use Symfony\Component\DependencyInjection\Tests\Fixtures\DummyDecorator2;
22+
use Symfony\Component\DependencyInjection\Tests\Fixtures\DummyDecorator3;
23+
24+
class AutoDecorationServicePassTest extends TestCase
25+
{
26+
public function testProcessUsingFQCN()
27+
{
28+
$container = new ContainerBuilder();
29+
$dummyDefinition = $container
30+
->register(Dummy::class)
31+
->setPublic(true)
32+
;
33+
$decoratorDefinition = $container
34+
->register('dummy.extended', DummyDecorator1::class)
35+
->setPublic(true)
36+
;
37+
38+
$this->process($container);
39+
40+
$this->assertSame($dummyDefinition, $container->getDefinition('dummy.extended.inner'));
41+
$this->assertFalse($container->getDefinition('dummy.extended.inner')->isPublic());
42+
43+
$this->assertNull($decoratorDefinition->getDecoratedService());
44+
}
45+
46+
public function testProcessUsingStringServiceId()
47+
{
48+
$container = new ContainerBuilder();
49+
$dummyDefinition = $container
50+
->register('dummy', Dummy::class)
51+
->setPublic(true)
52+
;
53+
$decoratorDefinition = $container
54+
->register('dummy.extended', DummyDecorator2::class)
55+
->setPublic(true)
56+
;
57+
58+
$this->process($container);
59+
60+
$this->assertSame($dummyDefinition, $container->getDefinition('dummy.extended.inner'));
61+
$this->assertFalse($container->getDefinition('dummy.extended.inner')->isPublic());
62+
63+
$this->assertNull($decoratorDefinition->getDecoratedService());
64+
}
65+
66+
public function testProcessWithDecorationPriority()
67+
{
68+
$container = new ContainerBuilder();
69+
$dummyDefinition = $container
70+
->register(Dummy::class)
71+
->setPublic(true)
72+
;
73+
$decoratorWithHighPriorityDefinition = $container
74+
->register('dummy.extended', DummyDecorator3::class)
75+
->setPublic(true)
76+
;
77+
$decoratorDefinition = $container
78+
->register('dummy.extended.extended', DummyDecorator1::class)
79+
->setPublic(true)
80+
;
81+
82+
$this->process($container);
83+
84+
$this->assertSame($dummyDefinition, $container->getDefinition('dummy.extended.inner'));
85+
$this->assertFalse($container->getDefinition('dummy.extended.inner')->isPublic());
86+
87+
$this->assertEquals('dummy.extended', $container->getAlias('dummy.extended.extended.inner'));
88+
$this->assertFalse($container->getAlias('dummy.extended.extended.inner')->isPublic());
89+
90+
$this->assertNull($decoratorWithHighPriorityDefinition->getDecoratedService());
91+
$this->assertNull($decoratorDefinition->getDecoratedService());
92+
}
93+
94+
protected function process(ContainerBuilder $container)
95+
{
96+
(new ResolveClassPass())->process($container);
97+
(new AutoDecorationServicePass())->process($container);
98+
(new DecoratorServicePass())->process($container);
99+
}
100+
}
+20Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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\Tests\Fixtures;
13+
14+
class Dummy implements DummyInterface
15+
{
16+
public function sayHello(): string
17+
{
18+
return 'Hello Dummy';
19+
}
20+
}
+37Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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\Tests\Fixtures;
13+
14+
use Symfony\Component\DependencyInjection\DecoratorInterface;
15+
16+
class DummyDecorator1 implements DecoratorInterface, DummyInterface
17+
{
18+
/**
19+
* @var DummyInterface
20+
*/
21+
protected $decorated;
22+
23+
public function __construct(DummyInterface $decorated)
24+
{
25+
$this->decorated = $decorated;
26+
}
27+
28+
public static function getDecoratedServiceId(): string
29+
{
30+
return Dummy::class;
31+
}
32+
33+
public function sayHello(): string
34+
{
35+
return sprintf('%s & Decorator1', $this->decorated->sayHello());
36+
}
37+
}
+37Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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\Tests\Fixtures;
13+
14+
use Symfony\Component\DependencyInjection\DecoratorInterface;
15+
16+
class DummyDecorator2 implements DecoratorInterface, DummyInterface
17+
{
18+
/**
19+
* @var DummyInterface
20+
*/
21+
protected $decorated;
22+
23+
public function __construct(DummyInterface $decorated)
24+
{
25+
$this->decorated = $decorated;
26+
}
27+
28+
public static function getDecoratedServiceId(): string
29+
{
30+
return 'dummy';
31+
}
32+
33+
public function sayHello(): string
34+
{
35+
return sprintf('%s & Decorator2', $this->decorated->sayHello());
36+
}
37+
}
+27Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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\Tests\Fixtures;
13+
14+
use Symfony\Component\DependencyInjection\DecoratorPriorityAwareInterface;
15+
16+
class DummyDecorator3 extends DummyDecorator1 implements DecoratorPriorityAwareInterface, DummyInterface
17+
{
18+
public static function getDecorationPriority(): int
19+
{
20+
return 42;
21+
}
22+
23+
public function sayHello(): string
24+
{
25+
return sprintf('%s & Decorator3', $this->decorated->sayHello());
26+
}
27+
}
+17Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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\Tests\Fixtures;
13+
14+
interface DummyInterface
15+
{
16+
public function sayHello(): string;
17+
}

0 commit comments

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