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 849f63f

Browse filesBrowse files
[DI] Optional class for named services
1 parent 0d2dfa7 commit 849f63f
Copy full SHA for 849f63f

File tree

6 files changed

+118
-17
lines changed
Filter options

6 files changed

+118
-17
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/FactoryReturnTypePass.php
+20-4Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php
+2-1Lines 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(),
+52Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/ContainerBuilder.php
+34-3Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,11 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
100100
*/
101101
private $envCounters = array();
102102

103+
/**
104+
* @var array a map of case less to case sensitive ids
105+
*/
106+
private $caseSensitiveIds = array();
107+
103108
/**
104109
* Sets the track resources flag.
105110
*
@@ -364,14 +369,18 @@ public function getCompiler()
364369
*/
365370
public function set($id, $service)
366371
{
367-
$id = strtolower($id);
372+
$caseSensitiveId = (string) $id;
373+
$id = strtolower($caseSensitiveId);
368374

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

374380
unset($this->definitions[$id], $this->aliasDefinitions[$id]);
381+
if ($id !== $caseSensitiveId) {
382+
$this->caseSensitiveIds[$id] = $caseSensitiveId;
383+
}
375384

376385
parent::set($id, $service);
377386
}
@@ -625,7 +634,8 @@ public function setAliases(array $aliases)
625634
*/
626635
public function setAlias($alias, $id)
627636
{
628-
$alias = strtolower($alias);
637+
$caseSensitiveAlias = (string) $alias;
638+
$alias = strtolower($caseSensitiveAlias);
629639

630640
if (is_string($id)) {
631641
$id = new Alias($id);
@@ -638,6 +648,9 @@ public function setAlias($alias, $id)
638648
}
639649

640650
unset($this->definitions[$alias]);
651+
if ($alias !== $caseSensitiveAlias) {
652+
$this->caseSensitiveIds[$alias] = $caseSensitiveAlias;
653+
}
641654

642655
$this->aliasDefinitions[$alias] = $id;
643656
}
@@ -775,9 +788,13 @@ public function setDefinition($id, Definition $definition)
775788
throw new BadMethodCallException('Adding definition to a frozen container is not allowed');
776789
}
777790

778-
$id = strtolower($id);
791+
$caseSensitiveId = (string) $id;
792+
$id = strtolower($caseSensitiveId);
779793

780794
unset($this->aliasDefinitions[$id]);
795+
if ($id !== $caseSensitiveId) {
796+
$this->caseSensitiveIds[$id] = $caseSensitiveId;
797+
}
781798

782799
return $this->definitions[$id] = $definition;
783800
}
@@ -836,6 +853,20 @@ public function findDefinition($id)
836853
return $this->getDefinition($id);
837854
}
838855

856+
/**
857+
* Returns the case sensitive id used at registration time.
858+
*
859+
* @param string $id
860+
*
861+
* @return string
862+
*/
863+
public function getCaseSensitiveId($id)
864+
{
865+
$id = strtolower($id);
866+
867+
return isset($this->caseSensitiveIds[$id]) ? $this->caseSensitiveIds[$id] : $id;
868+
}
869+
839870
/**
840871
* Creates a service for a service definition.
841872
*

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
+4-4Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ private function processAnonymousServices(\DOMDocument $xml, $file)
301301
if (false !== $nodes = $xpath->query('//container:argument[@type="service"][not(@id)]|//container:property[@type="service"][not(@id)]')) {
302302
foreach ($nodes as $node) {
303303
// give it a unique name
304-
$id = sprintf('%s_%d', hash('sha256', $file), ++$count);
304+
$id = sprintf('%d_%s', ++$count, hash('sha256', $file));
305305
$node->setAttribute('id', $id);
306306

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

328328
// resolve definitions
329-
krsort($definitions);
330-
foreach ($definitions as $id => list($domElement, $file, $wild)) {
329+
uksort($definitions, 'strnatcmp');
330+
foreach (array_reverse($definitions) as $id => list($domElement, $file, $wild)) {
331331
if (null !== $definition = $this->parseDefinition($domElement, $file)) {
332332
$this->container->setDefinition($id, $definition);
333333
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Tests/Compiler/FactoryReturnTypePassTest.php
+6-5Lines 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)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.