8000 [DI] Replace container injection by explicit service locators by chalasr · Pull Request #21553 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[DI] Replace container injection by explicit service locators #21553

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
Expand Down Expand Up @@ -239,7 +240,7 @@ private function createFirewalls($config, ContainerBuilder $container)

// load firewall map
$mapDef = $container->getDefinition('security.firewall.map');
$map = $authenticationProviders = array();
$map = $authenticationProviders = $contextRefs = array();
foreach ($firewalls as $name => $firewall) {
$configId = 'security.firewall.map.config.'.$name;

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

$contextRefs[$contextId] = new Reference($contextId);
$map[$contextId] = $matcher;
}
$mapDef->replaceArgument(0, new ServiceLocatorArgument($contextRefs));
$mapDef->replaceArgument(1, new IteratorArgument($map));

// add authentication providers to authentication manager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@
</service>

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

<service id="security.firewall.context" class="Symfony\Bundle\SecurityBundle\Security\FirewallContext" abstract="true">
Expand Down
5 changes: 1 addition & 4 deletions src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@

namespace Symfony\Bundle\SecurityBundle\Security;

use Psr\Container\ContainerInterface;
use Sy 6DB6 mfony\Component\Security\Http\FirewallMapInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\DependencyInjection\ContainerInterface;

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

/**
* {@inheritdoc}
*/
public function getListeners(Request $request)
{
$context = $this->getFirewallContext($request);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection\Argument;

use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;

/**
* Represents a service locator able to lazy load a given range of services.
*
* @author Robin Chalas <robin.chalas@gmail.com>
*
* @experimental in version 3.3
*/
class ServiceLocatorArgument implements ArgumentInterface
{
private $values;

/**
* @param Reference[] $values An array of references indexed by identifier
*/
public function __construct(array $values)
{
$this->setValues($values);
}

public function getValues()
{
return $this->values;
}

public function setValues(array $values)
{
foreach ($values as $v) {
if (!$v instanceof Reference) {
throw new InvalidArgumentException('Values of a ServiceLocatorArgument must be Reference objects.');
}
}

$this->values = $values;
}
}
1 change: 1 addition & 0 deletions src/Symfony/Component/DependencyInjection/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ CHANGELOG
3.3.0
-----

* [EXPERIMENTAL] added "service-locator" argument for lazy loading a set of identified values and services
* [EXPERIMENTAL] added prototype services for PSR4-based discovery and registration
* added `ContainerBuilder::getReflectionClass()` for retrieving and tracking reflection class info
* deprecated `ContainerBuilder::getClassResource()`, use `ContainerBuilder::getReflectionClass()` or `ContainerBuilder::addObjectResource()` instead
Expand Down
10 changes: 10 additions & 0 deletions src/Symfony/Component/DependencyInjection/ContainerBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\Compiler\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
Expand Down Expand Up @@ -1122,6 +1123,15 @@ public function resolveServices($value)
foreach ($value as $k => $v) {
$value[$k] = $this->resolveServices($v);
}
} elseif ($value instanceof ServiceLocatorArgument) {
$parameterBag = $this->getParameterBag();
$services = array();
foreach ($value->getValues() as $k => $v) {
$services[$k] = function () use ($v, $parameterBag) {
return $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($v)));
};
}
$value = new ServiceLocator($services);
} elseif ($value instanceof IteratorArgument) {
$parameterBag = $this->getParameterBag();
$value = new RewindableGenerator(function () use ($value, $parameterBag) {
Expand Down
10 changes: 10 additions & 0 deletions src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\Variable;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
Expand Down Expand Up @@ -897,6 +898,7 @@ private function startClass($class, $baseClass, $namespace)
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\ServiceLocator;
$bagClass

/*{$this->docStar}
Expand Down Expand Up @@ -1536,6 +1538,14 @@ private function dumpValue($value, $interpolate = true)
}

return sprintf('array(%s)', implode(', ', $code));
} elseif ($value instanceof ServiceLocatorArgument) {
$code = "\n";
foreach ($value->getValues() as $k => $v) {
$code .= sprintf(" %s => function () { return %s; },\n", $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate));
}
$code .= ' ';

return sprintf('new ServiceLocator(array(%s))', $code);
} elseif ($value instanceof IteratorArgument) {
$countCode = array();
$countCode[] = 'function () {';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\DependencyInjection\Reference;
Expand Down Expand Up @@ -291,6 +292,9 @@ private function convertParameters(array $parameters, $type, \DOMElement $parent
if (is_array($value)) {
$element->setAttribute('type', 'collection');
$this->convertParameters($value, $type, $element, 'key');
} elseif ($value instanceof ServiceLocatorArgument) {
$element->setAttribute('type', 'service-locator');
$this->convertParameters($value->getValues(), $type, $element);
} elseif ($value instanceof IteratorArgument) {
$element->setAttribute('type', 'iterator');
$this->convertParameters($value->getValues(), $type, $element, 'key');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Parameter;
Expand Down Expand Up @@ -258,6 +259,8 @@ private function dumpValue($value)
$tag = 'iterator';
} elseif ($value instanceof ClosureProxyArgument) {
$tag = 'closure_proxy';
} elseif ($value instanceof ServiceLocatorArgument) {
$tag = 'service_locator';
} else {
throw new RuntimeException(sprintf('Unspecified Yaml tag for type "%s".', get_class($value)));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@

namespace Symfony\Component\DependencyInjection\Loader;

use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Config\Util\XmlUtils;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\Reference;
Expand Down Expand Up @@ -498,6 +498,15 @@ private function getArgumentsAsPhp(\DOMElement $node, $name, $lowercase = true,
case 'iterator':
$arguments[$key] = new IteratorArgument($this->getArgumentsAsPhp($arg, $name, false));
break;
case 'service-locator':
$values = $this->getArgumentsAsPhp($arg, $name, false);
foreach ($values as $v) {
if (!$v instanceof Reference) {
throw new InvalidArgumentException('"service-locator" argument values must be services.');
}
}
$arguments[$key] = new ServiceLocatorArgument($values);
break;
case 'string':
$arguments[$key] = $arg->nodeValue;
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Parser as YamlParser;
use Symfony\Component\Yaml\Tag\TaggedValue;
Expand Down Expand Up @@ -616,6 +616,19 @@ private function resolveServices($value)

return new IteratorArgument(array_map(array($this, 'resolveServices'), $argument));
}
if ('service_locator' === $value->getTag()) {
if (!is_array($argument)) {
throw new InvalidArgumentException('"!service_locator" tag only accepts mappings.');
}

foreach ($argument as $v) {
if (!is_string($v) || 0 !== strpos($v[0], '@') || 0 === strpos($v[0], '@@')) {
throw new InvalidArgumentException('"!service_locator" tagged values must be {key: @service} mappings.');
}
}

return new ServiceLocatorArgument(array_map(array($this, 'resolveServices'), $argument));
}
if ('closure_proxy' === $value->getTag()) {
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], '@@')) {
throw new InvalidArgumentException('"!closure_proxy" tagged values must be arrays of [@service, method].');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@
<xsd:enumeration value="string" />
<xsd:enumeration value="constant" />
<xsd:enumeration value="iterator" />
<xsd:enumeration value="service-locator" />
<xsd:enumeration value="closure-proxy" />
</xsd:restriction>
</xsd:simpleType>
Expand Down
71 changes: 71 additions & 0 deletions src/Symfony/Component/DependencyInjection/ServiceLocator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection;

use Psr\Container\ContainerInterface as PsrContainerInterface;
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;

/**
* @author Robin Chalas <robin.chalas@gmail.com>
* @author Nicolas Grekas <p@tchwork.com>
*
* @experimental in version 3.3
*/
class ServiceLocator implements PsrContainerInterface
{
private $factories;
private $values = array();

/**
* @param callable[] $factories
*/
public function __construct(array $factories)
{
$this->factories = $factories;
}

/**
* {@inheritdoc}
*/
public function has($id)
{
return isset($this->factories[$id]);
}

/**
* {@inheritdoc}
*/
public function get($id)
{
if (!isset($this->factories[$id])) {
throw new ServiceNotFoundException($id, null, null, array_keys($this->factories));
}

if (true === $factory = $this->factories[$id]) {
throw new ServiceCircularReferenceException($id, array($id, $id));
}

if (false !== $factory) {
$this->factories[$id] = true;
$this->values[$id] = $factory();
$this->factories[$id] = false;
}

return $this->values[$id];
}

public function __invoke($id)
{
return isset($this->factories[$id]) ? $this->get($id) : null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
Expand All @@ -33,6 +34,7 @@
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition;
use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass;
use Symfony\Component\ExpressionLanguage\Expression;
Expand Down Expand Up @@ -437,6 +439,24 @@ public function testCreateServiceWithIteratorArgument()
$this->assertEquals(1, $i);
}

public function testCreateServiceWithServiceLocatorArgument()
{
$builder = new ContainerBuilder();
$builder->register('bar', 'stdClass');
$builder
->register('lazy_context', 'LazyContext')
->setArguments(array(new ServiceLocatorArgument(array('bar' => new Reference('bar'), 'invalid' => new Reference('invalid', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)))))
;

$lazyContext = $builder->get('lazy_context');
$locator = $lazyContext->lazyValues;

$this->assertInstanceOf(ServiceLocator::class, $locator);
$this->assertInstanceOf('stdClass', $locator->get('bar'));
$this->assertNull($locator->get('invalid'));
$this->assertSame($locator->get('bar'), $locator('bar'), '->get() should be used when invoking ServiceLocator');
}

/**
* @expectedException \RuntimeException
*/
Expand Down
Loading
0