From 00d7f6fa713f72ffb1166ed5faa58241df75ddfb Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Fri, 25 Aug 2017 13:52:13 -0400 Subject: [PATCH] [DI] improve psr4-based service discovery with namespace option --- .../Loader/YamlFileLoader.php | 10 ++++- .../OtherDir/Component1/Dir1/Service1.php | 7 ++++ .../OtherDir/Component1/Dir2/Service2.php | 8 ++++ .../OtherDir/Component1/Dir3/Service3.php | 8 ++++ .../OtherDir/Component2/Dir1/Service4.php | 7 ++++ .../OtherDir/Component2/Dir2/Service5.php | 8 ++++ .../yaml/services_prototype_namespace.yml | 10 +++++ ...s_prototype_namespace_without_resource.yml | 4 ++ .../Tests/Loader/YamlFileLoaderTest.php | 39 +++++++++++++++++++ 9 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/OtherDir/Component1/Dir1/Service1.php create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/OtherDir/Component1/Dir2/Service2.php create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/OtherDir/Component1/Dir3/Service3.php create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/OtherDir/Component2/Dir1/Service4.php create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/OtherDir/Component2/Dir2/Service5.php create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_prototype_namespace.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_prototype_namespace_without_resource.yml diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index 6056d05c101bd..8a1c51f4e6fc1 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -63,6 +63,7 @@ class YamlFileLoader extends FileLoader private static $prototypeKeywords = array( 'resource' => 'resource', + 'namespace' => 'namespace', 'exclude' => 'exclude', 'parent' => 'parent', 'shared' => 'shared', @@ -560,12 +561,17 @@ private function parseDefinition($id, $service, $file, array $defaults) } } + if (array_key_exists('namespace', $service) && !array_key_exists('resource', $service)) { + throw new InvalidArgumentException(sprintf('A "resource" attribute must be set when the "namespace" attribute is set for service "%s" in %s. Check your YAML syntax.', $id, $file)); + } + if (array_key_exists('resource', $service)) { if (!is_string($service['resource'])) { throw new InvalidArgumentException(sprintf('A "resource" attribute must be of type string for service "%s" in %s. Check your YAML syntax.', $id, $file)); } $exclude = isset($service['exclude']) ? $service['exclude'] : null; - $this->registerClasses($definition, $id, $service['resource'], $exclude); + $namespace = isset($service['namespace']) ? $service['namespace'] : $id; + $this->registerClasses($definition, $namespace, $service['resource'], $exclude); } else { $this->setDefinition($id, $definition); } @@ -804,7 +810,7 @@ private function checkDefinition($id, array $definition, $file) { if ($throw = $this->isLoadingInstanceof) { $keywords = self::$instanceofKeywords; - } elseif ($throw = isset($definition['resource'])) { + } elseif ($throw = (isset($definition['resource']) || isset($definition['namespace']))) { $keywords = self::$prototypeKeywords; } else { $keywords = self::$serviceKeywords; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/OtherDir/Component1/Dir1/Service1.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/OtherDir/Component1/Dir1/Service1.php new file mode 100644 index 0000000000000..ef3f28abb8a05 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/OtherDir/Component1/Dir1/Service1.php @@ -0,0 +1,7 @@ +assertContains('reflection.Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Sub\Bar', $resources); } + public function testPrototypeWithNamespace() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_prototype_namespace.yml'); + + $ids = array_keys($container->getDefinitions()); + sort($ids); + + $this->assertSame(array( + Prototype\OtherDir\Component1\Dir1\Service1::class, + Prototype\OtherDir\Component1\Dir2\Service2::class, + Prototype\OtherDir\Component2\Dir1\Service4::class, + Prototype\OtherDir\Component2\Dir2\Service5::class, + 'service_container', + ), $ids); + + $this->assertTrue($container->getDefinition(Prototype\OtherDir\Component1\Dir1\Service1::class)->hasTag('foo')); + $this->assertTrue($container->getDefinition(Prototype\OtherDir\Component2\Dir1\Service4::class)->hasTag('foo')); + $this->assertFalse($container->getDefinition(Prototype\OtherDir\Component1\Dir1\Service1::class)->hasTag('bar')); + $this->assertFalse($container->getDefinition(Prototype\OtherDir\Component2\Dir1\Service4::class)->hasTag('bar')); + + $this->assertTrue($container->getDefinition(Prototype\OtherDir\Component1\Dir2\Service2::class)->hasTag('bar')); + $this->assertTrue($container->getDefinition(Prototype\OtherDir\Component2\Dir2\Service5::class)->hasTag('bar')); + $this->assertFalse($container->getDefinition(Prototype\OtherDir\Component1\Dir2\Service2::class)->hasTag('foo')); + $this->assertFalse($container->getDefinition(Prototype\OtherDir\Component2\Dir2\Service5::class)->hasTag('foo')); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessageRegExp /A "resource" attribute must be set when the "namespace" attribute is set for service ".+" in .+/ + */ + public function testPrototypeWithNamespaceAndNoResource() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_prototype_namespace_without_resource.yml'); + } + public function testDefaults() { $container = new ContainerBuilder();