diff --git a/composer.json b/composer.json index 5cbae051924f0..3406f835f7bde 100644 --- a/composer.json +++ b/composer.json @@ -57,6 +57,7 @@ "symfony/security-bundle": "self.version", "symfony/serializer": "self.version", "symfony/stopwatch": "self.version", + "symfony/superclosure-bridge": "self.version", "symfony/swiftmailer-bridge": "self.version", "symfony/templating": "self.version", "symfony/translation": "self.version", @@ -76,7 +77,8 @@ "propel/propel1": "1.6.*", "ircmaxell/password-compat": "1.0.*", "ocramius/proxy-manager": ">=0.3.1,<0.6-dev", - "egulias/email-validator": "~1.2" + "egulias/email-validator": "~1.2", + "jeremeamia/superclosure": "~1.0" }, "autoload": { "psr-0": { "Symfony\\": "src/" }, diff --git a/src/Symfony/Bridge/SuperClosure/.gitignore b/src/Symfony/Bridge/SuperClosure/.gitignore new file mode 100644 index 0000000000000..c49a5d8df5c65 --- /dev/null +++ b/src/Symfony/Bridge/SuperClosure/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/src/Symfony/Bridge/SuperClosure/CHANGELOG.md b/src/Symfony/Bridge/SuperClosure/CHANGELOG.md new file mode 100644 index 0000000000000..22f1ed8ae0862 --- /dev/null +++ b/src/Symfony/Bridge/SuperClosure/CHANGELOG.md @@ -0,0 +1,7 @@ +CHANGELOG +========= + +2.6.0 +----- + + * Added the `Symfony\Bridge\SuperClosure` bridge diff --git a/src/Symfony/Bridge/SuperClosure/ClosureDumper/SuperClosureDumper.php b/src/Symfony/Bridge/SuperClosure/ClosureDumper/SuperClosureDumper.php new file mode 100644 index 0000000000000..ceafac0799943 --- /dev/null +++ b/src/Symfony/Bridge/SuperClosure/ClosureDumper/SuperClosureDumper.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\SuperClosure\ClosureDumper; + +use Jeremeamia\SuperClosure\ClosureParser; +use Symfony\Component\DependencyInjection\Dumper\ClosureDumper\ClosureDumperInterface; +use Symfony\Component\DependencyInjection\Exception\DumpingClosureException; + +/** + * @author Nikita Konstantinov + */ +class SuperClosureDumper implements ClosureDumperInterface +{ + /** + * {@inheritdoc} + */ + public function dump(\Closure $closure) + { + $parser = ClosureParser::fromClosure($closure); + + if ($parser->getUsedVariables()) { + throw new DumpingClosureException($closure, 'Closure must not depend on context (use statement)'); + } + + try { + // Remove trailing ";" + return substr($parser->getCode(), 0, -1); + } catch (\Exception $e) { + throw new DumpingClosureException($closure, $e->getMessage(), 0, $e); + } + } +} diff --git a/src/Symfony/Bridge/SuperClosure/LICENSE b/src/Symfony/Bridge/SuperClosure/LICENSE new file mode 100644 index 0000000000000..0b3292cf90235 --- /dev/null +++ b/src/Symfony/Bridge/SuperClosure/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2014 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/Symfony/Bridge/SuperClosure/Tests/Fixtures/php/services_with_closure_factory.php b/src/Symfony/Bridge/SuperClosure/Tests/Fixtures/php/services_with_closure_factory.php new file mode 100644 index 0000000000000..e4ded784632b8 --- /dev/null +++ b/src/Symfony/Bridge/SuperClosure/Tests/Fixtures/php/services_with_closure_factory.php @@ -0,0 +1,129 @@ + 42, + ); + + /** + * Constructor. + */ + public function __construct() + { + $this->services = + $this->scopedServices = + $this->scopeStacks = array(); + + $this->set('service_container', $this); + + $this->scopes = array(); + $this->scopeChildren = array(); + $this->methodMap = array( + 'bar' => 'getBarService', + 'foo' => 'getFooService', + ); + + $this->aliases = array(); + } + + /** + * {@inheritdoc} + */ + public function compile() + { + throw new LogicException('You cannot compile a dumped frozen container.'); + } + + /** + * Gets the 'bar' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \stdClass A stdClass instance. + */ + protected function getBarService() + { + return $this->services['bar'] = call_user_func(function (\stdClass $foo) { + $bar = clone $foo; + $bar->bar = 'bar'; + return $bar; +}, $this->get('foo')); + } + + /** + * Gets the 'foo' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \stdClass A stdClass instance. + */ + protected function getFooService() + { + return $this->services['foo'] = call_user_func(function ($baz) { + $foo = new \stdClass(); + $foo->foo = $baz; + return $foo; +}, 42); + } + + /** + * {@inheritdoc} + */ + public function getParameter($name) + { + $name = strtolower($name); + + if (!(isset(self::$parameters[$name]) || array_key_exists($name, self::$parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); + } + + return self::$parameters[$name]; + } + + /** + * {@inheritdoc} + */ + public function hasParameter($name) + { + $name = strtolower($name); + + return isset(self::$parameters[$name]) || array_key_exists($name, self::$parameters); + } + + /** + * {@inheritdoc} + */ + public function setParameter($name, $value) + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + /** + * {@inheritdoc} + */ + public function getParameterBag() + { + if (null === $this->parameterBag) { + $this->parameterBag = new FrozenParameterBag(self::$parameters); + } + + return $this->parameterBag; + } +} diff --git a/src/Symfony/Bridge/SuperClosure/Tests/PhpDumperTest.php b/src/Symfony/Bridge/SuperClosure/Tests/PhpDumperTest.php new file mode 100644 index 0000000000000..539c09641b23c --- /dev/null +++ b/src/Symfony/Bridge/SuperClosure/Tests/PhpDumperTest.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\SuperClosure\Tests; + +use Symfony\Bridge\SuperClosure\ClosureDumper\SuperClosureDumper; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Dumper\PhpDumper; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Integration tests of {@see \Symfony\Component\DependencyInjection\Dumper\PhpDumper} and SuperClosureDumper + * + * @author Nikita Konstantinov + */ +class PhpDumperTest extends \PHPUnit_Framework_TestCase +{ + public function testThatPhpDumperCanDumpClosure() + { + $container = new ContainerBuilder(); + + $container->setParameter('baz', 42); + + $container + ->register('foo', 'stdClass') + ->setFactory(function ($baz) { + $foo = new \stdClass(); + $foo->foo = $baz; + + return $foo; + }) + ->addArgument('%baz%') + ; + + $container + ->register('bar', 'stdClass') + ->setFactory(function (\stdClass $foo) { + $bar = clone $foo; + $bar->bar = 'bar'; + + return $bar; + }) + ->addArgument(new Reference('foo')) + ; + + $container->compile(); + + $dumper = new PhpDumper($container); + $dumper->setClosureDumper(new SuperClosureDumper()); + + $options = array('class' => 'ProjectServiceContainerWithClosures'); + + $this->assertStringEqualsFile(__DIR__.'/Fixtures/php/services_with_closure_factory.php', $dumper->dump($options)); + } + + public function testThatDumpedContainerWorks() + { + require_once __DIR__.'/Fixtures/php/services_with_closure_factory.php'; + + $container = new \ProjectServiceContainerWithClosures(); + + $expectedBar = new \stdClass(); + $expectedBar->foo = 42; + $expectedBar->bar = 'bar'; + + $this->assertEquals($expectedBar, $container->get('bar')); + } +} diff --git a/src/Symfony/Bridge/SuperClosure/Tests/SuperClosureDumperTest.php b/src/Symfony/Bridge/SuperClosure/Tests/SuperClosureDumperTest.php new file mode 100644 index 0000000000000..156d1a8ab3409 --- /dev/null +++ b/src/Symfony/Bridge/SuperClosure/Tests/SuperClosureDumperTest.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\SuperClosure\Tests; + +use Symfony\Bridge\SuperClosure\ClosureDumper\SuperClosureDumper; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * @author Nikita Konstantinov + */ +class SuperClosureDumperTest extends \PHPUnit_Framework_TestCase +{ + public function testThatClosureDumps() + { + $dumper = new SuperClosureDumper(); + + $expectedCode = <<<'CODE' +function (\Symfony\Component\DependencyInjection\ContainerInterface $container) { + return $container->get('foo'); +} +CODE; + + $actualCode = $dumper->dump(function (ContainerInterface $container) { + return $container->get('foo'); + }); + + $this->assertSame($expectedCode, $actualCode); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\DumpingClosureException + */ + public function testThatContextDependentClosureCannotBeDumped() + { + $dumper = new SuperClosureDumper(); + + $dumper->dump(function () use ($dumper) { + return new \stdClass(); + }); + } +} diff --git a/src/Symfony/Bridge/SuperClosure/composer.json b/src/Symfony/Bridge/SuperClosure/composer.json new file mode 100644 index 0000000000000..48a7d2e3c92cc --- /dev/null +++ b/src/Symfony/Bridge/SuperClosure/composer.json @@ -0,0 +1,33 @@ +{ + "name": "symfony/superclosure-bridge", + "type": "symfony-bridge", + "description": "Symfony SuperClosure Bridge", + "keywords": [], + "homepage": "http://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3", + "symfony/dependency-injection": "~2.6", + "jeremeamia/superclosure": "~1.0" + }, + "autoload": { + "psr-0": { "Symfony\\Bridge\\SuperClosure\\": "" } + }, + "target-dir": "Symfony/Bridge/SuperClosure", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + } +} diff --git a/src/Symfony/Bridge/SuperClosure/phpunit.xml.dist b/src/Symfony/Bridge/SuperClosure/phpunit.xml.dist new file mode 100644 index 0000000000000..a70dc4600a6b2 --- /dev/null +++ b/src/Symfony/Bridge/SuperClosure/phpunit.xml.dist @@ -0,0 +1,25 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 1d091a097f2b1..1bf5d60dea0c6 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -948,6 +948,8 @@ public function createService(Definition $definition, $id, $tryProxy = true) $callable = $definition->getFactory(); } elseif (is_array($factory)) { $callable = array($this->resolveServices($factory[0]), $factory[1]); + } elseif ($factory instanceof \Closure) { + $callable = $factory; } else { throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory', $id)); } diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php index b77a13737a5bb..72becec85e647 100644 --- a/src/Symfony/Component/DependencyInjection/Definition.php +++ b/src/Symfony/Component/DependencyInjection/Definition.php @@ -60,7 +60,7 @@ public function __construct($class = null, array $arguments = array()) /** * Sets a factory. * - * @param string|array $factory A PHP function or an array containing a class/Reference and a method to call + * @param string|array|\Closure $factory A PHP function/closure or an array containing a class/Reference and a method to call * * @return Definition The current instance */ @@ -78,7 +78,7 @@ public function setFactory($factory) /** * Gets the factory. * - * @return string|array The PHP function or an array containing a class/Reference and a method to call + * @return string|array|\Closure The PHP function/closure or an array containing a class/Reference and a method to call */ public function getFactory() { @@ -141,7 +141,7 @@ public function setFactoryMethod($factoryMethod) * * @return Definition The current instance * - * @throws InvalidArgumentException In case the decorated service id and the new decorated service id are equals. + * @throws \InvalidArgumentException In case the decorated service id and the new decorated service id are equals. */ public function setDecoratedService($id, $renamedId = null) { diff --git a/src/Symfony/Component/DependencyInjection/Dumper/ClosureDumper/ClosureDumperInterface.php b/src/Symfony/Component/DependencyInjection/Dumper/ClosureDumper/ClosureDumperInterface.php new file mode 100644 index 0000000000000..a3d2d8e272b14 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Dumper/ClosureDumper/ClosureDumperInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Dumper\ClosureDumper; + +use Symfony\Component\DependencyInjection\Exception\DumpingClosureException; + +/** + * @author Nikita Konstantinov + * + * @api + */ +interface ClosureDumperInterface +{ + /** + * @param \Closure $closure + * @return string + * + * @throws DumpingClosureException If closure couldn't be dumped + */ + public function dump(\Closure $closure); +} diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index ba90bf514e864..c810543e6913f 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Dumper; +use Symfony\Component\DependencyInjection\Dumper\ClosureDumper\ClosureDumperInterface; use Symfony\Component\DependencyInjection\Variable; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -66,6 +67,11 @@ class PhpDumper extends Dumper */ private $proxyDumper; + /** + * @var ClosureDumperInterface + */ + private $closureDumper; + /** * {@inheritdoc} * @@ -88,6 +94,16 @@ public function setProxyDumper(ProxyDumper $proxyDumper) $this->proxyDumper = $proxyDumper; } + /** + * Sets the dumper of closures + * + * @param ClosureDumperInterface $closureDumper + */ + public function setClosureDumper(ClosureDumperInterface $closureDumper) + { + $this->closureDumper = $closureDumper; + } + /** * Dumps the service container as a PHP class. * @@ -721,6 +737,7 @@ private function addNewInstance($id, Definition $definition, $return, $instantia if (null !== $definition->getFactory()) { $callable = $definition->getFactory(); + if (is_array($callable)) { if ($callable[0] instanceof Reference || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))) { @@ -736,6 +753,12 @@ private function addNewInstance($id, Definition $definition, $return, $instantia return sprintf(" $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? ', '.implode(', ', $arguments) : ''); } + if ($callable instanceof \Closure) { + $closureCode = $this->getClosureDumper()->dump($callable); + + return sprintf(" $return{$instantiation}call_user_func(%s%s);\n", $closureCode, $arguments ? ', '.implode(', ', $arguments) : ''); + } + return sprintf(" $return{$instantiation}\\%s(%s);\n", $callable, $arguments ? implode(', ', $arguments) : ''); } elseif (null !== $definition->getFactoryMethod()) { if (null !== $definition->getFactoryClass()) { @@ -1262,7 +1285,7 @@ private function dumpValue($value, $interpolate = true) } if ($factory[0] instanceof Definition) { - return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($factory[0]), $factory[1], count($arguments) > 0 ? ', '.implode(', ', $arguments) : ''); + return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($factory[0]), $factory[1], $arguments ? ', '.implode(', ', $arguments) : ''); } if ($factory[0] instanceof Reference) { @@ -1270,6 +1293,12 @@ private function dumpValue($value, $interpolate = true) } } + if ($factory instanceof \Closure) { + $closureCode = $this->getClosureDumper()->dump($factory); + + return sprintf('call_user_func(%s%s)', $closureCode, $arguments ? ', '.implode(', ', $arguments) : ''); + } + throw new RuntimeException('Cannot dump definition because of invalid factory'); } @@ -1445,4 +1474,13 @@ private function getExpressionLanguage() return $this->expressionLanguage; } + + private function getClosureDumper() + { + if ($this->closureDumper === null) { + throw new RuntimeException('PhpDumper requires a ClosureParserInterface implementation set in order to dump closures'); + } + + return $this->closureDumper; + } } diff --git a/src/Symfony/Component/DependencyInjection/Exception/DumpingClosureException.php b/src/Symfony/Component/DependencyInjection/Exception/DumpingClosureException.php new file mode 100644 index 0000000000000..dae5508aeda15 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Exception/DumpingClosureException.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Exception; + +/** + * This exception is thrown when closure is not dumpable, e.g. if closure depends on context + * + * @author Nikita Konstantinov + */ +class DumpingClosureException extends \RuntimeException implements ExceptionInterface +{ + public function __construct(\Closure $closure, $message, $code = 0, \Exception $previous = null) + { + $reflection = new \ReflectionFunction($closure); + + parent::__construct(sprintf( + 'Closure defined in %s at line %d could not be dumped: %s', + $reflection->getFileName(), + $reflection->getStartLine(), + $message + ), $code, $previous); + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 6e839653aa024..cc45b068f0869 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -396,6 +396,23 @@ public function testCreateServiceConfigurator() } } + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::createService + */ + public function testCreateServiceByClosure() + { + $builder = new ContainerBuilder(); + $builder->register('bar', 'stdClass'); + $builder->register('foo', 'Bar\FooClass')->setFactory(function (\stdClass $bar) { + $foo = new \Bar\FooClass(); + $foo->setBar($bar); + + return $foo; + })->addArgument(new Reference('bar')); + + $this->assertSame($builder->get('bar'), $builder->get('foo')->bar, '->createService() creates service from a closure with passed service as an argument'); + } + /** * @covers Symfony\Component\DependencyInjection\ContainerBuilder::createService * @expectedException \RuntimeException diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 6cb04446e093a..c910496875bb9 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -204,4 +204,56 @@ public function testCircularReference() $dumper = new PhpDumper($container); $dumper->dump(); } + + public function testClosureAsFactoryMethod() + { + $container = new ContainerBuilder(); + + $container->register('foo', 'stdClass')->setFactory( + function () { + return new \stdClass(); + } + ); + + $container->register('bar', 'stdClass')->setFactory( + function (\stdClass $foo) { + $bar = clone $foo; + $bar->bar = 42; + + return $bar; + } + )->addArgument(new Reference('foo')); + + $closureDumperMock = $this->getMockForAbstractClass('Symfony\Component\DependencyInjection\Dumper\ClosureDumper\ClosureDumperInterface'); + + $closureDumperMock + ->expects($this->at(0)) + ->method('dump') + ->will($this->returnValue( + <<<'CODE' +function (\stdClass $foo) { + $bar = clone $foo; + $bar->bar = 42; + + return $bar; + } +CODE + )); + + $closureDumperMock + ->expects($this->at(1)) + ->method('dump') + ->will($this->returnValue( + <<<'CODE' +function () { + return new \stdClass(); + } +CODE + )); + + $dumper = new PhpDumper($container); + $dumper->setClosureDumper($closureDumperMock); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services12.php', $dumper->dump()); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php new file mode 100644 index 0000000000000..1bd370f89f649 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php @@ -0,0 +1,67 @@ +methodMap = array( + 'bar' => 'getBarService', + 'foo' => 'getFooService', + ); + } + + /** + * Gets the 'bar' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \stdClass A stdClass instance. + */ + protected function getBarService() + { + return $this->services['bar'] = call_user_func(function (\stdClass $foo) { + $bar = clone $foo; + $bar->bar = 42; + + return $bar; + }, $this->get('foo')); + } + + /** + * Gets the 'foo' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \stdClass A stdClass instance. + */ + protected function getFooService() + { + return $this->services['foo'] = call_user_func(function () { + return new \stdClass(); + }); + } +} diff --git a/src/Symfony/Component/DependencyInjection/composer.json b/src/Symfony/Component/DependencyInjection/composer.json index c0090495a1bff..7b7d21db4227b 100644 --- a/src/Symfony/Component/DependencyInjection/composer.json +++ b/src/Symfony/Component/DependencyInjection/composer.json @@ -26,7 +26,8 @@ "suggest": { "symfony/yaml": "", "symfony/config": "", - "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them" + "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", + "symfony/superclosure-bridge": "Allow to dump closure factories" }, "autoload": { "psr-0": { "Symfony\\Component\\DependencyInjection\\": "" } diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index fa2275ad0806e..eed3d0801c6a2 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -13,6 +13,7 @@ use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator; use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper; +use Symfony\Bridge\SuperClosure\ClosureDumper\SuperClosureDumper; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Dumper\PhpDumper; @@ -710,6 +711,13 @@ protected function dumpContainer(ConfigCache $cache, ContainerBuilder $container $dumper->setProxyDumper(new ProxyDumper()); } + if ( + class_exists('Symfony\Bridge\SuperClosure\ClosureDumper\SuperClosureDumper') + && class_exists('Jeremeamia\SuperClosure\ClosureParser') + ) { + $dumper->setClosureDumper(new SuperClosureDumper()); + } + $content = $dumper->dump(array('class' => $class, 'base_class' => $baseClass)); if (!$this->debug) { $content = static::stripComments($content);