feature #21553 [DI] Replace container injection by explicit service locators (chalasr)
This PR was merged into the 3.3-dev branch.
Discussion
----------
[DI] Replace container injection by explicit service locators
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | #20658
| License | MIT
| Doc PR | https://github.com/symfony/symfony-docs/pull/7458
This adds a new `ServiceLocatorArgument` (`!service_locator`) argument type which takes a list of services, meant to be used as a concrete service locator in order to avoid the remaining needs for injecting the container when it's only a matter of dependency lazy-loading.
Config:
```yaml
App\FooBar: [!service_locator { key1: '@service1', key2: '@service2' }]
```
```xml
<service class="App\FooBar" public="false">
<argument type="service-locator">
<argument type="service" key="key1" id="service1"/>
<argument type="service" key="key2" id="service2"/>
</argument>
</service>
```
```php
new ServiceLocatorArgument(array('key1' => new Reference('service1'), 'key2' => new Reference('service2'));
```
Usage:
```php
$locator->has('key1') // true
$locator->has('service1') // false, the defined key must be used
$locator->get('key1'); // service1 instance
$locator->get('service1'); // exception
$locator->has('not-specified') // false
$locator->get('not-specified'); // exception
```
We have some concrete use cases in the core where this would be useful (see e.g. SecurityBundle's FirewallMap), same in userland/3rd party code (see related RFC).
Commits
-------
e7935c0adb
[DI] Replace container injection by explicit service locators
This commit is contained in:
commit
320529e646
@ -16,6 +16,7 @@ use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\User
|
||||
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;
|
||||
@ -239,7 +240,7 @@ class SecurityExtension extends Extension
|
||||
|
||||
// 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;
|
||||
|
||||
@ -253,8 +254,10 @@ class SecurityExtension extends Extension
|
||||
->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
|
||||
|
@ -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">
|
||||
|
@ -11,9 +11,9 @@
|
||||
|
||||
namespace Symfony\Bundle\SecurityBundle\Security;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\Security\Http\FirewallMapInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* This is a lazy-loading firewall map implementation.
|
||||
@ -116,9 +116,6 @@ class _FirewallMap
|
||||
$this->contexts = new \SplObjectStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getListeners(Request $request)
|
||||
{
|
||||
$context = $this->getFirewallContext($request);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -14,6 +14,7 @@ namespace Symfony\Component\DependencyInjection;
|
||||
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;
|
||||
@ -1122,6 +1123,15 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
||||
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) {
|
||||
|
@ -13,6 +13,7 @@ namespace Symfony\Component\DependencyInjection\Dumper;
|
||||
|
||||
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;
|
||||
@ -897,6 +898,7 @@ use Symfony\Component\DependencyInjection\Container;
|
||||
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}
|
||||
@ -1536,6 +1538,14 @@ EOF;
|
||||
}
|
||||
|
||||
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 () {';
|
||||
|
@ -13,6 +13,7 @@ namespace Symfony\Component\DependencyInjection\Dumper;
|
||||
|
||||
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;
|
||||
@ -291,6 +292,9 @@ class XmlDumper extends Dumper
|
||||
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');
|
||||
|
@ -17,6 +17,7 @@ use Symfony\Component\DependencyInjection\Alias;
|
||||
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;
|
||||
@ -258,6 +259,8 @@ class YamlDumper extends Dumper
|
||||
$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)));
|
||||
}
|
||||
|
@ -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;
|
||||
@ -498,6 +498,15 @@ class XmlFileLoader extends FileLoader
|
||||
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;
|
||||
|
@ -14,13 +14,13 @@ namespace Symfony\Component\DependencyInjection\Loader;
|
||||
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;
|
||||
@ -616,6 +616,19 @@ class YamlFileLoader extends FileLoader
|
||||
|
||||
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].');
|
||||
|
@ -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>
|
||||
|
71
src/Symfony/Component/DependencyInjection/ServiceLocator.php
Normal file
71
src/Symfony/Component/DependencyInjection/ServiceLocator.php
Normal file
@ -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;
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ use Symfony\Component\DependencyInjection\Alias;
|
||||
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;
|
||||
@ -33,6 +34,7 @@ use Symfony\Component\DependencyInjection\Reference;
|
||||
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;
|
||||
@ -437,6 +439,24 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
|
||||
$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
|
||||
*/
|
||||
|
@ -17,11 +17,13 @@ use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
|
||||
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
|
||||
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\DependencyInjection\Variable;
|
||||
use Symfony\Component\ExpressionLanguage\Expression;
|
||||
|
||||
@ -503,6 +505,35 @@ class PhpDumperTest extends \PHPUnit_Framework_TestCase
|
||||
$dumper->dump();
|
||||
}
|
||||
|
||||
public function testServiceLocatorArgumentProvideServiceLocator()
|
||||
{
|
||||
require_once self::$fixturesPath.'/includes/classes.php';
|
||||
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('lazy_referenced', 'stdClass');
|
||||
$container
|
||||
->register('lazy_context', 'LazyContext')
|
||||
->setArguments(array(new ServiceLocatorArgument(array('lazy1' => new Reference('lazy_referenced'), 'lazy2' => new Reference('lazy_referenced'), 'container' => new Reference('service_container')))))
|
||||
;
|
||||
$container->compile();
|
||||
|
||||
$dumper = new PhpDumper($container);
|
||||
$dump = $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Locator_Argument_Provide_Service_Locator'));
|
||||
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services_locator_argument.php', $dump);
|
||||
|
||||
require_once self::$fixturesPath.'/php/services_locator_argument.php';
|
||||
|
||||
$container = new \Symfony_DI_PhpDumper_Test_Locator_Argument_Provide_Service_Locator();
|
||||
$lazyContext = $container->get('lazy_context');
|
||||
|
||||
$this->assertInstanceOf(ServiceLocator::class, $lazyContext->lazyValues);
|
||||
$this->assertSame($container, $lazyContext->lazyValues->get('container'));
|
||||
$this->assertInstanceOf('stdClass', $lazy1 = $lazyContext->lazyValues->get('lazy1'));
|
||||
$this->assertInstanceOf('stdClass', $lazy2 = $lazyContext->lazyValues->get('lazy2'));
|
||||
$this->assertSame($lazy1, $lazy2);
|
||||
$this->assertFalse($lazyContext->lazyValues->has('lazy_referenced'), '->has() returns false for the original service id, only the key can be used');
|
||||
}
|
||||
|
||||
public function testLazyArgumentProvideGenerator()
|
||||
{
|
||||
require_once self::$fixturesPath.'/includes/classes.php';
|
||||
|
@ -7,6 +7,7 @@ use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
||||
|
||||
/**
|
||||
|
@ -6,6 +6,7 @@ use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
||||
|
||||
/**
|
||||
|
@ -6,6 +6,7 @@ use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
||||
|
||||
/**
|
||||
|
@ -6,6 +6,7 @@ use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
||||
|
||||
/**
|
||||
|
@ -6,6 +6,7 @@ use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
||||
|
||||
/**
|
||||
|
@ -6,6 +6,7 @@ use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
||||
|
||||
/**
|
||||
|
@ -6,6 +6,7 @@ use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
||||
|
||||
/**
|
||||
|
@ -6,6 +6,7 @@ use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
||||
|
||||
/**
|
||||
|
@ -6,6 +6,7 @@ use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
||||
|
||||
/**
|
||||
|
@ -6,6 +6,7 @@ use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
||||
|
||||
/**
|
||||
|
@ -6,6 +6,7 @@ use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
||||
|
||||
/**
|
||||
|
@ -6,6 +6,7 @@ use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
||||
|
||||
/**
|
||||
|
@ -6,6 +6,7 @@ use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
||||
|
||||
/**
|
||||
|
@ -6,6 +6,7 @@ use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
||||
|
||||
/**
|
||||
|
@ -6,6 +6,7 @@ use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
||||
|
||||
/**
|
||||
|
@ -6,6 +6,7 @@ use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
||||
|
||||
/**
|
||||
* Symfony_DI_PhpDumper_Test_Locator_Argument_Provide_Service_Locator.
|
||||
*
|
||||
* This class has been auto-generated
|
||||
* by the Symfony Dependency Injection Component.
|
||||
*
|
||||
* @final since Symfony 3.3
|
||||
*/
|
||||
class Symfony_DI_PhpDumper_Test_Locator_Argument_Provide_Service_Locator extends Container
|
||||
{
|
||||
private $parameters;
|
||||
private $targetDirs = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->services = array();
|
||||
$this->methodMap = array(
|
||||
'lazy_context' => 'getLazyContextService',
|
||||
'lazy_referenced' => 'getLazyReferencedService',
|
||||
);
|
||||
|
||||
$this->aliases = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function compile()
|
||||
{
|
||||
throw new LogicException('You cannot compile a dumped frozen container.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isFrozen()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the 'lazy_context' service.
|
||||
*
|
||||
* This service is shared.
|
||||
* This method always returns the same instance of the service.
|
||||
*
|
||||
* @return \LazyContext A LazyContext instance
|
||||
*/
|
||||
protected function getLazyContextService()
|
||||
{
|
||||
return $this->services['lazy_context'] = new \LazyContext(new ServiceLocator(array(
|
||||
'lazy1' => function () { return ${($_ = isset($this->services['lazy_referenced']) ? $this->services['lazy_referenced'] : $this->get('lazy_referenced')) && false ?: '_'}; },
|
||||
'lazy2' => function () { return ${($_ = isset($this->services['lazy_referenced']) ? $this->services['lazy_referenced'] : $this->get('lazy_referenced')) && false ?: '_'}; },
|
||||
'container' => function () { return $this; },
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the 'lazy_referenced' service.
|
||||
*
|
||||
* This service is shared.
|
||||
* This method always returns the same instance of the service.
|
||||
*
|
||||
* @return \stdClass A stdClass instance
|
||||
*/
|
||||
protected function getLazyReferencedService()
|
||||
{
|
||||
return $this->services['lazy_referenced'] = new \stdClass();
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
<parameters>
|
||||
<parameter key="baz_class">BazClass</parameter>
|
||||
<parameter key="foo">bar</parameter>
|
||||
</parameters>
|
||||
<services>
|
||||
<service id="foo.baz" class="%baz_class%">
|
||||
<factory class="%baz_class%" method="getInstance"/>
|
||||
<configurator class="%baz_class%" method="configureStatic1"/>
|
||||
</service>
|
||||
<service id="lazy_context" class="LazyContext">
|
||||
<argument type="service-locator">
|
||||
<argument key="foo_baz" type="service" id="foo.baz"/>
|
||||
<argument key="container" type="service" id="service_container"/>
|
||||
</argument>
|
||||
</service>
|
||||
<service id="lazy_context_ignore_invalid_ref" class="LazyContext">
|
||||
<argument type="service-locator">
|
||||
<argument key="foo_baz" type="service" id="foo.baz"/>
|
||||
<argument key="invalid" type="service" id="invalid" on-invalid="ignore"/>
|
||||
</argument>
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
@ -0,0 +1,15 @@
|
||||
parameters:
|
||||
baz_class: BazClass
|
||||
foo: bar
|
||||
|
||||
services:
|
||||
foo.baz:
|
||||
class: '%baz_class%'
|
||||
factory: ['%baz_class%', getInstance]
|
||||
configurator: ['%baz_class%', configureStatic1]
|
||||
lazy_context:
|
||||
class: LazyContext
|
||||
arguments: [!service_locator {foo_baz: '@foo.baz', container: '@service_container'} ]
|
||||
lazy_context_ignore_invalid_ref:
|
||||
class: LazyContext
|
||||
arguments: [!service_locator {foo_baz: '@foo.baz', invalid: '@?invalid'}]
|
@ -12,6 +12,7 @@
|
||||
namespace Symfony\Component\DependencyInjection\Tests\Loader;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
|
||||
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
@ -275,6 +276,16 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals(array(new IteratorArgument(array('foo', new Reference('foo.baz'), array('%foo%' => 'foo is %foo%', 'foobar' => '%foo%'), true, new Reference('service_container')))), $lazyDefinition->getArguments(), '->load() parses lazy arguments');
|
||||
}
|
||||
|
||||
public function testParsesServiceLocatorArgument()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
|
||||
$loader->load('services_locator_argument.xml');
|
||||
|
||||
$this->assertEquals(array(new ServiceLocatorArgument(array('foo_baz' => new Reference('foo.baz'), 'container' => new Reference('service_container')))), $container->getDefinition('lazy_context')->getArguments(), '->load() parses service-locator arguments');
|
||||
$this->assertEquals(array(new ServiceLocatorArgument(array('foo_baz' => new Reference('foo.baz'), 'invalid' => new Reference('invalid', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)))), $container->getDefinition('lazy_context_ignore_invalid_ref')->getArguments(), '->load() parses service-locator arguments');
|
||||
}
|
||||
|
||||
public function testParsesTags()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace Symfony\Component\DependencyInjection\Tests\Loader;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
|
||||
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
|
||||
@ -23,6 +24,7 @@ use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\Config\Resource\DirectoryResource;
|
||||
use Symfony\Component\Config\Resource\FileResource;
|
||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype;
|
||||
use Symfony\Component\ExpressionLanguage\Expression;
|
||||
|
||||
@ -352,6 +354,16 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals(array(new IteratorArgument(array('foo', new Reference('foo.baz'), array('%foo%' => 'foo is %foo%', 'foobar' => '%foo%'), true, new Reference('service_container')))), $lazyDefinition->getArguments(), '->load() parses lazy arguments');
|
||||
}
|
||||
|
||||
public function testParsesServiceLocatorArgument()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
|
||||
$loader->load('services_locator_argument.yml');
|
||||
|
||||
$this->assertEquals(array(new ServiceLocatorArgument(array('foo_baz' => new Reference('foo.baz'), 'container' => new Reference('service_container')))), $container->getDefinition('lazy_context')->getArguments(), '->load() parses service-locator arguments');
|
||||
$this->assertEquals(array(new ServiceLocatorArgument(array('foo_baz' => new Reference('foo.baz'), 'invalid' => new Reference('invalid', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)))), $container->getDefinition('lazy_context_ignore_invalid_ref')->getArguments(), '->load() parses service-locator arguments');
|
||||
}
|
||||
|
||||
public function testAutowire()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
@ -0,0 +1,78 @@
|
||||
<?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\Tests;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
|
||||
class ServiceLocatorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testHas()
|
||||
{
|
||||
$locator = new ServiceLocator(array(
|
||||
'foo' => function () { return 'bar'; },
|
||||
'bar' => function () { return 'baz'; },
|
||||
function () { return 'dummy'; },
|
||||
));
|
||||
|
||||
$this->assertTrue($locator->has('foo'));
|
||||
$this->assertTrue($locator->has('bar'));
|
||||
$this->assertFalse($locator->has('dummy'));
|
||||
}
|
||||
|
||||
public function testGet()
|
||||
{
|
||||
$locator = new ServiceLocator(array(
|
||||
'foo' => function () { return 'bar'; },
|
||||
'bar' => function () { return 'baz'; },
|
||||
));
|
||||
|
||||
$this->assertSame('bar', $locator->get('foo'));
|
||||
$this->assertSame('baz', $locator->get('bar'));
|
||||
}
|
||||
|
||||
public function testGetDoesNotExecuteTheSameCallableTwice()
|
||||
{
|
||||
$i = 0;
|
||||
$locator = new ServiceLocator(array('foo' => function () use (&$i) { $i++; return 'bar'; }));
|
||||
|
||||
$this->assertSame('bar', $locator->get('foo'));
|
||||
$this->assertSame('bar', $locator->get('foo'));
|
||||
$this->assertSame('bar', $locator->get('foo'));
|
||||
$this->assertSame(1, $i);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Psr\Container\NotFoundExceptionInterface
|
||||
* @expectedExceptionMessage You have requested a non-existent service "dummy"
|
||||
*/
|
||||
public function testGetThrowsOnUndefinedService()
|
||||
{
|
||||
$locator = new ServiceLocator(array(
|
||||
'foo' => function () { return 'bar'; },
|
||||
'bar' => function () { return 'baz'; },
|
||||
));
|
||||
|
||||
$locator->get('dummy');
|
||||
}
|
||||
|
||||
public function testInvoke()
|
||||
{
|
||||
$locator = new ServiceLocator(array(
|
||||
'foo' => function () { return 'bar'; },
|
||||
'bar' => function () { return 'baz'; },
|
||||
));
|
||||
|
||||
$this->assertSame('bar', $locator('foo'));
|
||||
$this->assertSame('baz', $locator('bar'));
|
||||
$this->assertNull($locator('dummy'), '->__invoke() should return null on invalid service');
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user