8000 [DI] Optional class for named services · symfony/symfony@71b17c7 · GitHub
[go: up one dir, main page]

Skip to content

Commit 71b17c7

Browse files
[DI] Optional class for named services
1 parent 8725f69 commit 71b17c7

File tree

6 files changed

+118
-17
lines changed

6 files changed

+118
-17
lines changed

src/Symfony/Component/DependencyInjection/Compiler/FactoryReturnTypePass.php

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,21 @@
1717

1818
/**
1919
* @author Guilhem N. <egetick@gmail.com>
20+
*
21+
* @deprecated since version 3.3, to be removed in 4.0.
2022
*/
2123
class FactoryReturnTypePass implements CompilerPassInterface
2224
{
25+
private $resolveClassPass;
26+
27+
public function __construct(ResolveClassPass $resolveClassPass = null)
28+
{
29+
if (null === $resolveClassPass) {
30+
@trigger_error('The '.__CLASS__.' class is deprecated since version 3.3 and will be removed in 4.0.', E_USER_DEPRECATED);
31+
}
32+
$this->resolveClassPass = $resolveClassPass;
33+
}
34+
2335
/**
2436
* {@inheritdoc}
2537
*/
@@ -29,21 +41,22 @@ public function process(ContainerBuilder $container)
2941
if (!method_exists(\ReflectionMethod::class, 'getReturnType')) {
3042
return;
3143
}
44+
$resolveClassPassChanges = null !== $this->resolveClassPass ? $this->resolveClassPass->getChanges() : array();
3245

3346
foreach ($container->getDefinitions() as $id => $definition) {
34-
$this->updateDefinition($container, $id, $definition);
47+
$this->updateDefinition($container, $id, $definition, $resolveClassPassChanges);
3548
}
3649
}
3750

38-
private function updateDefinition(ContainerBuilder $container, $id, Definition $definition, array $previous = array())
51+
private function updateDefinition(ContainerBuilder $container, $id, Definition $definition, array $resolveClassPassChanges, array $previous = array())
3952
{
4053
// circular reference
4154
if (isset($previous[$id])) {
4255
return;
4356
}
4457

4558
$factory = $definition->getFactory();
46-
if (null === $factory || null !== $definition->getClass()) {
59+
if (null === $factory || (!isset($resolveClassPassChanges[$id]) && null !== $definition->getClass())) {
4760
return;
4861
}
4962

@@ -58,7 +71,7 @@ private function updateDefinition(ContainerBuilder $container, $id, Definition $
5871
if ($factory[0] instanceof Reference) {
5972
$previous[$id] = true;
6073
$factoryDefinition = $container->findDefinition((string) $factory[0]);
61-
$this->updateDefinition($container, strtolower($factory[0]), $factoryDefinition, $previous);
74+
$this->updateDefinition($container, strtolower($factory[0]), $factoryDefinition, $resolveClassPassChanges, $previous);
6275
$class = $factoryDefinition->getClass();
6376
} else {
6477
$class = $factory[0];
@@ -83,6 +96,9 @@ private function updateDefinition(ContainerBuilder $container, $id, Definition $
8396
}
8497
}
8598

99+
if (null !== $returnType && (!isset($resolveClassPassChanges[$id]) || $returnType !== $resolveClassPassChanges[$id])) {
100+
@trigger_error(sprintf('Relying on its factory\'s return-type to define the class of service "%s" is deprecated since Symfony 3.3 and won\'t work in 4.0. Set the "class" attribute to "%s" on the service definition instead.', $id, $returnType), E_USER_DEPRECATED);
101+
}
86102
$definition->setClass($returnType);
87103
}
88104
}

src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,11 @@ public function __construct()
4141

4242
$this->optimizationPasses = array(array(
4343
new ExtensionCompilerPass(),
44+
$resolveClassPass = new ResolveClassPass(),
4445
new ResolveDefinitionTemplatesPass(),
4546
new DecoratorServicePass(),
4647
new ResolveParameterPlaceHoldersPass(),
47-
new FactoryReturnTypePass(),
48+
new FactoryReturnTypePass($resolveClassPass),
4849
new CheckDefinitionValidityPass(),
4950
new ResolveReferencesToAliasesPass(),
5051
new ResolveInvalidReferencesPass(),
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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\ChildDefinition;
16+
17+
/**
18+
* @author Nicolas Grekas <p@tchwork.com>
19+
*/
20+
class ResolveClassPass implements CompilerPassInterface
21+
{
22+
private $changes = array();
23+
24+
/**
25+
* {@inheritdoc}
26+
*/
27+
public function process(ContainerBuilder $container)
28+
{
29+
foreach ($container->getDefinitions() as $id => $definition) {
30+
if ($definition instanceof ChildDefinition || $definition->isSynthetic() || null !== $definition->getClass()) {
31+
continue;
32+
}
33+
if (preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+$/', $id)) {
34+
$this->changes[$id] = $container->getCaseSensitiveId($id);
35+
$definition->setClass($this->changes[$id]);
36+
}
37+
}
38+
}
39+
40+
/**
41+
* @internal
42+
*
43+
* @deprecated since 3.3, to be removed in 4.0.
44+
*/
45+
public function getChanges()
46+
{
47+
$changes = $this->changes;
48+
$this->changes = array();
49+
50+
return $changes;
51+
}
52+
}

src/Symfony/Component/DependencyInjection/ContainerBuilder.php

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
103103
*/
104104
private $envCounters = array();
105105

106+
/**
107+
* @var array a map of case less to case sensitive ids
108+
*/
109+
private $caseSensitiveIds = array();
110+
106111
/**
107112
* Sets the track resources flag.
108113
*
@@ -367,14 +372,18 @@ public function getCompiler()
367372
*/
368373
public function set($id, $service)
369374
{
370-
$id = strtolower($id);
375+
$caseSensitiveId = $id;
376+
$id = strtolower($caseSensitiveId);
371377

372378
if ($this->isFrozen() && (isset($this->definitions[$id]) && !$this->definitions[$id]->isSynthetic())) {
373379
// setting a synthetic service on a frozen container is alright
374380
throw new BadMethodCallException(sprintf('Setting service "%s" for an unknown or non-synthetic service definition on a frozen container is not allowed.', $id));
375381
}
376382

377383
unset($this->definitions[$id], $this->aliasDefinitions[$id]);
384+
if ($id !== $caseSensitiveId) {
385+
$this->caseSensitiveIds[$id] = $caseSensitiveId;
386+
}
378387

379388
parent::set($id, $service);
380389
}
@@ -628,7 +637,8 @@ public function setAliases(array $aliases)
628637
*/
629638
public function setAlias($alias, $id)
630639
{
631-
$alias = strtolower($alias);
640+
$caseSensitiveAlias = $alias;
641+
$alias = strtolower($caseSensitiveAlias);
632642

633643
if (is_string($id)) {
634644
$id = new Alias($id);
@@ -641,6 +651,9 @@ public function setAlias($alias, $id)
641651
}
642652

643653
unset($this->definitions[$alias]);
654+
if ($alias !== $caseSensitiveAlias) {
655+
$this->caseSensitiveIds[$alias] = $caseSensitiveAlias;
656+
}
644657

645658
$this->aliasDefinitions[$alias] = $id;
646659
}
@@ -778,9 +791,13 @@ public function setDefinition($id, Definition $definition)
778791
throw new BadMethodCallException('Adding definition to a frozen container is not allowed');
779792
}
780793

781-
$id = strtolower($id);
794+
$caseSensitiveId = $id;
795+
$id = strtolower($caseSensitiveId);
782796

783797
unset($this->aliasDefinitions[$id]);
798+
if ($id !== $caseSensitiveId) {
799+
$this->caseSensitiveIds[$id] = $caseSensitiveId;
800+
}
784801

785802
return $this->definitions[$id] = $definition;
786803
}
@@ -839,6 +856,20 @@ public function findDefinition($id)
839856
return $this->getDefinition($id);
840857
}
841858

859+
/**
860+
* Returns the case sensitive id used at registration time.
861+
*
862+
* @param string $id
863+
*
864+
* @return string
865+
*/
866+
public function getCaseSensitiveId($id)
867+
{
868+
$id = strtolower($id);
869+
870+
return isset($this->caseSensitiveIds[$id]) ? $this->caseSensitiveIds[$id] : $id;
871+
}
872+
842873
/**
8 10000 43874
* Creates a service for a service definition.
844875
*

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ private function processAnonymousServices(\DOMDocument $xml, $file)
303303
if (false !== $nodes = $xpath->query('//container:argument[@type="service"][not(@id)]|//container:property[@type="service"][not(@id)]')) {
304304
foreach ($nodes as $node) {
305305
// give it a unique name
306-
$id = sprintf('%s_%d', hash('sha256', $file), ++$count);
306+
$id = sprintf('%d_%s', ++$count, hash('sha256', $file));
307307
$node->setAttribute('id', $id);
308308

309309
if ($services = $this->getChildren($node, 'service')) {
@@ -321,15 +321,15 @@ private function processAnonymousServices(\DOMDocument $xml, $file)
321321
if (false !== $nodes = $xpath->query('//container:services/container:service[not(@id)]')) {
322322
foreach ($nodes as $node) {
323323
// give it a unique name
324-
$id = sprintf('%s_%d', hash('sha256', $file), ++$count);
324+
$id = sprintf('%d_%s', ++$count, hash('sha256', $file));
325325
$node->setAttribute('id', $id);
326326
$definitions[$id] = array($node, $file, true);
327327
}
328328
}
329329

330330
// resolve definitions
331-
krsort($definitions);
332-
foreach ($definitions as $id => list($domElement, $file, $wild)) {
331+
uksort($definitions, 'strnatcmp');
332+
foreach (array_reverse($definitions) as $id => list($domElement, $file, $wild)) {
333333
if (null !== $definition = $this->parseDefinition($domElement, $file)) {
334334
$this->container->setDefinition($id, $definition);
335335
}

src/Symfony/Component/DependencyInjection/Tests/Compiler/FactoryReturnTypePassTest.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
/**
2222
* @author Guilhem N. <egetick@gmail.com>
23+
*
24+
* @group legacy
2325
*/
2426
class FactoryReturnTypePassTest extends \PHPUnit_Framework_TestCase
2527
{
@@ -103,17 +105,16 @@ public function testCircularReference()
103105
$this->assertNull($factory2->getClass());
104106
}
105107

108+
/**
109+
* @requires function ReflectionMethod::getReturnType
110+
* @expectedDeprecation Relying on its factory's return-type to define the class of service "factory" is deprecated since Symfony 3.3 and won't work in 4.0. Set the "class" attribute to "Symfony\Component\DependencyInjection\Tests\Fixtures\FactoryDummy" on the service definition instead.
111+
*/
106112
public function testCompile()
107113
{
108114
$container = new ContainerBuilder();
109115

110116
$factory = $container->register('factory');
111117
$factory->setFactory(array(FactoryDummy::class, 'createFactory'));
112-
113-
if (!method_exists(\ReflectionMethod::class, 'getReturnType')) {
114-
$this->setExpectedException(\RuntimeException::class, 'Please add the class to service "factory" even if it is constructed by a factory since we might need to add method calls based on compile-time checks.');
115-
}
116-
117118
$container->compile();
118119

119120
$this->assertEquals(FactoryDummy::class, $container->getDefinition('factory')->getClass());

0 commit comments

Comments
 (0)
0