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 1c0c22d

Browse filesBrowse files
committed
[DI] Replace container injection by explicit service locators
[SecurityBundle] Avoid container injection in FirewallMap
1 parent 91904af commit 1c0c22d
Copy full SHA for 1c0c22d

37 files changed

+476
-9
lines changed

‎src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+4-1Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
1717
use Symfony\Component\DependencyInjection\Alias;
1818
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
19+
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
1920
use Symfony\Component\DependencyInjection\ChildDefinition;
2021
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
2122
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
@@ -239,7 +240,7 @@ private function createFirewalls($config, ContainerBuilder $container)
239240

240241
// load firewall map
241242
$mapDef = $container->getDefinition('security.firewall.map');
242-
$map = $authenticationProviders = array();
243+
$map = $authenticationProviders = $contextRefs = array();
243244
foreach ($firewalls as $name => $firewall) {
244245
$configId = 'security.firewall.map.config.'.$name;
245246

@@ -253,8 +254,10 @@ private function createFirewalls($config, ContainerBuilder $container)
253254
->replaceArgument(2, new Reference($configId))
254255
;
255256

257+
$contextRefs[$contextId] = new Reference($contextId);
256258
$map[$contextId] = $matcher;
257259
}
260+
$mapDef->replaceArgument(0, new ServiceLocatorArgument($contextRefs));
258261
$mapDef->replaceArgument(1, new IteratorArgument($map));
259262

260263
// add authentication providers to authentication manager

‎src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@
105105
</service>
106106

107107
<service id="security.firewall.map" class="Symfony\Bundle\SecurityBundle\Security\FirewallMap" public="false">
108-
<argument type="service" id="service_container" />
109-
<argument />
108+
<argument /> <!-- Firewall context locator -->
109+
<argument /> <!-- Request matchers -->
110110
</service>
111111

112112
<service id="security.firewall.context" class="Symfony\Bundle\SecurityBundle\Security\FirewallContext" abstract="true">

‎src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php
+1-4Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111

1212
namespace Symfony\Bundle\SecurityBundle\Security;
1313

14+
use Psr\Container\ContainerInterface;
1415
use Symfony\Component\Security\Http\FirewallMapInterface;
1516
use Symfony\Component\HttpFoundation\Request;
16-
use Symfony\Component\DependencyInjection\ContainerInterface;
1717

1818
/**
1919
* This is a lazy-loading firewall map implementation.
@@ -116,9 +116,6 @@ public function __construct(ContainerInterface $container, $map)
116116
$this->contexts = new \SplObjectStorage();
117117
}
118118

119-
/**
120-
* {@inheritdoc}
121-
*/
122119
public function getListeners(Request $request)
123120
{
124121
$context = $this->getFirewallContext($request);
+51Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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\Argument;
13+
14+
use Symfony\Component\DependencyInjection\Reference;
15+
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
16+
17+
/**
18+
* Represents a service locator able to lazy load a given range of services.
19+
*
20+
* @author Robin Chalas <robin.chalas@gmail.com>
21+
*
22+
* @experimental in version 3.3
23+
*/
24+
class ServiceLocatorArgument implements ArgumentInterface
25+
{
26+
private $values;
27+
28+
/**
29+
* @param Reference[] $values An array of references indexed by identifier
30+
*/
31+
public function __construct(array $values)
32+
{
33+
$this->setValues($values);
34+
}
35+
36+
public function getValues()
37+
{
38+
return $this->values;
39+
}
40+
41+
public function setValues(array $values)
42+
{
43+
foreach ($values as $v) {
44+
if (!$v instanceof Reference) {
45+
throw new InvalidArgumentException('Values of a ServiceLocatorArgument must be Reference objects.');
46+
}
47+
}
48+
49+
$this->values = $values;
50+
}
51+
}

‎src/Symfony/Component/DependencyInjection/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
3.3.0
55
-----
66

7+
* [EXPERIMENTAL] added "service-locator" argument for lazy loading a set of identified values and services
78
* [EXPERIMENTAL] added prototype services for PSR4-based discovery and registration
89
* added `ContainerBuilder::getReflectionClass()` for retrieving and tracking reflection class info
910
* deprecated `ContainerBuilder::getClassResource()`, use `ContainerBuilder::getReflectionClass()` or `ContainerBuilder::addObjectResource()` instead

‎src/Symfony/Component/DependencyInjection/ContainerBuilder.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/ContainerBuilder.php
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
1515
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
1616
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
17+
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
1718
use Symfony\Component\DependencyInjection\Compiler\Compiler;
1819
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
1920
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
@@ -1122,6 +1123,15 @@ public function resolveServices($value)
11221123
foreach ($value as $k => $v) {
11231124
$value[$k] = $this->resolveServices($v);
11241125
}
1126+
} elseif ($value instanceof ServiceLocatorArgument) {
1127+
$parameterBag = $this->getParameterBag();
1128+
$services = array();
1129+
foreach ($value->getValues() as $k => $v) {
1130+
$services[$k] = function () use ($v, $parameterBag) {
1131+
return $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($v)));
1132+
};
1133+
}
1134+
$value = new ServiceLocator($services);
11251135
} elseif ($value instanceof IteratorArgument) {
11261136
$parameterBag = $this->getParameterBag();
11271137
$value = new RewindableGenerator(function () use ($value, $parameterBag) {

‎src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
1515
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
16+
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
1617
use Symfony\Component\DependencyInjection\Variable;
1718
use Symfony\Component\DependencyInjection\Definition;
1819
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -897,6 +898,7 @@ private function startClass($class, $baseClass, $namespace)
897898
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
898899
use Symfony\Component\DependencyInjection\Exception\LogicException;
899900
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
901+
use Symfony\Component\DependencyInjection\ServiceLocator;
900902
$bagClass
901903
902904
/*{$this->docStar}
@@ -1536,6 +1538,14 @@ private function dumpValue($value, $interpolate = true)
15361538
}
15371539

15381540
return sprintf('array(%s)', implode(', ', $code));
1541+
} elseif ($value instanceof ServiceLocatorArgument) {
1542+
$code = "\n";
1543+
foreach ($value->getValues() as $k => $v) {
1544+
$code .= sprintf(" %s => function () { return %s; },\n", $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate));
1545+
}
1546+
$code .= ' ';
1547+
1548+
return sprintf('new ServiceLocator(array(%s))', $code);
15391549
} elseif ($value instanceof IteratorArgument) {
15401550
$countCode = array();
15411551
$countCode[] = 'function () {';

‎src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
1515
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
16+
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
1617
use Symfony\Component\DependencyInjection\ContainerInterface;
1718
use Symfony\Component\DependencyInjection\Parameter;
1819
use Symfony\Component\DependencyInjection\Reference;
@@ -291,6 +292,9 @@ private function convertParameters(array $parameters, $type, \DOMElement $parent
291292
if (is_array($value)) {
292293
$element->setAttribute('type', 'collection');
293294
$this->convertParameters($value, $type, $element, 'key');
295+
} elseif ($value instanceof ServiceLocatorArgument) {
296+
$element->setAttribute('type', 'service-locator');
297+
$this->convertParameters($value->getValues(), $type, $element);
294298
} elseif ($value instanceof IteratorArgument) {
295299
$element->setAttribute('type', 'iterator');
296300
$this->convertParameters($value->getValues(), $type, $element, 'key');

‎src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
1818
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
1919
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
20+
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
2021
use Symfony\Component\DependencyInjection\ContainerInterface;
2122
use Symfony\Component\DependencyInjection\Definition;
2223
use Symfony\Component\DependencyInjection\Parameter;
@@ -258,6 +259,8 @@ private function dumpValue($value)
258259
$tag = 'iterator';
259260
} elseif ($value instanceof ClosureProxyArgument) {
260261
$tag = 'closure_proxy';
262+
} elseif ($value instanceof ServiceLocatorArgument) {
263+
$tag = 'service_locator';
261264
} else {
262265
throw new RuntimeException(sprintf('Unspecified Yaml tag for type "%s".', get_class($value)));
263266
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
+10-1Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Loader;
1313

14-
use Symfony\Component\Config\Resource\FileResource;
1514
use Symfony\Component\Config\Util\XmlUtils;
1615
use Symfony\Component\DependencyInjection\ContainerInterface;
1716
use Symfony\Component\DependencyInjection\Alias;
1817
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
1918
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
19+
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
2020
use Symfony\Component\DependencyInjection\Definition;
2121
use Symfony\Component\DependencyInjection\ChildDefinition;
2222
use Symfony\Component\DependencyInjection\Reference;
@@ -498,6 +498,15 @@ private function getArgumentsAsPhp(\DOMElement $node, $name, $lowercase = true,
498498
case 'iterator':
499499
$arguments[$key] = new IteratorArgument($this->getArgumentsAsPhp($arg, $name, false));
500500
break;
501+
case 'service-locator':
502+
$values = $this->getArgumentsAsPhp($arg, $name, false);
503+
foreach ($values as $v) {
504+
if (!$v instanceof Reference) {
505+
throw new InvalidArgumentException('"service-locator" argument values must be services.');
506+
}
507+
}
508+
$arguments[$key] = new ServiceLocatorArgument($args);
509+
break;
501510
case 'string':
502511
$arguments[$key] = $arg->nodeValue;
503512
break;

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
+14-1Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@
1414
use Symfony\Component\DependencyInjection\Alias;
1515
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
1616
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
17+
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
1718
use Symfony\Component\DependencyInjection\ChildDefinition;
1819
use Symfony\Component\DependencyInjection\ContainerInterface;
1920
use Symfony\Component\DependencyInjection\Definition;
2021
use Symfony\Component\DependencyInjection\Reference;
2122
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
2223
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
23-
use Symfony\Component\Config\Resource\FileResource;
2424
use Symfony\Component\Yaml\Exception\ParseException;
2525
use Symfony\Component\Yaml\Parser as YamlParser;
2626
use Symfony\Component\Yaml\Tag\TaggedValue;
@@ -616,6 +616,19 @@ private function resolveServices($value)
616616

617617
return new IteratorArgument(array_map(array($this, 'resolveServices'), $argument));
618618
}
619+
if ('service_locator' === $value->getTag()) {
620+
if (!is_array($argument)) {
621+
throw new InvalidArgumentException('"!service_locator" tag only accepts mappings.');
622+
}
623+
624+
foreach ($argument as $v) {
625+
if (!is_string($v) || 0 !== strpos($v[0], '@') || 0 === strpos($v[0], '@@')) {
626+
throw new InvalidArgumentException('"!service_locator" tagged values must be {key: @service} mappings.');
627+
}
628+
}
629+
630+
return new ServiceLocatorArgument(array_map(array($this, 'resolveServices'), $argument));
631+
}
619632
if ('closure_proxy' === $value->getTag()) {
620633
if (!is_array($argument) || array(0, 1) !== array_keys($argument) || !is_string($argument[0]) || !is_string($argument[1]) || 0 !== strpos($argument[0], '@') || 0 === strpos($argument[0], '@@')) {
621634
throw new InvalidArgumentException('"!closure_proxy" tagged values must be arrays of [@service, method].');

‎src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@
246246
<xsd:enumeration value="string" />
247247
<xsd:enumeration value="constant" />
248248
<xsd:enumeration value="iterator" />
249+
<xsd:enumeration value="service-locator" />
249250
<xsd:enumeration value="closure-proxy" />
250251
</xsd:restriction>
251252
</xsd:simpleType>
+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;
13+
14+
use Psr\Container\ContainerInterface as PsrContainerInterface;
15+
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
16+
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
17+
18+
/**
19+
* @author Robin Chalas <robin.chalas@gmail.com>
20+
* @author Nicolas Grekas <p@tchwork.com>
21+
*
22+
* @experimental in version 3.3
23+
*/
24+
class ServiceLocator implements PsrContainerInterface
25+
{
26+
private $factories;
27+
private $values = array();
28+
29+
/**
30+
* @param callable[] $factories
31+
*/
32+
public function __construct(array $factories)
33+
{
34+
$this->factories = $factories;
35+
}
36+
37+
/**
38+
* {@inheritdoc}
39+
*/
40+
public function has($id)
41+
{
42+
return isset($this->factories[$id]);
43+
}
44+
45+
/**
46+
* {@inheritdoc}
47+
*/
48+
public function get($id)
49+
{
50+
if (!isset($this->factories[$id])) {
51+
throw new ServiceNotFoundException($id, null, null, array_keys($this->factories));
52+
}
53+
54+
if (true === $factory = $this->factories[$id]) {
55+
throw new ServiceCircularReferenceException($id, array($id, $id));
56+
}
57+
58+
if (false !== $factory) {
59+
$this->factories[$id] = true;
60+
$this->values[$id] = $factory();
61+
$this->factories[$id] = false;
62+
}
63+
64+
return $this->values[$id];
65+
}
66+
67+
public function __invoke($id)
68+
{
69+
return isset($this->factories[$id]) ? $this->get($id) : null;
70+
}
71+
}

‎src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
+22Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
2222
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
2323
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
24+
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
2425
use Symfony\Component\DependencyInjection\ChildDefinition;
2526
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
2627
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -33,6 +34,7 @@
3334
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
3435
use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
3536
use Symfony\Component\Config\Resource\FileResource;
37+
use Symfony\Component\DependencyInjection\ServiceLocator;
3638
use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition;
3739
use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass;
3840
use Symfony\Component\ExpressionLanguage\Expression;
@@ -437,6 +439,26 @@ public function testCreateServiceWithIteratorArgument()
437439
$this->assertEquals(1, $i);
438440
}
439441

442+
public function testCreateServiceWithServiceLocatorArgument()
443+
{
444+
$builder = new ContainerBuilder();
445+
$builder->register('bar', 'stdClass');
446+
$builder
447+
->register('lazy_context', 'LazyContext')
448+
->setArguments(array(new ServiceLocatorArgument(array('bar' => new Reference('bar'), 'invalid' => new Reference('invalid', ContainerInterface::IGNORE_ON_INVALID_REFERENCE), 'foo_string' => 'foo', 'foo_collection' => array('foo')))))
449+
;
450+
451+
$lazyContext = $builder->get('lazy_context');
452+
$locator = $lazyContext->lazyValues;
453+
454+
$this->assertInstanceOf(ServiceLocator::class, $locator);
455+
$this->assertInstanceOf('stdClass', $locator->get('bar'));
456+
$this->assertSame('foo', $locator->get('foo_string'));
457+
$this->assertSame(array('foo'), $locator->get('foo_collection'));
458+
$this->assertNull($locator->get('invalid'));
459+
$this->assertSame($locator->get('bar'), $locator('bar'), '->get() should be used when invoking ServiceLocator');
460+
}
461+
440462
/**
441463
* @expectedException \RuntimeException
442464
*/

0 commit comments

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