diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index ffa15161e5645..e72d26b079427 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -26,6 +26,7 @@ use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; +use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\Config\Resource\ClassExistenceResource; @@ -123,6 +124,11 @@ class ContainerBuilder extends Container implements TaggedContainerInterface private $removedIds = array(); private $alreadyLoading = array(); + /** + * @var callable[] configurator class name => callable + */ + private $customConfiguratorFactories; + public function __construct(ParameterBagInterface $parameterBag = null) { parent::__construct($parameterBag); @@ -222,6 +228,38 @@ public function hasExtension($name) return isset($this->extensions[$name]) || isset($this->extensionsByNs[$name]); } + /** + * @param string $configuratorClassName + * @param callable $factory function (ContainerConfigurator $c) { .... } + */ + public function registerCustomConfiguratorFactory(string $configuratorClassName, callable $factory) + { + if (!class_exists($configuratorClassName)) { + throw new \LogicException(sprintf('Container configurator class %s does not exist.', $configuratorClassName)); + } + + if (isset($this->customConfiguratorFactories[$configuratorClassName])) { + throw new \LogicException(sprintf('Container configurator factory for %s was already registered.', $configuratorClassName)); + } + + $this->customConfiguratorFactories[$configuratorClassName] = $factory; + } + + /** + * @param string $configuratorClassName + * @param ContainerConfigurator $configurator + * + * @return mixed + */ + public function getCustomConfigurator(string $configuratorClassName, ContainerConfigurator $configurator) + { + if (!isset($this->customConfiguratorFactories[$configuratorClassName])) { + throw new \LogicException(sprintf('Container configurator factory for %s does not exist.', $configuratorClassName)); + } + + return call_user_func($this->customConfiguratorFactories[$configuratorClassName], $configurator); + } + /** * Returns an array of resources loaded to build this configuration. * diff --git a/src/Symfony/Component/DependencyInjection/Loader/PhpFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/PhpFileLoader.php index f0be7534ea67d..6aea38f56bbb4 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/PhpFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/PhpFileLoader.php @@ -44,7 +44,20 @@ public function load($resource, $type = null) $callback = $load($path); if ($callback instanceof \Closure) { - $callback(new ContainerConfigurator($this->container, $this, $this->instanceof, $path, $resource), $this->container, $this); + $method = new \ReflectionFunction($callback); + + if (0 === $method->getNumberOfParameters()) { + throw new \LogicException('Closure in %s:%d must have at least 1 argument.', $path, $method->getStartLine()); + } + + $configurator = new ContainerConfigurator($this->container, $this, $this->instanceof, $path, $resource); + $configuratorClass = $method->getParameters()[0]->getClass()->name; + + if (ContainerConfigurator::class !== $configuratorClass) { + $configurator = $this->container->getCustomConfigurator($configuratorClass, $configurator); + } + + $callback($configurator, $this->container, $this); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/FooContainerConfigurator.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/FooContainerConfigurator.php new file mode 100644 index 0000000000000..12cecd5171a2b --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/FooContainerConfigurator.php @@ -0,0 +1,20 @@ +configurator = $configurator; + } + + public function foo() + { + $this->configurator->parameters()->set('foo', 'bar'); + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/custom_configurator.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/custom_configurator.php new file mode 100644 index 0000000000000..1c98646148b6b --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/custom_configurator.php @@ -0,0 +1,7 @@ +foo(); +}; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php index 4d8f300cd9b50..8ef14ee08410d 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php @@ -15,8 +15,10 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Dumper\PhpDumper; use Symfony\Component\DependencyInjection\Dumper\YamlDumper; +use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\Tests\Fixtures\FooContainerConfigurator; class PhpFileLoaderTest extends TestCase { @@ -74,6 +76,23 @@ public function provideConfig() yield array('php7'); } + public function testCustomContainerConfigurator() + { + $container = new ContainerBuilder(); + $container->registerCustomConfiguratorFactory( + FooContainerConfigurator::class, + function (ContainerConfigurator $c) { + return new FooContainerConfigurator($c); + } + ); + + $fixtures = realpath(__DIR__.'/../Fixtures'); + $loader = new PhpFileLoader($container, new FileLocator()); + $loader->load($fixtures.'/config/custom_configurator.php'); + + $this->assertEquals('bar', $container->getParameter('foo')); + } + /** * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException * @expectedExceptionMessage The service "child_service" cannot have a "parent" and also have "autoconfigure". Try disabling autoconfiguration for the service.