From 59c75bad7bd1c341d394e5a29ee67937e31ced71 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 12 Apr 2021 16:24:08 +0200 Subject: [PATCH] [DI] add `#[When(env: 'foo')]` to skip autoregistering a class when the env doesn't match --- .../DependencyInjection/Attribute/When.php | 26 +++++++++++++++++++ .../DependencyInjection/CHANGELOG.md | 1 + .../DependencyInjection/Loader/FileLoader.php | 18 ++++++++++++- .../Loader/XmlFileLoader.php | 8 +++++- .../Loader/YamlFileLoader.php | 8 +++++- .../Tests/Fixtures/Prototype/Foo.php | 4 +++ .../Tests/Loader/FileLoaderTest.php | 21 +++++++++++++++ 7 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/Attribute/When.php diff --git a/src/Symfony/Component/DependencyInjection/Attribute/When.php b/src/Symfony/Component/DependencyInjection/Attribute/When.php new file mode 100644 index 0000000000..8cb01c87e1 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Attribute/When.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Attribute; + +/** + * An attribute to tell under which environement this class should be registered as a service. + * + * @author Nicolas Grekas + */ +#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)] +class When +{ + public function __construct( + public string $env, + ) { + } +} diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index ad5e8f0ce9..59b6454b0c 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -13,6 +13,7 @@ CHANGELOG * Add support for per-env configuration in XML and Yaml loaders * Add `ContainerBuilder::willBeAvailable()` to help with conditional configuration * Add support an integer return value for default_index_method + * Add `#[When(env: 'foo')]` to skip autoregistering a class when the env doesn't match * Add `env()` and `EnvConfigurator` in the PHP-DSL * Add support for `ConfigBuilder` in the `PhpFileLoader` * Add `ContainerConfigurator::env()` to get the current environment diff --git a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php index 75606c5abf..2d51cc492d 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php @@ -17,6 +17,7 @@ use Symfony\Component\Config\FileLocatorInterface; use Symfony\Component\Config\Loader\FileLoader as BaseFileLoader; use Symfony\Component\Config\Loader\Loader; use Symfony\Component\Config\Resource\GlobResource; +use Symfony\Component\DependencyInjection\Attribute\When; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\RegisterAutoconfigureAttributesPass; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -98,11 +99,26 @@ abstract class FileLoader extends BaseFileLoader } $autoconfigureAttributes = new RegisterAutoconfigureAttributesPass(); - $classes = $this->findClasses($namespace, $resource, (array) $exclude, $autoconfigureAttributes->accept($prototype) ? $autoconfigureAttributes : null); + $autoconfigureAttributes = $autoconfigureAttributes->accept($prototype) ? $autoconfigureAttributes : null; + $classes = $this->findClasses($namespace, $resource, (array) $exclude, $autoconfigureAttributes); // prepare for deep cloning $serializedPrototype = serialize($prototype); foreach ($classes as $class => $errorMessage) { + if ($autoconfigureAttributes && $this->env) { + $r = $this->container->getReflectionClass($class); + $attribute = null; + foreach ($r->getAttributes(When::class) as $attribute) { + if ($this->env === $attribute->newInstance()->env) { + $attribute = null; + break; + } + } + if (null !== $attribute) { + continue; + } + } + if (interface_exists($class, false)) { $this->interfaces[] = $class; } else { diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index b694b7791c..be1b6be63a 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -50,7 +50,13 @@ class XmlFileLoader extends FileLoader $this->container->fileExists($path); - $this->loadXml($xml, $path); + $env = $this->env; + $this->env = null; + try { + $this->loadXml($xml, $path); + } finally { + $this->env = $env; + } if ($this->env) { $xpath = new \DOMXPath($xml); diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index b5a99db5b1..fde5d9dadb 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -129,7 +129,13 @@ class YamlFileLoader extends FileLoader return; } - $this->loadContent($content, $path); + $env = $this->env; + $this->env = null; + try { + $this->loadContent($content, $path); + } finally { + $this->env = $env; + } // per-env configuration if ($this->env && isset($content['when@'.$this->env])) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/Foo.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/Foo.php index b6690a8d26..c1afea70d4 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/Foo.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/Foo.php @@ -2,6 +2,10 @@ namespace Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype; +use Symfony\Component\DependencyInjection\Attribute\When; + +#[When(env: 'prod')] +#[When(env: 'dev')] class Foo implements FooInterface, Sub\BarInterface { public function __construct($bar = null) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php index fbf825e299..8faa1d1e1f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php @@ -243,6 +243,27 @@ class FileLoaderTest extends TestCase 'yaml/*' ); } + + /** + * @requires PHP 8 + * + * @testWith ["prod", true] + * ["dev", true] + * ["bar", false] + * [null, true] + */ + public function testRegisterClassesWithWhenEnv(?string $env, bool $expected) + { + $container = new ContainerBuilder(); + $loader = new TestFileLoader($container, new FileLocator(self::$fixturesPath.'/Fixtures'), $env); + $loader->registerClasses( + (new Definition())->setAutoconfigured(true), + 'Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\\', + 'Prototype/{Foo.php}' + ); + + $this->assertSame($expected, $container->has(Foo::class)); + } } class TestFileLoader extends FileLoader