From e7935c0adbecb90f7ac31ca6037ae4b8102dfed3 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Tue, 7 Feb 2017 00:00:41 +0100 Subject: [PATCH] [DI] Replace container injection by explicit service locators [SecurityBundle] Avoid container injection in FirewallMap --- .../DependencyInjection/SecurityExtension.php | 5 +- .../Resources/config/security.xml | 4 +- .../SecurityBundle/Security/FirewallMap.php | 5 +- .../Argument/ServiceLocatorArgument.php | 51 +++++++++++ .../DependencyInjection/CHANGELOG.md | 1 + .../DependencyInjection/ContainerBuilder.php | 10 +++ .../DependencyInjection/Dumper/PhpDumper.php | 10 +++ .../DependencyInjection/Dumper/XmlDumper.php | 4 + .../DependencyInjection/Dumper/YamlDumper.php | 3 + .../Loader/XmlFileLoader.php | 11 ++- .../Loader/YamlFileLoader.php | 15 +++- .../schema/dic/services/services-1.0.xsd | 1 + .../DependencyInjection/ServiceLocator.php | 71 ++++++++++++++++ .../Tests/ContainerBuilderTest.php | 20 +++++ .../Tests/Dumper/PhpDumperTest.php | 31 +++++++ .../Tests/Fixtures/php/services1-1.php | 1 + .../Tests/Fixtures/php/services1.php | 1 + .../Tests/Fixtures/php/services10.php | 1 + .../Tests/Fixtures/php/services12.php | 1 + .../Tests/Fixtures/php/services13.php | 1 + .../Tests/Fixtures/php/services19.php | 1 + .../Tests/Fixtures/php/services24.php | 1 + .../Tests/Fixtures/php/services26.php | 1 + .../Tests/Fixtures/php/services29.php | 1 + .../Tests/Fixtures/php/services31.php | 1 + .../Tests/Fixtures/php/services32.php | 1 + .../Tests/Fixtures/php/services33.php | 1 + .../Tests/Fixtures/php/services8.php | 1 + .../Tests/Fixtures/php/services9.php | 1 + .../Tests/Fixtures/php/services9_compiled.php | 1 + ...ump_overriden_getters_with_constructor.php | 1 + .../php/services_locator_argument.php | 84 +++++++++++++++++++ .../xml/services_locator_argument.xml | 25 ++++++ .../yaml/services_locator_argument.yml | 15 ++++ .../Tests/Loader/XmlFileLoaderTest.php | 11 +++ .../Tests/Loader/YamlFileLoaderTest.php | 12 +++ .../Tests/ServiceLocatorTest.php | 78 +++++++++++++++++ 37 files changed, 474 insertions(+), 9 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/Argument/ServiceLocatorArgument.php create mode 100644 src/Symfony/Component/DependencyInjection/ServiceLocator.php create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator_argument.php create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_locator_argument.xml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_locator_argument.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/ServiceLocatorTest.php diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 9c332b00660c..7557d32d8441 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -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; @@ -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; @@ -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 diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml index 884ef56b7382..f06e3efc4559 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml @@ -105,8 +105,8 @@ - - + + diff --git a/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php b/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php index 019506d746d9..a511193aaae2 100644 --- a/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php +++ b/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php @@ -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 @@ public function __construct(ContainerInterface $container, $map) $this->contexts = new \SplObjectStorage(); } - /** - * {@inheritdoc} - */ public function getListeners(Request $request) { $context = $this->getFirewallContext($request); diff --git a/src/Symfony/Component/DependencyInjection/Argument/ServiceLocatorArgument.php b/src/Symfony/Component/DependencyInjection/Argument/ServiceLocatorArgument.php new file mode 100644 index 000000000000..d926598186f2 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Argument/ServiceLocatorArgument.php @@ -0,0 +1,51 @@ + + * + * 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 + * + * @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; + } +} diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index 0461e74bee3b..18e64be813bc 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -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 diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index e9165c0c9555..084198df319a 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -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; @@ -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) { diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 449602779ef1..bfe916b6c00c 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -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; @@ -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} @@ -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 () {'; diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php index 73fb33c2aead..2d1b65b50e40 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php @@ -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; @@ -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'); diff --git a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php index b680c12c9102..10592131f3b2 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php @@ -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; @@ -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))); } diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index a75fcc8ee0b6..0902087a053c 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -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 @@ 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; diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index caa74350786b..c0257910ec6d 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -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; @@ -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].'); diff --git a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd index fd5bb7b5d1da..1bdd2fc915ab 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd +++ b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd @@ -246,6 +246,7 @@ + diff --git a/src/Symfony/Component/DependencyInjection/ServiceLocator.php b/src/Symfony/Component/DependencyInjection/ServiceLocator.php new file mode 100644 index 000000000000..4826c067959b --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/ServiceLocator.php @@ -0,0 +1,71 @@ + + * + * 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 + * @author Nicolas Grekas + * + * @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; + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 3fdf33b5cae9..9749875f8446 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -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; @@ -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; @@ -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 */ diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index bf6b789c13fb..3886e93a0c60 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -17,11 +17,13 @@ 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 @@ public function testCircularReferenceAllowanceForInlinedDefinitionsForLazyServic $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'; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php index 3a8c1199d80c..96e8ff1ebb85 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php @@ -7,6 +7,7 @@ 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; /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php index 42a78c86f653..1924052a3399 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php @@ -6,6 +6,7 @@ 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; /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php index 750d41a6ef7c..a5c41acb6db7 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php @@ -6,6 +6,7 @@ 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; /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php index 62d60c296911..0f41657f53d2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php @@ -6,6 +6,7 @@ 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; /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php index 0bc71cab9c72..a66610813617 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php @@ -6,6 +6,7 @@ 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; /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php index 86a6333760db..2fe07e9664fa 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php @@ -6,6 +6,7 @@ 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; /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php index 2020bb0774de..f74b11ca7cb2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php @@ -6,6 +6,7 @@ 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; /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php index f5f94217e90f..9c4ca45b34a2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php @@ -6,6 +6,7 @@ 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; /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services29.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services29.php index 764b34b65541..3d2d0c08530a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services29.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services29.php @@ -6,6 +6,7 @@ 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; /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services31.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services31.php index c6c1bb946b99..f5f420027535 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services31.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services31.php @@ -6,6 +6,7 @@ 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; /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services32.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services32.php index 6dda45ad0859..2fb0571099bf 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services32.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services32.php @@ -6,6 +6,7 @@ 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; /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services33.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services33.php index 1369dd25f0d6..4a1a7853d1f5 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services33.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services33.php @@ -6,6 +6,7 @@ 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; /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php index 74fb6787506a..bda14b746e61 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php @@ -6,6 +6,7 @@ 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; /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php index 91afec33403c..3cea6d7689a0 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php @@ -6,6 +6,7 @@ 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; /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php index acaebde4a77e..97ce40f4eb13 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php @@ -6,6 +6,7 @@ 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; /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dump_overriden_getters_with_constructor.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dump_overriden_getters_with_constructor.php index da02469bcfc6..c15a126446e5 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dump_overriden_getters_with_constructor.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dump_overriden_getters_with_constructor.php @@ -6,6 +6,7 @@ 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; /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator_argument.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator_argument.php new file mode 100644 index 000000000000..064269c6f48e --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator_argument.php @@ -0,0 +1,84 @@ +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(); + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_locator_argument.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_locator_argument.xml new file mode 100644 index 000000000000..93dbef712ee4 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_locator_argument.xml @@ -0,0 +1,25 @@ + + + + BazClass + bar + + + + + + + + + + + + + + + + + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_locator_argument.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_locator_argument.yml new file mode 100644 index 000000000000..ee72e51f79fd --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_locator_argument.yml @@ -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'}] diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index 623768ba4df6..0ea2aa16ac7b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -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 @@ public function testParsesIteratorArgument() $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(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index de417b59cc56..d1c037101da0 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -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\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 @@ public function testParsesIteratorArgument() $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(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/ServiceLocatorTest.php b/src/Symfony/Component/DependencyInjection/Tests/ServiceLocatorTest.php new file mode 100644 index 000000000000..e1785b506d3f --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/ServiceLocatorTest.php @@ -0,0 +1,78 @@ + + * + * 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'); + } +}