From f503ad8026457d91a503c86f6113589be1183db4 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sat, 30 Mar 2013 00:21:12 +0100 Subject: [PATCH 01/21] First implementation of lazy services via proxy manager --- composer.json | 1 + .../DependencyInjection/ContainerBuilder.php | 19 +++++++++++- .../DependencyInjection/Definition.php | 30 +++++++++++++++++++ .../DependencyInjection/Dumper/XmlDumper.php | 3 ++ .../DependencyInjection/Dumper/YamlDumper.php | 4 +++ .../Loader/XmlFileLoader.php | 2 +- .../Loader/YamlFileLoader.php | 4 +++ .../schema/dic/services/services-1.0.xsd | 1 + .../Tests/ContainerBuilderTest.php | 24 +++++++++++++++ .../Tests/DefinitionTest.php | 12 ++++++++ .../Tests/Fixtures/xml/services6.xml | 2 +- .../Tests/Fixtures/yaml/services6.yml | 1 + .../Tests/Loader/XmlFileLoaderTest.php | 1 + .../Tests/Loader/YamlFileLoaderTest.php | 1 + 14 files changed, 102 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 504b48d92c76a..a102f4705c448 100644 --- a/composer.json +++ b/composer.json @@ -61,6 +61,7 @@ "symfony/yaml": "self.version" }, "require-dev": { + "ocramius/proxy-manager": "0.2.*", "doctrine/data-fixtures": "1.0.*", "doctrine/dbal": "~2.2", "doctrine/orm": "~2.2,>=2.2.3", diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 5d8d0d1a10f15..9ce9a18595635 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -11,6 +11,9 @@ namespace Symfony\Component\DependencyInjection; +use ProxyManager\Configuration; +use ProxyManager\Factory\LazyLoadingValueHolderFactory; +use ProxyManager\Proxy\LazyLoadingInterface; use Symfony\Component\DependencyInjection\Compiler\Compiler; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Compiler\PassConfig; @@ -873,8 +876,22 @@ public function findDefinition($id) * @throws RuntimeException When the service is a synthetic service * @throws InvalidArgumentException When configure callable is not callable */ - private function createService(Definition $definition, $id) + public function createService(Definition $definition, $id, $tryProxy = true) { + if ($tryProxy && ($className = $definition->getClass()) && $definition->isLazy()) { + $factory = new LazyLoadingValueHolderFactory(new Configuration()); + $container = $this; + + return $factory->createProxy( + $className, + function (& $wrappedInstance, LazyLoadingInterface $proxy) use ($container, $definition, $id) { + $proxy->setProxyInitializer(null); + + $wrappedInstance = $container->createService($definition, $id, false); + } + ); + } + if ($definition->isSynthetic()) { throw new RuntimeException(sprintf('You have requested a synthetic service ("%s"). The DIC does not know how to construct this service.', $id)); } diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php index 9d52426121874..803dc1640d785 100644 --- a/src/Symfony/Component/DependencyInjection/Definition.php +++ b/src/Symfony/Component/DependencyInjection/Definition.php @@ -37,6 +37,7 @@ class Definition private $synthetic; private $abstract; private $synchronized; + private $lazy; protected $arguments; @@ -58,6 +59,7 @@ public function __construct($class = null, array $arguments = array()) $this->public = true; $this->synthetic = false; $this->synchronized = false; + $this->lazy = false; $this->abstract = false; $this->properties = array(); } @@ -599,6 +601,34 @@ public function isSynchronized() return $this->synchronized; } + /** + * Sets the lazy flag of this service. + * + * @param Boolean $lazy + * + * @return Definition The current instance + * + * @api + */ + public function setLazy($lazy) + { + $this->lazy = (Boolean) $lazy; + + return $this; + } + + /** + * Whether this service is lazy. + * + * @return Boolean + * + * @api + */ + public function isLazy() + { + return $this->lazy; + } + /** * Sets whether this definition is synthetic, that is not constructed by the * container, but dynamically injected. diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php index da2e2c4cd80be..a5ceb2c68d3c3 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php @@ -133,6 +133,9 @@ private function addService($definition, $id, \DOMElement $parent) if ($definition->isSynchronized()) { $service->setAttribute('synchronized', 'true'); } + if ($definition->isLazy()) { + $service->setAttribute('lazy', 'true'); + } foreach ($definition->getTags() as $name => $tags) { foreach ($tags as $attributes) { diff --git a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php index bc10c4a7b4e17..0059f0d0d7388 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php @@ -106,6 +106,10 @@ private function addService($id, $definition) $code .= sprintf(" factory_class: %s\n", $definition->getFactoryClass()); } + if ($definition->isLazy()) { + $code .= sprintf(" lazy: true\n"); + } + if ($definition->getFactoryMethod()) { $code .= sprintf(" factory_method: %s\n", $definition->getFactoryMethod()); } diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 85898d3d3fe86..9f25ab7683cd2 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -148,7 +148,7 @@ private function parseDefinition($id, $service, $file) $definition = new Definition(); } - foreach (array('class', 'scope', 'public', 'factory-class', 'factory-method', 'factory-service', 'synthetic', 'synchronized', 'abstract') as $key) { + foreach (array('class', 'scope', 'public', 'factory-class', 'factory-method', 'factory-service', 'synthetic', 'synchronized', 'lazy', 'abstract') as $key) { if (isset($service[$key])) { $method = 'set'.str_replace('-', '', $key); $definition->$method((string) $service->getAttributeAsPhp($key)); diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index b95280dfcfe5f..cf68a33756747 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -157,6 +157,10 @@ private function parseDefinition($id, $service, $file) $definition->setSynchronized($service['synchronized']); } + if (isset($service['lazy'])) { + $definition->setLazy($service['lazy']); + } + if (isset($service['public'])) { $definition->setPublic($service['public']); } diff --git a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd index 4d9addcd971f8..f1c2003c62258 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd +++ b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd @@ -87,6 +87,7 @@ + diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 7d2cb278871cc..1e22d3e778843 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -263,6 +263,30 @@ public function testCreateService() $this->assertInstanceOf('\FooClass', $builder->get('foo2'), '->createService() replaces parameters in the file provided by the service definition'); } + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::createService + */ + public function testCreateServiceWithDelegateFactory() + { + $builder = new ContainerBuilder(); + + $builder->register('foo1', 'FooClass')->setFile(__DIR__.'/Fixtures/includes/foo.php'); + $builder->getDefinition('foo1')->setLazy(true); + + /* @var $foo1 \ProxyManager\Proxy\LazyLoadingInterface|\ProxyManager\Proxy\ValueHolderInterface */ + $foo1 = $builder->get('foo1'); + + $this->assertInstanceOf('\FooClass', $foo1); + $this->assertInstanceOf('\ProxyManager\Proxy\LazyLoadingInterface', $foo1); + $this->assertFalse($foo1->isProxyInitialized()); + + $foo1->initializeProxy(); + + $this->assertTrue($foo1->isProxyInitialized()); + $this->assertInstanceOf('\FooClass', $foo1->getWrappedValueHolderValue()); + $this->assertNotInstanceOf('\ProxyManager\Proxy\LazyLoadingInterface', $foo1->getWrappedValueHolderValue()); + } + /** * @covers Symfony\Component\DependencyInjection\ContainerBuilder::createService */ diff --git a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php index d9a4282efefbc..d41c6a8e2b2ae 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php @@ -164,6 +164,18 @@ public function testSetIsSynchronized() $this->assertTrue($def->isSynchronized(), '->isSynchronized() returns true if the service is synchronized.'); } + /** + * @covers Symfony\Component\DependencyInjection\Definition::setLazy + * @covers Symfony\Component\DependencyInjection\Definition::isLazy + */ + public function testSetIsLazy() + { + $def = new Definition('stdClass'); + $this->assertFalse($def->isLazy(), '->isLazy() returns false by default'); + $this->assertSame($def, $def->setLazy(true), '->setLazy() implements a fluent interface'); + $this->assertTrue($def->isLazy(), '->isLazy() returns true if the service is lazy.'); + } + /** * @covers Symfony\Component\DependencyInjection\Definition::setAbstract * @covers Symfony\Component\DependencyInjection\Definition::isAbstract diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml index 4d2aa3d79ae24..abd9fbc1529b1 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml @@ -46,6 +46,6 @@ - + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml index 820c364a06556..7ba9453bdd6dd 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml @@ -28,3 +28,4 @@ services: class: Request synthetic: true synchronized: true + lazy: true diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index b355f0ac215c3..d8138f947541a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -187,6 +187,7 @@ public function testLoadServices() $this->assertTrue($services['request']->isSynthetic(), '->load() parses the synthetic flag'); $this->assertTrue($services['request']->isSynchronized(), '->load() parses the synchronized flag'); + $this->assertTrue($services['request']->isLazy(), '->load() parses the lazy flag'); $aliases = $container->getAliases(); $this->assertTrue(isset($aliases['alias_for_foo']), '->load() parses elements'); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index 290d6628acf5b..e452e5d221d19 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -130,6 +130,7 @@ public function testLoadServices() $this->assertTrue($services['request']->isSynthetic(), '->load() parses the synthetic flag'); $this->assertTrue($services['request']->isSynchronized(), '->load() parses the synchronized flag'); + $this->assertTrue($services['request']->isLazy(), '->load() parses the lazy flag'); $aliases = $container->getAliases(); $this->assertTrue(isset($aliases['alias_for_foo']), '->load() parses aliases'); From 67aef456ba5c7d4a2e8ba6b1170d9f6e1cfde25e Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sat, 30 Mar 2013 01:23:57 +0100 Subject: [PATCH 02/21] Adding basic logic to generate proxy instantiation into a php dumped container --- .../DependencyInjection/ContainerBuilder.php | 2 + .../DependencyInjection/Dumper/PhpDumper.php | 62 ++++++++++++++++- .../Tests/Dumper/PhpDumperTest.php | 14 ++++ .../Tests/Fixtures/php/lazy_service.php | 66 +++++++++++++++++++ 4 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service.php diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 9ce9a18595635..4e3edcb352f69 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -888,6 +888,8 @@ function (& $wrappedInstance, LazyLoadingInterface $proxy) use ($container, $def $proxy->setProxyInitializer(null); $wrappedInstance = $container->createService($definition, $id, false); + + return true; } ); } diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 294de23a7773d..88a627fef4de0 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -149,6 +149,51 @@ private function addServiceLocalTempVariables($cId, $definition) return $code; } + /** + * Generates the logic required for proxy lazy loading + * + * @param string $id The service id + * @param Definition $definition + * + * @return string + */ + private function addProxyLoading($id, Definition $definition) + { + if (!($definition->isLazy() && $definition->getClass())) { + return ''; + } + + $class = $this->dumpValue($definition->getClass()); + + if (0 === strpos($class, "'") && !preg_match('/^\'[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) { + return ''; + } + + // @todo this should happen directly through the factory class, but we have to ensure that the proxy + // @todo class is generated during the dump process + $methodName = 'get' . Container::camelize($id) . 'Service'; + + return <<createProxy( + $class, + function (& \$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface \$proxy) use (\$container) { + \$proxy->setProxyInitializer(null); + + \$wrappedInstance = \$container->$methodName(false); + + return true; + } + ); + } + + +EOF; + } + /** * Generates the require_once statement for service includes. * @@ -483,18 +528,29 @@ private function addService($id, $definition) EOF; } - $code = <<isLazy()) { + $lazyInitialization = '$lazyLoad = true'; + $lazyInitializationDoc = "\n * @param boolean \$lazyLoad whether to try lazy-loading the" + . " service with a proxy\n *"; + } else { + $lazyInitialization = ''; + $lazyInitializationDoc = ''; + } + + $code = <<addProxyLoading($id, $definition); + if (!in_array($scope, array(ContainerInterface::SCOPE_CONTAINER, ContainerInterface::SCOPE_PROTOTYPE))) { $code .= <<scopedServices['$scope'])) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 72d587ff070fc..767635aebc03d 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -52,6 +52,20 @@ public function testDumpFrozenContainerWithNoParameter() $this->assertNotRegexp("/function getDefaultParameters\(/", $dumpedString, '->dump() does not add getDefaultParameters() method definition.'); } + public function testDumpContainerWithProxyService() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass'); + $container->getDefinition('foo')->setLazy(true); + + $container->compile(); + + $dumper = new PhpDumper($container); + + $dumpedString = $dumper->dump(); + $this->assertStringEqualsFile(self::$fixturesPath.'/php/lazy_service.php', $dumpedString, '->dump() does generate proxy lazy loading logic.'); + } + public function testDumpOptimizationString() { $definition = new Definition(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service.php new file mode 100644 index 0000000000000..9f4eafd0c0fa3 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service.php @@ -0,0 +1,66 @@ +services = + $this->scopedServices = + $this->scopeStacks = array(); + + $this->set('service_container', $this); + + $this->scopes = array(); + $this->scopeChildren = array(); + } + + /** + * Gets the 'foo' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @param boolean $lazyLoad whether to try lazy-loading the service with a proxy + * + * @return stdClass A stdClass instance. + */ + protected function getFooService($lazyLoad = true) + { + if ($lazyLoad) { + $factory = new \ProxyManager\Factory\LazyLoadingValueHolderFactory(new \ProxyManager\Configuration()); + $container = $this; + + return $factory->createProxy( + 'stdClass', + function (& $wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface $proxy) use ($container) { + $proxy->setProxyInitializer(null); + + $wrappedInstance = $container->getFooService(false); + + return true; + } + ); + } + + return $this->services['foo'] = new \stdClass(); + } +} From f4a19c7d0500805c527066da10b7d916242e346e Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sat, 30 Mar 2013 02:54:45 +0100 Subject: [PATCH 03/21] Compiling proxies into the generated DIC file --- .../DependencyInjection/Dumper/PhpDumper.php | 48 ++++- .../Tests/Dumper/PhpDumperTest.php | 2 +- .../Tests/Fixtures/php/lazy_service.php | 66 ------ .../Tests/Fixtures/php/lazy_service.txt | 190 ++++++++++++++++++ 4 files changed, 235 insertions(+), 71 deletions(-) delete mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service.php create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service.txt diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 88a627fef4de0..a418b0a92e5a8 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -11,6 +11,10 @@ namespace Symfony\Component\DependencyInjection\Dumper; +use CG\Core\DefaultGeneratorStrategy; +use CG\Generator\PhpClass; +use ProxyManager\ProxyGenerator\LazyLoadingValueHolderGenerator; +use ReflectionClass; use Symfony\Component\DependencyInjection\Variable; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -94,7 +98,8 @@ public function dump(array $options = array()) $code .= $this->addServices(). $this->addDefaultParametersMethod(). - $this->endClass() + $this->endClass(). + $this->addProxyClasses() ; return $code; @@ -172,14 +177,13 @@ private function addProxyLoading($id, Definition $definition) // @todo this should happen directly through the factory class, but we have to ensure that the proxy // @todo class is generated during the dump process $methodName = 'get' . Container::camelize($id) . 'Service'; + $proxyClass = str_replace('\\', '', $definition->getClass()) . '_' . md5(spl_object_hash($definition)); return <<createProxy( - $class, + return new $proxyClass( function (& \$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface \$proxy) use (\$container) { \$proxy->setProxyInitializer(null); @@ -194,6 +198,42 @@ function (& \$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface \$proxy) EOF; } + /** + * Generates code for the proxy classes to be attached after the container class + * + * @return string + */ + private function addProxyClasses() + { + $definitions = $this->container->getDefinitions(); + + ksort($definitions); + + $proxyDefinitions = array_filter( + $this->container->getDefinitions(), + function (Definition $definition) { + return $definition->isLazy() && $definition->getClass(); + } + ); + + $proxyGenerator = new LazyLoadingValueHolderGenerator(); + $classGenerator = new DefaultGeneratorStrategy(); + $code = ''; + + /* @var $proxyDefinitions Definition[] */ + foreach ($proxyDefinitions as $definition) { + $phpClass = new PhpClass( + str_replace('\\', '', $definition->getClass()) . '_' . md5(spl_object_hash($definition)) + ); + + $proxyGenerator->generate(new ReflectionClass($definition->getClass()), $phpClass); + + $code .= "\n" . $classGenerator->generate($phpClass); + } + + return $code; + } + /** * Generates the require_once statement for service includes. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 767635aebc03d..fe1580dfc464f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -63,7 +63,7 @@ public function testDumpContainerWithProxyService() $dumper = new PhpDumper($container); $dumpedString = $dumper->dump(); - $this->assertStringEqualsFile(self::$fixturesPath.'/php/lazy_service.php', $dumpedString, '->dump() does generate proxy lazy loading logic.'); + $this->assertStringMatchesFormatFile(self::$fixturesPath.'/php/lazy_service.txt', $dumpedString, '->dump() does generate proxy lazy loading logic.'); } public function testDumpOptimizationString() diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service.php deleted file mode 100644 index 9f4eafd0c0fa3..0000000000000 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service.php +++ /dev/null @@ -1,66 +0,0 @@ -services = - $this->scopedServices = - $this->scopeStacks = array(); - - $this->set('service_container', $this); - - $this->scopes = array(); - $this->scopeChildren = array(); - } - - /** - * Gets the 'foo' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @param boolean $lazyLoad whether to try lazy-loading the service with a proxy - * - * @return stdClass A stdClass instance. - */ - protected function getFooService($lazyLoad = true) - { - if ($lazyLoad) { - $factory = new \ProxyManager\Factory\LazyLoadingValueHolderFactory(new \ProxyManager\Configuration()); - $container = $this; - - return $factory->createProxy( - 'stdClass', - function (& $wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface $proxy) use ($container) { - $proxy->setProxyInitializer(null); - - $wrappedInstance = $container->getFooService(false); - - return true; - } - ); - } - - return $this->services['foo'] = new \stdClass(); - } -} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service.txt new file mode 100644 index 0000000000000..1a27b5ae0f3cd --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service.txt @@ -0,0 +1,190 @@ +services = + $this->scopedServices = + $this->scopeStacks = array(); + + $this->set('service_container', $this); + + $this->scopes = array(); + $this->scopeChildren = array(); + } + + /** + * Gets the 'foo' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @param boolean $lazyLoad whether to try lazy-loading the service with a proxy + * + * @return stdClass A stdClass instance. + */ + protected function getFooService($lazyLoad = true) + { + if ($lazyLoad) { + $container = $this; + + return new stdClass_%s( + function (& $wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface $proxy) use ($container) { + $proxy->setProxyInitializer(null); + + $wrappedInstance = $container->getFooService(false); + + return true; + } + ); + } + + return $this->services['foo'] = new \stdClass(); + } +} + +class %s extends \stdClass implements \ProxyManager\Proxy\LazyLoadingInterface, \ProxyManager\Proxy\ValueHolderInterface +{ + /** + * @var \Closure|null initializer responsible for generating the wrapped object + */ + private $%s; + /** + * @var \Closure|null initializer responsible for generating the wrapped object + */ + private $%s; + + /** + * {@inheritDoc} + */ + public function setProxyInitializer(\Closure $initializer = NULL) + { + $this->%s = $initializer; + } + + /** + * {@inheritDoc} + */ + public function isProxyInitialized() + { + return null !== $this->%s; + } + + /** + * {@inheritDoc} + */ + public function initializeProxy() + { + return $this->%s && $this->%s->__invoke($this->%s, $this, 'initializeProxy', array()); + } + + /** + * {@inheritDoc} + */ + public function getWrappedValueHolderValue() + { + return $this->%s; + } + + /** + * {@inheritDoc} + */ + public function getProxyInitializer() + { + return $this->%s; + } + + /** + */ + public function __wakeup() + { + } + + /** + * @param string $name + */ + public function __unset($name) + { + $this->%s && $this->%s->__invoke($this->%s, $this, '__unset', array('name' => $name)); + + unset($this->%s->$name); + } + + /** + */ + public function __sleep() + { + $this->%s && $this->%s->__invoke($this->%s, $this, '__sleep', array()); + + return array('%s'); + } + + /** + * @param string $name + * @param mixed $value + */ + public function __set($name, $value) + { + $this->%s && $this->%s->__invoke($this->%s, $this, '__set', array('name' => $name, 'value' => $value)); + + $this->%s->$name = $value; + } + + /** + * @param string $name + */ + public function __isset($name) + { + $this->%s && $this->%s->__invoke($this->%s, $this, '__isset', array('name' => $name)); + + return isset($this->%s->$name); + } + + /** + * @param string $name + */ + public function __get($name) + { + $this->%s && $this->%s->__invoke($this->%s, $this, '__get', array('name' => $name)); + + return $this->%s->$name; + } + + /** + * @override constructor for lazy initialization + * @param \Closure|null $initializer + */ + public function __construct($initializer) + { + $this->%s = $initializer; + } + + /** + */ + public function __clone() + { + $this->%s && $this->%s->__invoke($this->%s, $this, '__clone', array()); + + $this->%s = clone $this->%s; + } +} \ No newline at end of file From d2760f126a8d1f39b06e0b257fbd915e1064df7c Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sat, 30 Mar 2013 11:56:42 +0100 Subject: [PATCH 04/21] Upgrading dependency to ProxyManager 0.3.* --- composer.json | 4 +- .../DependencyInjection/Dumper/PhpDumper.php | 13 ++- .../Tests/Fixtures/php/lazy_service.txt | 106 ++++++++++-------- .../DependencyInjection/composer.json | 3 +- 4 files changed, 70 insertions(+), 56 deletions(-) diff --git a/composer.json b/composer.json index a102f4705c448..bf74f0864c187 100644 --- a/composer.json +++ b/composer.json @@ -61,13 +61,13 @@ "symfony/yaml": "self.version" }, "require-dev": { - "ocramius/proxy-manager": "0.2.*", "doctrine/data-fixtures": "1.0.*", "doctrine/dbal": "~2.2", "doctrine/orm": "~2.2,>=2.2.3", "monolog/monolog": "~1.3", "propel/propel1": "1.6.*", - "ircmaxell/password-compat": "1.0.*" + "ircmaxell/password-compat": "1.0.*", + "ocramius/proxy-manager": "0.3.*" }, "autoload": { "psr-0": { "Symfony\\": "src/" }, diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index a418b0a92e5a8..02866e3ab753e 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -11,8 +11,8 @@ namespace Symfony\Component\DependencyInjection\Dumper; -use CG\Core\DefaultGeneratorStrategy; -use CG\Generator\PhpClass; +use ProxyManager\Generator\ClassGenerator; +use ProxyManager\GeneratorStrategy\BaseGeneratorStrategy; use ProxyManager\ProxyGenerator\LazyLoadingValueHolderGenerator; use ReflectionClass; use Symfony\Component\DependencyInjection\Variable; @@ -216,13 +216,18 @@ function (Definition $definition) { } ); + if (empty($proxyDefinitions)) { + // avoids hard dependency to ProxyManager + return ''; + } + $proxyGenerator = new LazyLoadingValueHolderGenerator(); - $classGenerator = new DefaultGeneratorStrategy(); + $classGenerator = new BaseGeneratorStrategy(); $code = ''; /* @var $proxyDefinitions Definition[] */ foreach ($proxyDefinitions as $definition) { - $phpClass = new PhpClass( + $phpClass = new ClassGenerator( str_replace('\\', '', $definition->getClass()) . '_' . md5(spl_object_hash($definition)) ); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service.txt index 1a27b5ae0f3cd..43d5541451bc2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service.txt @@ -63,61 +63,58 @@ class ProjectServiceContainer extends Container } } -class %s extends \stdClass implements \ProxyManager\Proxy\LazyLoadingInterface, \ProxyManager\Proxy\ValueHolderInterface +class stdClass_%s extends \stdClass implements \ProxyManager\Proxy\LazyLoadingInterface, \ProxyManager\Proxy\ValueHolderInterface { + /** * @var \Closure|null initializer responsible for generating the wrapped object */ - private $%s; + private %s = null; + /** * @var \Closure|null initializer responsible for generating the wrapped object */ - private $%s; + private %s = null; /** - * {@inheritDoc} + * @override constructor for lazy initialization + * + * @param \Closure|null $initializer */ - public function setProxyInitializer(\Closure $initializer = NULL) + public function __construct($initializer) { $this->%s = $initializer; } /** - * {@inheritDoc} + * @param string $name */ - public function isProxyInitialized() + public function __get($name) { - return null !== $this->%s; - } + $this->%s && $this->%s->__invoke($this->%s, $this, '__get', array('name' => $name)); - /** - * {@inheritDoc} - */ - public function initializeProxy() - { - return $this->%s && $this->%s->__invoke($this->%s, $this, 'initializeProxy', array()); + return $this->%s->$name; } /** - * {@inheritDoc} + * @param string $name + * @param mixed $value */ - public function getWrappedValueHolderValue() + public function __set($name, $value) { - return $this->%s; - } + $this->%s && $this->%s->__invoke($this->%s, $this, '__set', array('name' => $name, 'value' => $value)); - /** - * {@inheritDoc} - */ - public function getProxyInitializer() - { - return $this->%s; + $this->%s->$name = $value; } /** + * @param string $name */ - public function __wakeup() + public function __isset($name) { + $this->%s && $this->%s->__invoke($this->%s, $this, '__isset', array('name' => $name)); + + return isset($this->%s->$name); } /** @@ -131,6 +128,17 @@ class %s extends \stdClass implements \ProxyManager\Proxy\LazyLoadingInterface, } /** + * + */ + public function __clone() + { + $this->%s && $this->%s->__invoke($this->%s, $this, '__clone', array()); + + $this->%s = clone $this->%s; + } + + /** + * */ public function __sleep() { @@ -140,51 +148,51 @@ class %s extends \stdClass implements \ProxyManager\Proxy\LazyLoadingInterface, } /** - * @param string $name - * @param mixed $value + * */ - public function __set($name, $value) + public function __wakeup() { - $this->%s && $this->%s->__invoke($this->%s, $this, '__set', array('name' => $name, 'value' => $value)); - - $this->%s->$name = $value; } /** - * @param string $name + * {@inheritDoc} */ - public function __isset($name) + public function setProxyInitializer(\Closure $initializer = null) { - $this->%s && $this->%s->__invoke($this->%s, $this, '__isset', array('name' => $name)); - - return isset($this->%s->$name); + $this->%s = $initializer; } /** - * @param string $name + * {@inheritDoc} */ - public function __get($name) + public function getProxyInitializer() { - $this->%s && $this->%s->__invoke($this->%s, $this, '__get', array('name' => $name)); - - return $this->%s->$name; + return $this->%s; } /** - * @override constructor for lazy initialization - * @param \Closure|null $initializer + * {@inheritDoc} */ - public function __construct($initializer) + public function initializeProxy() { - $this->%s = $initializer; + return $this->%s && $this->%s->__invoke($this->%s, $this, 'initializeProxy', array()); } /** + * {@inheritDoc} */ - public function __clone() + public function isProxyInitialized() { - $this->%s && $this->%s->__invoke($this->%s, $this, '__clone', array()); + return null !== $this->%s; + } - $this->%s = clone $this->%s; + /** + * {@inheritDoc} + */ + public function getWrappedValueHolderValue() + { + return $this->%s; } + + } \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/composer.json b/src/Symfony/Component/DependencyInjection/composer.json index 887686747eda3..d817b9b9b04fd 100644 --- a/src/Symfony/Component/DependencyInjection/composer.json +++ b/src/Symfony/Component/DependencyInjection/composer.json @@ -20,7 +20,8 @@ }, "require-dev": { "symfony/yaml": "~2.0", - "symfony/config": ">=2.2,<2.4-dev" + "symfony/config": ">=2.2,<2.4-dev", + "ocramius/proxy-manager": "0.3.*" }, "suggest": { "symfony/yaml": "2.2.*", From 4a13f822cd93aa0a0cff75a9504bd658099b4fa8 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 31 Mar 2013 07:26:17 +0200 Subject: [PATCH 05/21] Suggesting ProxyManager in composer.json, removing useless calls --- .../DependencyInjection/ContainerBuilder.php | 25 ++++++++++++++----- .../DependencyInjection/Dumper/PhpDumper.php | 11 +++----- .../DependencyInjection/composer.json | 5 ++-- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 4e3edcb352f69..9370f690b2b09 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -13,6 +13,7 @@ use ProxyManager\Configuration; use ProxyManager\Factory\LazyLoadingValueHolderFactory; +use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy; use ProxyManager\Proxy\LazyLoadingInterface; use Symfony\Component\DependencyInjection\Compiler\Compiler; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; @@ -868,6 +869,7 @@ public function findDefinition($id) * * @param Definition $definition A service definition instance * @param string $id The service identifier + * @param Boolean $tryProxy Whether to try proxying the service with a lazy proxy * * @return object The service described by the service definition * @@ -875,11 +877,26 @@ public function findDefinition($id) * @throws RuntimeException When the factory definition is incomplete * @throws RuntimeException When the service is a synthetic service * @throws InvalidArgumentException When configure callable is not callable + * + * @internal this method is public because of PHP 5.3 limitations, do not use it explicitly in your code */ public function createService(Definition $definition, $id, $tryProxy = true) { - if ($tryProxy && ($className = $definition->getClass()) && $definition->isLazy()) { - $factory = new LazyLoadingValueHolderFactory(new Configuration()); + if ($definition->isSynthetic()) { + throw new RuntimeException(sprintf('You have requested a synthetic service ("%s"). The DIC does not know how to construct this service.', $id)); + } + + if ( + $tryProxy + && ($className = $definition->getClass()) + && $definition->isLazy() + && class_exists('ProxyManager\\Factory\\LazyLoadingValueHolderFactory') + ) { + $config = new Configuration(); + + $config->setGeneratorStrategy(new EvaluatingGeneratorStrategy()); + + $factory = new LazyLoadingValueHolderFactory($config); $container = $this; return $factory->createProxy( @@ -894,10 +911,6 @@ function (& $wrappedInstance, LazyLoadingInterface $proxy) use ($container, $def ); } - if ($definition->isSynthetic()) { - throw new RuntimeException(sprintf('You have requested a synthetic service ("%s"). The DIC does not know how to construct this service.', $id)); - } - $parameterBag = $this->getParameterBag(); if (null !== $definition->getFile()) { diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 02866e3ab753e..c72f791f38893 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -171,11 +171,10 @@ private function addProxyLoading($id, Definition $definition) $class = $this->dumpValue($definition->getClass()); if (0 === strpos($class, "'") && !preg_match('/^\'[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) { + // provided class name is not valid return ''; } - // @todo this should happen directly through the factory class, but we have to ensure that the proxy - // @todo class is generated during the dump process $methodName = 'get' . Container::camelize($id) . 'Service'; $proxyClass = str_replace('\\', '', $definition->getClass()) . '_' . md5(spl_object_hash($definition)); @@ -205,10 +204,6 @@ function (& \$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface \$proxy) */ private function addProxyClasses() { - $definitions = $this->container->getDefinitions(); - - ksort($definitions); - $proxyDefinitions = array_filter( $this->container->getDefinitions(), function (Definition $definition) { @@ -216,8 +211,8 @@ function (Definition $definition) { } ); - if (empty($proxyDefinitions)) { - // avoids hard dependency to ProxyManager + // avoids hard dependency to ProxyManager + if (empty($proxyDefinitions) || !class_exists('ProxyManager\\GeneratorStrategy\\BaseGeneratorStrategy')) { return ''; } diff --git a/src/Symfony/Component/DependencyInjection/composer.json b/src/Symfony/Component/DependencyInjection/composer.json index d817b9b9b04fd..469811a075d48 100644 --- a/src/Symfony/Component/DependencyInjection/composer.json +++ b/src/Symfony/Component/DependencyInjection/composer.json @@ -21,11 +21,12 @@ "require-dev": { "symfony/yaml": "~2.0", "symfony/config": ">=2.2,<2.4-dev", - "ocramius/proxy-manager": "0.3.*" + "ocramius/proxy-manager": ">=0.3.1,<0.4.x-dev" }, "suggest": { "symfony/yaml": "2.2.*", - "symfony/config": "2.2.*" + "symfony/config": "2.2.*", + "ocramius/proxy-manager": "Generate service proxies to lazy load them" }, "autoload": { "psr-0": { "Symfony\\Component\\DependencyInjection\\": "" } From a6a65724d2b0f8a3598990d0f8ea9d4dcfb18c1b Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 31 Mar 2013 08:17:48 +0200 Subject: [PATCH 06/21] Adding failing test to demonstrate that proxy initialization breaks shared services --- .../DependencyInjection/Tests/ContainerBuilderTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 1e22d3e778843..0fd3b0f4aebc2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -276,12 +276,14 @@ public function testCreateServiceWithDelegateFactory() /* @var $foo1 \ProxyManager\Proxy\LazyLoadingInterface|\ProxyManager\Proxy\ValueHolderInterface */ $foo1 = $builder->get('foo1'); + $this->assertSame($foo1, $builder->get('foo1'), 'The same proxy is retrieved on multiple subsequent calls'); $this->assertInstanceOf('\FooClass', $foo1); $this->assertInstanceOf('\ProxyManager\Proxy\LazyLoadingInterface', $foo1); $this->assertFalse($foo1->isProxyInitialized()); $foo1->initializeProxy(); + $this->assertSame($foo1, $builder->get('foo1'), 'The same proxy is retrieved after initialization'); $this->assertTrue($foo1->isProxyInitialized()); $this->assertInstanceOf('\FooClass', $foo1->getWrappedValueHolderValue()); $this->assertNotInstanceOf('\ProxyManager\Proxy\LazyLoadingInterface', $foo1->getWrappedValueHolderValue()); From 35fddedc501948880bc651863d10ec194245ceb1 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 31 Mar 2013 08:18:19 +0200 Subject: [PATCH 07/21] Fixing shared service instance --- .../DependencyInjection/ContainerBuilder.php | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 9370f690b2b09..1dfe5c3bf3ad8 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -898,8 +898,7 @@ public function createService(Definition $definition, $id, $tryProxy = true) $factory = new LazyLoadingValueHolderFactory($config); $container = $this; - - return $factory->createProxy( + $proxy = $factory->createProxy( $className, function (& $wrappedInstance, LazyLoadingInterface $proxy) use ($container, $definition, $id) { $proxy->setProxyInitializer(null); @@ -909,6 +908,10 @@ function (& $wrappedInstance, LazyLoadingInterface $proxy) use ($container, $def return true; } ); + + $this->shareService($definition, $proxy, $id); + + return $proxy; } $parameterBag = $this->getParameterBag(); @@ -935,16 +938,8 @@ function (& $wrappedInstance, LazyLoadingInterface $proxy) use ($container, $def $service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments); } - if (self::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) { - if (self::SCOPE_CONTAINER !== $scope && !isset($this->scopedServices[$scope])) { - throw new InactiveScopeException($id, $scope); - } - - $this->services[$lowerId = strtolower($id)] = $service; - - if (self::SCOPE_CONTAINER !== $scope) { - $this->scopedServices[$scope][$lowerId] = $service; - } + if (!$definition->isLazy()) { + $this->shareService($definition, $service, $id); } foreach ($definition->getMethodCalls() as $call) { @@ -1089,4 +1084,19 @@ private function callMethod($service, $call) call_user_func_array(array($service, $call[0]), $this->resolveServices($this->getParameterBag()->resolveValue($call[1]))); } + + private function shareService(Definition $definition, $service, $id) + { + if (self::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) { + if (self::SCOPE_CONTAINER !== $scope && !isset($this->scopedServices[$scope])) { + throw new InactiveScopeException($id, $scope); + } + + $this->services[$lowerId = strtolower($id)] = $service; + + if (self::SCOPE_CONTAINER !== $scope) { + $this->scopedServices[$scope][$lowerId] = $service; + } + } + } } From bec77744041aeef807538df113f126e8e11cac2b Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 31 Mar 2013 08:27:10 +0200 Subject: [PATCH 08/21] Sharing services in the container should only happen when proxying failed --- src/Symfony/Component/DependencyInjection/ContainerBuilder.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 1dfe5c3bf3ad8..945b40ffa3f1a 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -938,7 +938,8 @@ function (& $wrappedInstance, LazyLoadingInterface $proxy) use ($container, $def $service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments); } - if (!$definition->isLazy()) { + if ($tryProxy || !$definition->isLazy()) { + // share only if proxying failed, or if not a proxy $this->shareService($definition, $service, $id); } From 468e92ec04caaa3006bb85d1730d596ce5c9e6fa Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 31 Mar 2013 09:00:42 +0200 Subject: [PATCH 09/21] Adding tests for proxy sharing within dumped containers --- .../Tests/Dumper/PhpDumperTest.php | 31 ++- .../Tests/Fixtures/php/lazy_service.php | 199 ++++++++++++++++++ ...service.txt => lazy_service_structure.txt} | 4 +- 3 files changed, 228 insertions(+), 6 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service.php rename src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/{lazy_service.txt => lazy_service_structure.txt} (97%) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index fe1580dfc464f..12f88ca6b19a6 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -55,15 +55,38 @@ public function testDumpFrozenContainerWithNoParameter() public function testDumpContainerWithProxyService() { $container = new ContainerBuilder(); + $container->register('foo', 'stdClass'); $container->getDefinition('foo')->setLazy(true); - $container->compile(); - $dumper = new PhpDumper($container); - + $dumper = new PhpDumper($container); $dumpedString = $dumper->dump(); - $this->assertStringMatchesFormatFile(self::$fixturesPath.'/php/lazy_service.txt', $dumpedString, '->dump() does generate proxy lazy loading logic.'); + + $this->assertStringMatchesFormatFile(self::$fixturesPath.'/php/lazy_service_structure.txt', $dumpedString, '->dump() does generate proxy lazy loading logic.'); + } + + /** + * Verifies that the generated container retrieves the same proxy instance on multiple subsequent requests + */ + public function testDumpContainerWithProxyServiceWillShareProxies() + { + require_once self::$fixturesPath.'/php/lazy_service.php'; + + $container = new \LazyServiceProjectServiceContainer(); + + /* @var $proxy \stdClass_c1d194250ee2e2b7d2eab8b8212368a8 */ + $proxy = $container->get('foo'); + + $this->assertInstanceOf('stdClass_c1d194250ee2e2b7d2eab8b8212368a8', $proxy); + $this->assertSame($proxy, $container->get('foo')); + + $this->assertFalse($proxy->isProxyInitialized()); + + $proxy->initializeProxy(); + + $this->assertTrue($proxy->isProxyInitialized()); + $this->assertSame($proxy, $container->get('foo')); } public function testDumpOptimizationString() diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service.php new file mode 100644 index 0000000000000..cc69dd8247aa3 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service.php @@ -0,0 +1,199 @@ + +services = + $this->scopedServices = + $this->scopeStacks = array(); + + $this->set('service_container', $this); + + $this->scopes = array(); + $this->scopeChildren = array(); + } + + /** + * Gets the 'foo' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @param boolean $lazyLoad whether to try lazy-loading the service with a proxy + * + * @return stdClass A stdClass instance. + */ + protected function getFooService($lazyLoad = true) + { + if ($lazyLoad) { + $container = $this; + + return $this->services['foo'] = new stdClass_c1d194250ee2e2b7d2eab8b8212368a8( + function (& $wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface $proxy) use ($container) { + $proxy->setProxyInitializer(null); + + $wrappedInstance = $container->getFooService(false); + + return true; + } + ); + } + + return new \stdClass(); + } +} + +class stdClass_c1d194250ee2e2b7d2eab8b8212368a8 extends \stdClass implements \ProxyManager\Proxy\LazyLoadingInterface, \ProxyManager\Proxy\ValueHolderInterface +{ + + /** + * @var \Closure|null initializer responsible for generating the wrapped object + */ + private $valueHolder5157dd96e88c0 = null; + + /** + * @var \Closure|null initializer responsible for generating the wrapped object + */ + private $initializer5157dd96e8924 = null; + + /** + * @override constructor for lazy initialization + * + * @param \Closure|null $initializer + */ + public function __construct($initializer) + { + $this->initializer5157dd96e8924 = $initializer; + } + + /** + * @param string $name + */ + public function __get($name) + { + $this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, '__get', array('name' => $name)); + + return $this->valueHolder5157dd96e88c0->$name; + } + + /** + * @param string $name + * @param mixed $value + */ + public function __set($name, $value) + { + $this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, '__set', array('name' => $name, 'value' => $value)); + + $this->valueHolder5157dd96e88c0->$name = $value; + } + + /** + * @param string $name + */ + public function __isset($name) + { + $this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, '__isset', array('name' => $name)); + + return isset($this->valueHolder5157dd96e88c0->$name); + } + + /** + * @param string $name + */ + public function __unset($name) + { + $this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, '__unset', array('name' => $name)); + + unset($this->valueHolder5157dd96e88c0->$name); + } + + /** + * + */ + public function __clone() + { + $this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, '__clone', array()); + + $this->valueHolder5157dd96e88c0 = clone $this->valueHolder5157dd96e88c0; + } + + /** + * + */ + public function __sleep() + { + $this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, '__sleep', array()); + + return array('valueHolder5157dd96e88c0'); + } + + /** + * + */ + public function __wakeup() + { + } + + /** + * {@inheritDoc} + */ + public function setProxyInitializer(\Closure $initializer = null) + { + $this->initializer5157dd96e8924 = $initializer; + } + + /** + * {@inheritDoc} + */ + public function getProxyInitializer() + { + return $this->initializer5157dd96e8924; + } + + /** + * {@inheritDoc} + */ + public function initializeProxy() + { + return $this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, 'initializeProxy', array()); + } + + /** + * {@inheritDoc} + */ + public function isProxyInitialized() + { + return null !== $this->valueHolder5157dd96e88c0; + } + + /** + * {@inheritDoc} + */ + public function getWrappedValueHolderValue() + { + return $this->valueHolder5157dd96e88c0; + } + + +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service_structure.txt similarity index 97% rename from src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service.txt rename to src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service_structure.txt index 43d5541451bc2..9247ec9308a84 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service_structure.txt @@ -48,7 +48,7 @@ class ProjectServiceContainer extends Container if ($lazyLoad) { $container = $this; - return new stdClass_%s( + return $this->services['foo'] = new stdClass_%s( function (& $wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface $proxy) use ($container) { $proxy->setProxyInitializer(null); @@ -59,7 +59,7 @@ class ProjectServiceContainer extends Container ); } - return $this->services['foo'] = new \stdClass(); + return new \stdClass(); } } From 4ecd5adfdc2557c5f26e1f17cc116b722642b9d0 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 31 Mar 2013 09:01:01 +0200 Subject: [PATCH 10/21] Fixing shared proxies into the container --- .../DependencyInjection/Dumper/PhpDumper.php | 42 ++++++++++++++----- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index c72f791f38893..dd001c05454ef 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -164,15 +164,16 @@ private function addServiceLocalTempVariables($cId, $definition) */ private function addProxyLoading($id, Definition $definition) { - if (!($definition->isLazy() && $definition->getClass())) { + if (!$this->isProxyCandidate($definition)) { return ''; } - $class = $this->dumpValue($definition->getClass()); + $instantiation = 'return'; - if (0 === strpos($class, "'") && !preg_match('/^\'[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) { - // provided class name is not valid - return ''; + if (ContainerInterface::SCOPE_CONTAINER === $definition->getScope()) { + $instantiation .= " \$this->services['$id'] ="; + } elseif (ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) { + $instantiation .= " \$this->services['$id'] = \$this->scopedServices['$scope']['$id'] ="; } $methodName = 'get' . Container::camelize($id) . 'Service'; @@ -182,7 +183,7 @@ private function addProxyLoading($id, Definition $definition) if (\$lazyLoad) { \$container = \$this; - return new $proxyClass( + $instantiation new $proxyClass( function (& \$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface \$proxy) use (\$container) { \$proxy->setProxyInitializer(null); @@ -222,13 +223,13 @@ function (Definition $definition) { /* @var $proxyDefinitions Definition[] */ foreach ($proxyDefinitions as $definition) { - $phpClass = new ClassGenerator( + $generatedClass = new ClassGenerator( str_replace('\\', '', $definition->getClass()) . '_' . md5(spl_object_hash($definition)) ); - $proxyGenerator->generate(new ReflectionClass($definition->getClass()), $phpClass); + $proxyGenerator->generate(new ReflectionClass($definition->getClass()), $generatedClass); - $code .= "\n" . $classGenerator->generate($phpClass); + $code .= "\n" . $classGenerator->generate($generatedClass); } return $code; @@ -368,9 +369,10 @@ private function addServiceInstance($id, $definition) $simple = $this->isSimpleInstance($id, $definition); $instantiation = ''; - if (ContainerInterface::SCOPE_CONTAINER === $definition->getScope()) { + + if (!$this->isProxyCandidate($definition) && ContainerInterface::SCOPE_CONTAINER === $definition->getScope()) { $instantiation = "\$this->services['$id'] = ".($simple ? '' : '$instance'); - } elseif (ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) { + } elseif (!$this->isProxyCandidate($definition) && ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) { $instantiation = "\$this->services['$id'] = \$this->scopedServices['$scope']['$id'] = ".($simple ? '' : '$instance'); } elseif (!$simple) { $instantiation = '$instance'; @@ -1274,6 +1276,24 @@ private function getServiceCall($id, Reference $reference = null) } } + /** + * Tells if the given definitions are to be used for proxying + * + * @param Definition $definition + * + * @return bool + */ + private function isProxyCandidate(Definition $definition) + { + if (!($definition->isLazy() && $definition->getClass())) { + return false; + } + + $class = $this->dumpValue($definition->getClass()); + + return (boolean) preg_match('/^\'[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class); + } + /** * Returns the next name to use * From 11a1da905ffbc405752bf76ff405ab688cb30595 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 31 Mar 2013 09:06:27 +0200 Subject: [PATCH 11/21] Bumping required version of ProxyManager --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index bf74f0864c187..1e3fdae52142f 100644 --- a/composer.json +++ b/composer.json @@ -67,7 +67,7 @@ "monolog/monolog": "~1.3", "propel/propel1": "1.6.*", "ircmaxell/password-compat": "1.0.*", - "ocramius/proxy-manager": "0.3.*" + "ocramius/proxy-manager": ">=0.3.1,<0.4-dev" }, "autoload": { "psr-0": { "Symfony\\": "src/" }, From 5870fedf77cfe086e40f99b49c48ca129f1f6292 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 31 Mar 2013 09:07:07 +0200 Subject: [PATCH 12/21] Docblock for ContainerBuilder#shareService --- .../Component/DependencyInjection/ContainerBuilder.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 945b40ffa3f1a..50f5e4f13db12 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -1086,6 +1086,15 @@ private function callMethod($service, $call) call_user_func_array(array($service, $call[0]), $this->resolveServices($this->getParameterBag()->resolveValue($call[1]))); } + /** + * Shares a given service in the container + * + * @param Definition $definition + * @param mixed $service + * @param string $id + * + * @throws Exception\RuntimeException + */ private function shareService(Definition $definition, $service, $id) { if (self::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) { From 695e3c5becf3657e60514235fd334b3860fd6731 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 31 Mar 2013 10:52:31 +0200 Subject: [PATCH 13/21] Adding `ContainerBuilder#addClassResource` --- .../DependencyInjection/ContainerBuilder.php | 26 ++++++-- .../Tests/ContainerBuilderTest.php | 60 +++++++++++++++++++ 2 files changed, 82 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 50f5e4f13db12..d70498137dfa0 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -15,6 +15,7 @@ use ProxyManager\Factory\LazyLoadingValueHolderFactory; use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy; use ProxyManager\Proxy\LazyLoadingInterface; +use ReflectionClass; use Symfony\Component\DependencyInjection\Compiler\Compiler; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Compiler\PassConfig; @@ -226,15 +227,32 @@ public function setResources(array $resources) * @api */ public function addObjectResource($object) + { + if ($this->trackResources) { + $this->addClassResource(new ReflectionClass($object)); + } + + return $this; + } + + /** + * Adds the given class hierarchy as resources. + * + * @param \ReflectionClass $class + * + * @return ContainerBuilder The current instance + * + * @api + */ + public function addClassResource(ReflectionClass $class) { if (!$this->trackResources) { return $this; } - $parent = new \ReflectionObject($object); do { - $this->addResource(new FileResource($parent->getFileName())); - } while ($parent = $parent->getParentClass()); + $this->addResource(new FileResource($class->getFileName())); + } while ($class = $class->getParentClass()); return $this; } @@ -933,7 +951,7 @@ function (& $wrappedInstance, LazyLoadingInterface $proxy) use ($container, $def $service = call_user_func_array(array($factory, $definition->getFactoryMethod()), $arguments); } else { - $r = new \ReflectionClass($parameterBag->resolveValue($definition->getClass())); + $r = new ReflectionClass($parameterBag->resolveValue($definition->getClass())); $service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 0fd3b0f4aebc2..6fe92afce3db9 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -491,6 +491,66 @@ public function testFindDefinition() $this->assertEquals($definition, $container->findDefinition('foobar'), '->findDefinition() returns a Definition'); } + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::addObjectResource + */ + public function testAddObjectResource() + { + if (!class_exists('Symfony\Component\Config\Resource\FileResource')) { + $this->markTestSkipped('The "Config" component is not available'); + } + + $container = new ContainerBuilder(); + + $container->setResourceTracking(false); + $container->addObjectResource(new \BarClass()); + + $this->assertEmpty($container->getResources(), 'No resources get registered without resource tracking'); + + $container->setResourceTracking(true); + $container->addObjectResource(new \BarClass()); + + $resources = $container->getResources(); + + $this->assertCount(1, $resources, '1 resource was registered'); + + /* @var $resource \Symfony\Component\Config\Resource\FileResource */ + $resource = end($resources); + + $this->assertInstanceOf('Symfony\Component\Config\Resource\FileResource', $resource); + $this->assertSame(realpath(__DIR__.'/Fixtures/includes/classes.php'), realpath($resource->getResource())); + } + + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::addClassResource + */ + public function testAddClassResource() + { + if (!class_exists('Symfony\Component\Config\Resource\FileResource')) { + $this->markTestSkipped('The "Config" component is not available'); + } + + $container = new ContainerBuilder(); + + $container->setResourceTracking(false); + $container->addClassResource(new \ReflectionClass('BarClass')); + + $this->assertEmpty($container->getResources(), 'No resources get registered without resource tracking'); + + $container->setResourceTracking(true); + $container->addClassResource(new \ReflectionClass('BarClass')); + + $resources = $container->getResources(); + + $this->assertCount(1, $resources, '1 resource was registered'); + + /* @var $resource \Symfony\Component\Config\Resource\FileResource */ + $resource = end($resources); + + $this->assertInstanceOf('Symfony\Component\Config\Resource\FileResource', $resource); + $this->assertSame(realpath(__DIR__.'/Fixtures/includes/classes.php'), realpath($resource->getResource())); + } + /** * @covers Symfony\Component\DependencyInjection\ContainerBuilder::getResources * @covers Symfony\Component\DependencyInjection\ContainerBuilder::addResource From c5a5af099fadae7c59426b74c9a405637cf7da75 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 1 Apr 2013 13:03:54 +0200 Subject: [PATCH 14/21] Adding test to check that class resources are registered for lazy services --- .../Tests/ContainerBuilderTest.php | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 6fe92afce3db9..b1b0a040f5776 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -14,6 +14,7 @@ require_once __DIR__.'/Fixtures/includes/classes.php'; require_once __DIR__.'/Fixtures/includes/ProjectExtension.php'; +use Symfony\Component\Config\Resource\ResourceInterface; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -551,6 +552,35 @@ public function testAddClassResource() $this->assertSame(realpath(__DIR__.'/Fixtures/includes/classes.php'), realpath($resource->getResource())); } + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::compile + */ + public function testCompilesClassDefinitionsOfLazyServices() + { + if (!class_exists('Symfony\Component\Config\Resource\FileResource')) { + $this->markTestSkipped('The "Config" component is not available'); + } + + $container = new ContainerBuilder(); + + $this->assertEmpty($container->getResources(), 'No resources get registered without resource tracking'); + + $container->register('foo', 'BarClass'); + $container->getDefinition('foo')->setLazy(true); + + $container->compile(); + + $classesPath = realpath(__DIR__.'/Fixtures/includes/classes.php'); + $matchingResources = array_filter( + $container->getResources(), + function (ResourceInterface $resource) use ($classesPath) { + return $resource instanceof FileResource && $classesPath === realpath($resource->getResource()); + } + ); + + $this->assertNotEmpty($matchingResources); + } + /** * @covers Symfony\Component\DependencyInjection\ContainerBuilder::getResources * @covers Symfony\Component\DependencyInjection\ContainerBuilder::addResource From 29899ecb3c0971dfac65d1fdaa722e7a65d4ac76 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 1 Apr 2013 13:04:25 +0200 Subject: [PATCH 15/21] Fixing tests, registering class resources for lazy services --- .../Component/DependencyInjection/ContainerBuilder.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index d70498137dfa0..1bd112170ca25 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -606,6 +606,12 @@ public function compile() foreach ($this->compiler->getPassConfig()->getPasses() as $pass) { $this->addObjectResource($pass); } + + foreach ($this->definitions as $definition) { + if ($definition->isLazy() && ($class = $definition->getClass()) && class_exists($class)) { + $this->addClassResource(new ReflectionClass($class)); + } + } } $this->compiler->compile($this); From b5d029866979a9da479394a8a7466ddf3183ba69 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 2 Apr 2013 20:43:26 +0200 Subject: [PATCH 16/21] Reverting import of global namespace classes --- .../Component/DependencyInjection/ContainerBuilder.php | 9 ++++----- .../Component/DependencyInjection/Dumper/PhpDumper.php | 3 +-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 1bd112170ca25..1ccbf34e55d66 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -15,7 +15,6 @@ use ProxyManager\Factory\LazyLoadingValueHolderFactory; use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy; use ProxyManager\Proxy\LazyLoadingInterface; -use ReflectionClass; use Symfony\Component\DependencyInjection\Compiler\Compiler; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Compiler\PassConfig; @@ -229,7 +228,7 @@ public function setResources(array $resources) public function addObjectResource($object) { if ($this->trackResources) { - $this->addClassResource(new ReflectionClass($object)); + $this->addClassResource(new \ReflectionClass($object)); } return $this; @@ -244,7 +243,7 @@ public function addObjectResource($object) * * @api */ - public function addClassResource(ReflectionClass $class) + public function addClassResource(\ReflectionClass $class) { if (!$this->trackResources) { return $this; @@ -609,7 +608,7 @@ public function compile() foreach ($this->definitions as $definition) { if ($definition->isLazy() && ($class = $definition->getClass()) && class_exists($class)) { - $this->addClassResource(new ReflectionClass($class)); + $this->addClassResource(new \ReflectionClass($class)); } } } @@ -957,7 +956,7 @@ function (& $wrappedInstance, LazyLoadingInterface $proxy) use ($container, $def $service = call_user_func_array(array($factory, $definition->getFactoryMethod()), $arguments); } else { - $r = new ReflectionClass($parameterBag->resolveValue($definition->getClass())); + $r = new \ReflectionClass($parameterBag->resolveValue($definition->getClass())); $service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments); } diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index dd001c05454ef..18523dc0f3560 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -14,7 +14,6 @@ use ProxyManager\Generator\ClassGenerator; use ProxyManager\GeneratorStrategy\BaseGeneratorStrategy; use ProxyManager\ProxyGenerator\LazyLoadingValueHolderGenerator; -use ReflectionClass; use Symfony\Component\DependencyInjection\Variable; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -227,7 +226,7 @@ function (Definition $definition) { str_replace('\\', '', $definition->getClass()) . '_' . md5(spl_object_hash($definition)) ); - $proxyGenerator->generate(new ReflectionClass($definition->getClass()), $generatedClass); + $proxyGenerator->generate(new \ReflectionClass($definition->getClass()), $generatedClass); $code .= "\n" . $classGenerator->generate($generatedClass); } From cda390b92a4a1740ce4124b5081a6f8d4d95a274 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Thu, 25 Apr 2013 21:50:41 +0200 Subject: [PATCH 17/21] Lazier checks on the proxy structure (avoiding whitespace-based test failures --- .../Fixtures/php/lazy_service_structure.txt | 133 ++++-------------- 1 file changed, 30 insertions(+), 103 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service_structure.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service_structure.txt index 9247ec9308a84..a0bd62e8dbb2a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service_structure.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service_structure.txt @@ -64,135 +64,62 @@ class ProjectServiceContainer extends Container } class stdClass_%s extends \stdClass implements \ProxyManager\Proxy\LazyLoadingInterface, \ProxyManager\Proxy\ValueHolderInterface -{ - - /** - * @var \Closure|null initializer responsible for generating the wrapped object - */ +{%a + /**%a*/ private %s = null; - /** - * @var \Closure|null initializer responsible for generating the wrapped object - */ + /**%a*/ private %s = null; - /** - * @override constructor for lazy initialization - * - * @param \Closure|null $initializer - */ + /**%a*/ public function __construct($initializer) - { - $this->%s = $initializer; - } + {%a} - /** - * @param string $name - */ + /**%a*/ public function __get($name) - { - $this->%s && $this->%s->__invoke($this->%s, $this, '__get', array('name' => $name)); + {%a} - return $this->%s->$name; - } - - /** - * @param string $name - * @param mixed $value - */ + /**%a*/ public function __set($name, $value) - { - $this->%s && $this->%s->__invoke($this->%s, $this, '__set', array('name' => $name, 'value' => $value)); - - $this->%s->$name = $value; - } + {%a} - /** - * @param string $name - */ + /**%a*/ public function __isset($name) - { - $this->%s && $this->%s->__invoke($this->%s, $this, '__isset', array('name' => $name)); - - return isset($this->%s->$name); - } + {%a} - /** - * @param string $name - */ + /**%a*/ public function __unset($name) - { - $this->%s && $this->%s->__invoke($this->%s, $this, '__unset', array('name' => $name)); - - unset($this->%s->$name); - } + {%a} - /** - * - */ + /**%a*/ public function __clone() - { - $this->%s && $this->%s->__invoke($this->%s, $this, '__clone', array()); + {%a} - $this->%s = clone $this->%s; - } - - /** - * - */ + /**%a*/ public function __sleep() - { - $this->%s && $this->%s->__invoke($this->%s, $this, '__sleep', array()); - - return array('%s'); - } + {%a} - /** - * - */ + /**%a*/ public function __wakeup() - { - } + {%a} - /** - * {@inheritDoc} - */ + /**%a*/ public function setProxyInitializer(\Closure $initializer = null) - { - $this->%s = $initializer; - } + {%a} - /** - * {@inheritDoc} - */ + /**%a*/ public function getProxyInitializer() - { - return $this->%s; - } + {%a} - /** - * {@inheritDoc} - */ + /**%a*/ public function initializeProxy() - { - return $this->%s && $this->%s->__invoke($this->%s, $this, 'initializeProxy', array()); - } + {%a} - /** - * {@inheritDoc} - */ + /**%a*/ public function isProxyInitialized() - { - return null !== $this->%s; - } + {%a} - /** - * {@inheritDoc} - */ + /**%a*/ public function getWrappedValueHolderValue() - { - return $this->%s; - } - - -} \ No newline at end of file + {%a} +%a} \ No newline at end of file From 1eb4cf700e003329aaf8b747a30439b2bbd7e8fb Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Thu, 25 Apr 2013 22:06:27 +0200 Subject: [PATCH 18/21] Getters for proxied services are public for 5.3.3 compatibility --- .../DependencyInjection/ContainerBuilder.php | 11 ++++++----- .../DependencyInjection/Dumper/PhpDumper.php | 9 ++++++--- .../Tests/Fixtures/php/lazy_service.php | 2 +- .../Tests/Fixtures/php/lazy_service_structure.txt | 2 +- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 1ccbf34e55d66..030551a955782 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -434,13 +434,14 @@ public function has($id) * Gets a service. * * @param string $id The service identifier - * @param integer $invalidBehavior The behavior when the service does not exist + * @param int $invalidBehavior The behavior when the service does not exist * + * @throws InvalidArgumentException + * @throws InactiveScopeException + * @throws LogicException + * @throws \Exception * @return object The associated service * - * @throws InvalidArgumentException if the service is not defined - * @throws LogicException if the service has a circular reference to itself - * * @see Reference * * @api @@ -1116,7 +1117,7 @@ private function callMethod($service, $call) * @param mixed $service * @param string $id * - * @throws Exception\RuntimeException + * @throws InactiveScopeException */ private function shareService(Definition $definition, $service, $id) { diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 18523dc0f3560..64b7b7af2f22f 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -578,19 +578,22 @@ private function addService($id, $definition) $lazyInitializationDoc = ''; } - $code = <<isProxyCandidate($definition); + $visibility = $isProxyCandidate ? 'public' : 'protected'; + $code = <<addProxyLoading($id, $definition); + $code .= $isProxyCandidate ? $this->addProxyLoading($id, $definition) : ''; if (!in_array($scope, array(ContainerInterface::SCOPE_CONTAINER, ContainerInterface::SCOPE_PROTOTYPE))) { $code .= << Date: Thu, 25 Apr 2013 23:33:15 +0200 Subject: [PATCH 19/21] Enforcing soft dependency to ProxyManager --- .../DependencyInjection/Dumper/PhpDumper.php | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 64b7b7af2f22f..b502bdc6f99d8 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -176,7 +176,7 @@ private function addProxyLoading($id, Definition $definition) } $methodName = 'get' . Container::camelize($id) . 'Service'; - $proxyClass = str_replace('\\', '', $definition->getClass()) . '_' . md5(spl_object_hash($definition)); + $proxyClass = str_replace('\\', '', $definition->getClass()) . '_' . spl_object_hash($definition); return <<container->getDefinitions(), - function (Definition $definition) { - return $definition->isLazy() && $definition->getClass(); + $proxyDefinitions = array(); + + foreach ($this->container->getDefinitions() as $definition) { + if ($this->isProxyCandidate($definition)) { + $proxyDefinitions[] = $definition; } - ); + } // avoids hard dependency to ProxyManager - if (empty($proxyDefinitions) || !class_exists('ProxyManager\\GeneratorStrategy\\BaseGeneratorStrategy')) { + if (empty($proxyDefinitions)) { return ''; } @@ -365,13 +366,13 @@ private function addServiceInstance($id, $definition) throw new InvalidArgumentException(sprintf('"%s" is not a valid class name for the "%s" service.', $class, $id)); } - $simple = $this->isSimpleInstance($id, $definition); - - $instantiation = ''; + $simple = $this->isSimpleInstance($id, $definition); + $isProxyCandidate = $this->isProxyCandidate($definition); + $instantiation = ''; - if (!$this->isProxyCandidate($definition) && ContainerInterface::SCOPE_CONTAINER === $definition->getScope()) { + if (!$isProxyCandidate && ContainerInterface::SCOPE_CONTAINER === $definition->getScope()) { $instantiation = "\$this->services['$id'] = ".($simple ? '' : '$instance'); - } elseif (!$this->isProxyCandidate($definition) && ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) { + } elseif (!$isProxyCandidate && ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) { $instantiation = "\$this->services['$id'] = \$this->scopedServices['$scope']['$id'] = ".($simple ? '' : '$instance'); } elseif (!$simple) { $instantiation = '$instance'; @@ -1279,7 +1280,8 @@ private function getServiceCall($id, Reference $reference = null) } /** - * Tells if the given definitions are to be used for proxying + * Tells if the given definitions are to be used for proxying, and if proxying is possible, + * since ProxyManager may not be available * * @param Definition $definition * @@ -1287,7 +1289,11 @@ private function getServiceCall($id, Reference $reference = null) */ private function isProxyCandidate(Definition $definition) { - if (!($definition->isLazy() && $definition->getClass())) { + if (!( + $definition->isLazy() + && $definition->getClass() + && class_exists('ProxyManager\\Factory\\LazyLoadingValueHolderFactory') + )) { return false; } From 1e24767d1a4824da39935864bcad4e7e87b31461 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Thu, 25 Apr 2013 23:38:19 +0200 Subject: [PATCH 20/21] Reverting documentation changes, adding exception types description in `@throws` --- .../DependencyInjection/ContainerBuilder.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 030551a955782..654c42b302b3f 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -434,14 +434,15 @@ public function has($id) * Gets a service. * * @param string $id The service identifier - * @param int $invalidBehavior The behavior when the service does not exist + * @param integer $invalidBehavior The behavior when the service does not exist * - * @throws InvalidArgumentException - * @throws InactiveScopeException - * @throws LogicException - * @throws \Exception * @return object The associated service * + * @throws InvalidArgumentException when no definitions are available + * @throws InactiveScopeException when the current scope is not active + * @throws LogicException when a circular dependency is detected + * @throws \Exception + * * @see Reference * * @api From 450635a22654e2ba4f93e346e56677f767278e62 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sat, 27 Apr 2013 21:30:48 +0200 Subject: [PATCH 21/21] Lazier checks on the proxy structure --- .../Fixtures/php/lazy_service_structure.txt | 60 +------------------ 1 file changed, 1 insertion(+), 59 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service_structure.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service_structure.txt index a9210c6d35eaf..1eaf8cad606c3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service_structure.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_service_structure.txt @@ -64,62 +64,4 @@ class ProjectServiceContainer extends Container } class stdClass_%s extends \stdClass implements \ProxyManager\Proxy\LazyLoadingInterface, \ProxyManager\Proxy\ValueHolderInterface -{%a - /**%a*/ - private %s = null; - - /**%a*/ - private %s = null; - - /**%a*/ - public function __construct($initializer) - {%a} - - /**%a*/ - public function __get($name) - {%a} - - /**%a*/ - public function __set($name, $value) - {%a} - - /**%a*/ - public function __isset($name) - {%a} - - /**%a*/ - public function __unset($name) - {%a} - - /**%a*/ - public function __clone() - {%a} - - /**%a*/ - public function __sleep() - {%a} - - /**%a*/ - public function __wakeup() - {%a} - - /**%a*/ - public function setProxyInitializer(\Closure $initializer = null) - {%a} - - /**%a*/ - public function getProxyInitializer() - {%a} - - /**%a*/ - public function initializeProxy() - {%a} - - /**%a*/ - public function isProxyInitialized() - {%a} - - /**%a*/ - public function getWrappedValueHolderValue() - {%a} -%a} \ No newline at end of file +{%a} \ No newline at end of file pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy