feature #40782 [DependencyInjection] Add #[When(env: 'foo')]
to skip autoregistering a class when the env doesn't match (nicolas-grekas)
This PR was merged into the 5.3-dev branch.
Discussion
----------
[DependencyInjection] Add `#[When(env: 'foo')]` to skip autoregistering a class when the env doesn't match
| Q | A
| ------------- | ---
| Branch? | 5.x
| Bug fix? | no
| New feature? | yes
| Deprecations? | no
| Tickets | -
| License | MIT
| Doc PR | -
This is a follow up of #40214, in order to conditionally auto-register classes.
By adding a `#[When(env: prod)]` annotation on a class, one can tell that a class should be skipped when the current env doesn't match the one declared in the attribute.
This saves from writing similar conditional configuration by using the per-env `services_prod.yaml` convention (+corresponding exclusion from `services.yaml`), or some logic in the Kernel.
Commits
-------
59c75bad7b
[DI] add `#[When(env: 'foo')]` to skip autoregistering a class when the env doesn't match
This commit is contained in:
commit
4cac9cf829
26
src/Symfony/Component/DependencyInjection/Attribute/When.php
Normal file
26
src/Symfony/Component/DependencyInjection/Attribute/When.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?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\Attribute;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An attribute to tell under which environement this class should be registered as a service.
|
||||||
|
*
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)]
|
||||||
|
class When
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public string $env,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@ CHANGELOG
|
|||||||
* Add support for per-env configuration in XML and Yaml loaders
|
* Add support for per-env configuration in XML and Yaml loaders
|
||||||
* Add `ContainerBuilder::willBeAvailable()` to help with conditional configuration
|
* Add `ContainerBuilder::willBeAvailable()` to help with conditional configuration
|
||||||
* Add support an integer return value for default_index_method
|
* 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 `env()` and `EnvConfigurator` in the PHP-DSL
|
||||||
* Add support for `ConfigBuilder` in the `PhpFileLoader`
|
* Add support for `ConfigBuilder` in the `PhpFileLoader`
|
||||||
* Add `ContainerConfigurator::env()` to get the current environment
|
* Add `ContainerConfigurator::env()` to get the current environment
|
||||||
|
@ -17,6 +17,7 @@ use Symfony\Component\Config\FileLocatorInterface;
|
|||||||
use Symfony\Component\Config\Loader\FileLoader as BaseFileLoader;
|
use Symfony\Component\Config\Loader\FileLoader as BaseFileLoader;
|
||||||
use Symfony\Component\Config\Loader\Loader;
|
use Symfony\Component\Config\Loader\Loader;
|
||||||
use Symfony\Component\Config\Resource\GlobResource;
|
use Symfony\Component\Config\Resource\GlobResource;
|
||||||
|
use Symfony\Component\DependencyInjection\Attribute\When;
|
||||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||||
use Symfony\Component\DependencyInjection\Compiler\RegisterAutoconfigureAttributesPass;
|
use Symfony\Component\DependencyInjection\Compiler\RegisterAutoconfigureAttributesPass;
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
@ -98,11 +99,26 @@ abstract class FileLoader extends BaseFileLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
$autoconfigureAttributes = new RegisterAutoconfigureAttributesPass();
|
$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
|
// prepare for deep cloning
|
||||||
$serializedPrototype = serialize($prototype);
|
$serializedPrototype = serialize($prototype);
|
||||||
|
|
||||||
foreach ($classes as $class => $errorMessage) {
|
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)) {
|
if (interface_exists($class, false)) {
|
||||||
$this->interfaces[] = $class;
|
$this->interfaces[] = $class;
|
||||||
} else {
|
} else {
|
||||||
|
@ -50,7 +50,13 @@ class XmlFileLoader extends FileLoader
|
|||||||
|
|
||||||
$this->container->fileExists($path);
|
$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) {
|
if ($this->env) {
|
||||||
$xpath = new \DOMXPath($xml);
|
$xpath = new \DOMXPath($xml);
|
||||||
|
@ -129,7 +129,13 @@ class YamlFileLoader extends FileLoader
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->loadContent($content, $path);
|
$env = $this->env;
|
||||||
|
$this->env = null;
|
||||||
|
try {
|
||||||
|
$this->loadContent($content, $path);
|
||||||
|
} finally {
|
||||||
|
$this->env = $env;
|
||||||
|
}
|
||||||
|
|
||||||
// per-env configuration
|
// per-env configuration
|
||||||
if ($this->env && isset($content['when@'.$this->env])) {
|
if ($this->env && isset($content['when@'.$this->env])) {
|
||||||
|
@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype;
|
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
|
class Foo implements FooInterface, Sub\BarInterface
|
||||||
{
|
{
|
||||||
public function __construct($bar = null)
|
public function __construct($bar = null)
|
||||||
|
@ -243,6 +243,27 @@ class FileLoaderTest extends TestCase
|
|||||||
'yaml/*'
|
'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
|
class TestFileLoader extends FileLoader
|
||||||
|
Reference in New Issue
Block a user