feature #41182 [DependencyInjection] allow PHP-DSL files to be env-conditional (nicolas-grekas)
This PR was merged into the 5.3-dev branch.
Discussion
----------
[DependencyInjection] allow PHP-DSL files to be env-conditional
| Q | A
| ------------- | ---
| Branch? | 5.x
| Bug fix? | no
| New feature? | yes
| Deprecations? | no
| Tickets | -
| License | MIT
| Doc PR | -
This PR makes config builders compatible with conditional configuration based on the $env.
See fixture for an example:
```php
use Symfony\Component\DependencyInjection\Tests\Fixtures\AcmeConfig;
if ('prod' !== $env) {
return;
}
return static function (AcmeConfig $config) {
$config->color('blue');
};
```
On PHP8, the PR to allow using `#[When(env: prod')]`:
```php
use Symfony\Component\DependencyInjection\Attribute\When;
use Symfony\Component\DependencyInjection\Tests\Fixtures\AcmeConfig;
return #[When(env: 'prod')] function (AcmeConfig $config) {
$config->color('blue');
};
```
Without this patch, such a config file cannot be used if AcmeBundle is not loaded in the current $env.
This is a follow up of https://symfony.com/blog/new-in-symfony-5-3-configure-multiple-environments-in-a-single-file#comment-24521 by `@a`-menshchikov
Commits
-------
29173a91d1
[DependencyInjection] inject $env in the scope of PHP-DSL files
This commit is contained in:
commit
b14d76921f
@ -16,7 +16,7 @@ namespace Symfony\Component\DependencyInjection\Attribute;
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)]
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
|
||||
class When
|
||||
{
|
||||
public function __construct(
|
||||
|
@ -15,6 +15,7 @@ use Symfony\Component\Config\Builder\ConfigBuilderGenerator;
|
||||
use Symfony\Component\Config\Builder\ConfigBuilderGeneratorInterface;
|
||||
use Symfony\Component\Config\Builder\ConfigBuilderInterface;
|
||||
use Symfony\Component\Config\FileLocatorInterface;
|
||||
use Symfony\Component\DependencyInjection\Attribute\When;
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
@ -55,12 +56,12 @@ class PhpFileLoader extends FileLoader
|
||||
$this->container->fileExists($path);
|
||||
|
||||
// the closure forbids access to the private scope in the included file
|
||||
$load = \Closure::bind(function ($path) use ($container, $loader, $resource, $type) {
|
||||
$load = \Closure::bind(function ($path, $env) use ($container, $loader, $resource, $type) {
|
||||
return include $path;
|
||||
}, $this, ProtectedPhpFileLoader::class);
|
||||
|
||||
try {
|
||||
$callback = $load($path);
|
||||
$callback = $load($path, $this->env);
|
||||
|
||||
if (\is_object($callback) && \is_callable($callback)) {
|
||||
$this->executeCallback($callback, new ContainerConfigurator($this->container, $this, $this->instanceof, $path, $resource, $this->env), $path);
|
||||
@ -98,8 +99,22 @@ class PhpFileLoader extends FileLoader
|
||||
|
||||
$arguments = [];
|
||||
$configBuilders = [];
|
||||
$parameters = (new \ReflectionFunction($callback))->getParameters();
|
||||
foreach ($parameters as $parameter) {
|
||||
$r = new \ReflectionFunction($callback);
|
||||
|
||||
if (\PHP_VERSION_ID >= 80000) {
|
||||
$attribute = null;
|
||||
foreach ($r->getAttributes(When::class) as $attribute) {
|
||||
if ($this->env === $attribute->newInstance()->env) {
|
||||
$attribute = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (null !== $attribute) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($r->getParameters() as $parameter) {
|
||||
$reflectionType = $parameter->getType();
|
||||
if (!$reflectionType instanceof \ReflectionNamedType) {
|
||||
throw new \InvalidArgumentException(sprintf('Could not resolve argument "$%s" for "%s". You must typehint it (for example with "%s" or "%s").', $parameter->getName(), $path, ContainerConfigurator::class, ContainerBuilder::class));
|
||||
|
@ -2,6 +2,10 @@
|
||||
|
||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\AcmeConfig;
|
||||
|
||||
return static function (AcmeConfig $config) {
|
||||
if ('prod' !== $env) {
|
||||
return;
|
||||
}
|
||||
|
||||
return function (AcmeConfig $config) {
|
||||
$config->color('blue');
|
||||
};
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
|
||||
|
||||
return static function (ContainerConfigurator $configurator): void {
|
||||
return function (ContainerConfigurator $configurator): void {
|
||||
$services = $configurator->services();
|
||||
|
||||
$services
|
||||
|
@ -2,6 +2,6 @@
|
||||
|
||||
use Symfony\Config\AcmeConfig\NestedConfig;
|
||||
|
||||
return static function (NestedConfig $config) {
|
||||
return function (NestedConfig $config) {
|
||||
throw new RuntimeException('This code should not be run.');
|
||||
};
|
||||
|
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\DependencyInjection\Attribute\When;
|
||||
|
||||
return #[When(env: 'prod')] function () {
|
||||
throw new RuntimeException('This code should not be run.');
|
||||
};
|
@ -176,4 +176,16 @@ class PhpFileLoaderTest extends TestCase
|
||||
|
||||
$loader->load($fixtures.'/config/nested_bundle_config.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires PHP 8
|
||||
*/
|
||||
public function testWhenEnv()
|
||||
{
|
||||
$fixtures = realpath(__DIR__.'/../Fixtures');
|
||||
$container = new ContainerBuilder();
|
||||
$loader = new PhpFileLoader($container, new FileLocator(), 'dev', new ConfigBuilderGenerator(sys_get_temp_dir()));
|
||||
|
||||
$loader->load($fixtures.'/config/when_env.php');
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user