diff --git a/.php_cs.dist b/.php_cs.dist index 4720014f40b7c..620c6ee2db959 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -19,6 +19,7 @@ return PhpCsFixer\Config::create() ->in(__DIR__.'/src') ->append(array(__FILE__)) ->exclude(array( + 'Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures', // directories containing files with content that is autogenerated by `var_export`, which breaks CS in output code 'Symfony/Component/Cache/Tests/Marshaller/Fixtures', 'Symfony/Component/DependencyInjection/Tests/Fixtures', diff --git a/src/Symfony/Bridge/ProxyManager/CHANGELOG.md b/src/Symfony/Bridge/ProxyManager/CHANGELOG.md index 56c8b20e28272..3435a4a186494 100644 --- a/src/Symfony/Bridge/ProxyManager/CHANGELOG.md +++ b/src/Symfony/Bridge/ProxyManager/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +4.2.0 +----- + + * allowed creating lazy-proxies from interfaces + 3.3.0 ----- diff --git a/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/LazyLoadingValueHolderFactoryV2.php b/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/LazyLoadingValueHolderFactory.php similarity index 85% rename from src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/LazyLoadingValueHolderFactoryV2.php rename to src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/LazyLoadingValueHolderFactory.php index f41fc20b5d523..a37aa2e04c808 100644 --- a/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/LazyLoadingValueHolderFactoryV2.php +++ b/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/LazyLoadingValueHolderFactory.php @@ -18,14 +18,14 @@ /** * @internal */ -class LazyLoadingValueHolderFactoryV2 extends BaseFactory +class LazyLoadingValueHolderFactory extends BaseFactory { private $generator; /** * {@inheritdoc} */ - protected function getGenerator(): ProxyGeneratorInterface + public function getGenerator(): ProxyGeneratorInterface { return $this->generator ?: $this->generator = new LazyLoadingValueHolderGenerator(); } diff --git a/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/LazyLoadingValueHolderFactoryV1.php b/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/LazyLoadingValueHolderFactoryV1.php deleted file mode 100644 index 3298b84d46278..0000000000000 --- a/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/LazyLoadingValueHolderFactoryV1.php +++ /dev/null @@ -1,31 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\ProxyManager\LazyProxy\Instantiator; - -use ProxyManager\Factory\LazyLoadingValueHolderFactory as BaseFactory; -use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\LazyLoadingValueHolderGenerator; - -/** - * @internal - */ -class LazyLoadingValueHolderFactoryV1 extends BaseFactory -{ - private $generatorV1; - - /** - * {@inheritdoc} - */ - protected function getGenerator() - { - return $this->generatorV1 ?: $this->generatorV1 = new LazyLoadingValueHolderGenerator(); - } -} diff --git a/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/RuntimeInstantiator.php b/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/RuntimeInstantiator.php index 33fc49e1012d9..cc68d65058839 100644 --- a/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/RuntimeInstantiator.php +++ b/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/RuntimeInstantiator.php @@ -12,7 +12,6 @@ namespace Symfony\Bridge\ProxyManager\LazyProxy\Instantiator; use ProxyManager\Configuration; -use ProxyManager\Factory\LazyLoadingValueHolderFactory; use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy; use ProxyManager\Proxy\LazyLoadingInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -26,9 +25,6 @@ */ class RuntimeInstantiator implements InstantiatorInterface { - /** - * @var LazyLoadingValueHolderFactory - */ private $factory; public function __construct() @@ -36,11 +32,7 @@ public function __construct() $config = new Configuration(); $config->setGeneratorStrategy(new EvaluatingGeneratorStrategy()); - if (method_exists('ProxyManager\Version', 'getVersion')) { - $this->factory = new LazyLoadingValueHolderFactoryV2($config); - } else { - $this->factory = new LazyLoadingValueHolderFactoryV1($config); - } + $this->factory = new LazyLoadingValueHolderFactory($config); } /** @@ -49,9 +41,9 @@ public function __construct() public function instantiateProxy(ContainerInterface $container, Definition $definition, $id, $realInstantiator) { return $this->factory->createProxy( - $definition->getClass(), + $this->factory->getGenerator()->getProxifiedClass($definition), function (&$wrappedInstance, LazyLoadingInterface $proxy) use ($realInstantiator) { - $wrappedInstance = call_user_func($realInstantiator); + $wrappedInstance = \call_user_func($realInstantiator); $proxy->setProxyInitializer(null); diff --git a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/LazyLoadingValueHolderGenerator.php b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/LazyLoadingValueHolderGenerator.php index 1d9432f622b41..356fd44cda454 100644 --- a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/LazyLoadingValueHolderGenerator.php +++ b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/LazyLoadingValueHolderGenerator.php @@ -12,6 +12,7 @@ namespace Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper; use ProxyManager\ProxyGenerator\LazyLoadingValueHolderGenerator as BaseGenerator; +use Symfony\Component\DependencyInjection\Definition; use Zend\Code\Generator\ClassGenerator; /** @@ -19,6 +20,13 @@ */ class LazyLoadingValueHolderGenerator extends BaseGenerator { + private $fluentSafe = false; + + public function setFluentSafe(bool $fluentSafe) + { + $this->fluentSafe = $fluentSafe; + } + /** * {@inheritdoc} */ @@ -26,6 +34,52 @@ public function generate(\ReflectionClass $originalClass, ClassGenerator $classG { parent::generate($originalClass, $classGenerator); + foreach ($classGenerator->getMethods() as $method) { + $body = preg_replace( + '/(\$this->initializer[0-9a-f]++) && \1->__invoke\(\$this->(valueHolder[0-9a-f]++), (.*?), \1\);/', + '$1 && ($1->__invoke(\$$2, $3, $1) || 1) && $this->$2 = \$$2;', + $method->getBody() + ); + $body = str_replace('(new \ReflectionClass(get_class()))', '$reflection', $body); + + if ($originalClass->isInterface()) { + $body = str_replace('get_parent_class($this)', var_export($originalClass->name, true), $body); + $body = preg_replace_callback('/\n\n\$realInstanceReflection = [^{]++\{([^}]++)\}\n\n.*/s', function ($m) { + $r = ''; + foreach (explode("\n", $m[1]) as $line) { + $r .= "\n".substr($line, 4); + if (0 === strpos($line, ' return ')) { + break; + } + } + + return $r; + }, $body); + } + + if ($this->fluentSafe) { + $indent = $method->getIndentation(); + $method->setIndentation(''); + $code = $method->generate(); + if (null !== $docBlock = $method->getDocBlock()) { + $code = substr($code, \strlen($docBlock->generate())); + } + $refAmp = (strpos($code, '&') ?: \PHP_INT_MAX) <= strpos($code, '(') ? '&' : ''; + $body = preg_replace( + '/\nreturn (\$this->valueHolder[0-9a-f]++)(->[^;]++);$/', + "\nif ($1 === \$returnValue = {$refAmp}$1$2) {\n \$returnValue = \$this;\n}\n\nreturn \$returnValue;", + $body + ); + $method->setIndentation($indent); + } + + if (0 === strpos($originalClass->getFilename(), __FILE__)) { + $body = str_replace(var_export($originalClass->name, true), '__CLASS__', $body); + } + + $method->setBody($body); + } + if ($classGenerator->hasMethod('__destruct')) { $destructor = $classGenerator->getMethod('__destruct'); $body = $destructor->getBody(); @@ -37,5 +91,53 @@ public function generate(\ReflectionClass $originalClass, ClassGenerator $classG $destructor->setBody($newBody); } + + if (0 === strpos($originalClass->getFilename(), __FILE__)) { + $interfaces = $classGenerator->getImplementedInterfaces(); + array_pop($interfaces); + $classGenerator->setImplementedInterfaces(array_merge($interfaces, $originalClass->getInterfaceNames())); + } + } + + public function getProxifiedClass(Definition $definition): ?string + { + if (!$definition->hasTag('proxy')) { + return \class_exists($class = $definition->getClass()) || \interface_exists($class) ? $class : null; + } + if (!$definition->isLazy()) { + throw new \InvalidArgumentException(sprintf('Invalid definition for service of class "%s": setting the "proxy" tag on a service requires it to be "lazy".', $definition->getClass())); + } + $tags = $definition->getTag('proxy'); + if (!isset($tags[0]['interface'])) { + throw new \InvalidArgumentException(sprintf('Invalid definition for service of class "%s": the "interface" attribute is missing on the "proxy" tag.', $definition->getClass())); + } + if (1 === \count($tags)) { + return \class_exists($tags[0]['interface']) || \interface_exists($tags[0]['interface']) ? $tags[0]['interface'] : null; + } + + $proxyInterface = 'LazyProxy'; + $interfaces = ''; + foreach ($tags as $tag) { + if (!isset($tag['interface'])) { + throw new \InvalidArgumentException(sprintf('Invalid definition for service of class "%s": the "interface" attribute is missing on a "proxy" tag.', $definition->getClass())); + } + if (!\interface_exists($tag['interface'])) { + throw new \InvalidArgumentException(sprintf('Invalid definition for service of class "%s": several "proxy" tags found but "%s" is not an interface.', $definition->getClass(), $tag['interface'])); + } + + $proxyInterface .= '\\'.$tag['interface']; + $interfaces .= ', \\'.$tag['interface']; + } + + if (!\interface_exists($proxyInterface)) { + $i = strrpos($proxyInterface, '\\'); + $namespace = substr($proxyInterface, 0, $i); + $interface = substr($proxyInterface, 1 + $i); + $interfaces = substr($interfaces, 2); + + eval("namespace {$namespace}; interface {$interface} extends {$interfaces} {}"); + } + + return $proxyInterface; } } diff --git a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php index 0871c4ce9d70d..3ce787b85222f 100644 --- a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php +++ b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php @@ -14,7 +14,6 @@ use ProxyManager\Generator\ClassGenerator; use ProxyManager\GeneratorStrategy\BaseGeneratorStrategy; use ProxyManager\Version; -use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface; @@ -43,7 +42,7 @@ public function __construct(string $salt = '') */ public function isProxyCandidate(Definition $definition) { - return $definition->isLazy() && ($class = $definition->getClass()) && class_exists($class); + return ($definition->isLazy() || $definition->hasTag('proxy')) && $this->proxyGenerator->getProxifiedClass($definition); } /** @@ -63,14 +62,10 @@ public function getProxyFactoryCode(Definition $definition, $id, $factoryCode = $proxyClass = $this->getProxyClassName($definition); - $hasStaticConstructor = $this->generateProxyClass($definition)->hasMethod('staticProxyConstructor'); - - $constructorCall = sprintf($hasStaticConstructor ? '%s::staticProxyConstructor' : 'new %s', '\\'.$proxyClass); - return <<createProxy('$proxyClass', function () { - return $constructorCall(function (&\$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface \$proxy) { + return \\$proxyClass::staticProxyConstructor(function (&\$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface \$proxy) { \$wrappedInstance = $factoryCode; \$proxy->setProxyInitializer(null); @@ -91,12 +86,6 @@ public function getProxyCode(Definition $definition) { $code = $this->classGenerator->generate($this->generateProxyClass($definition)); - $code = preg_replace( - '/(\$this->initializer[0-9a-f]++) && \1->__invoke\(\$this->(valueHolder[0-9a-f]++), (.*?), \1\);/', - '$1 && ($1->__invoke(\$$2, $3, $1) || 1) && $this->$2 = \$$2;', - $code - ); - if (version_compare(self::getProxyManagerVersion(), '2.2', '<')) { $code = preg_replace( '/((?:\$(?:this|initializer|instance)->)?(?:publicProperties|initializer|valueHolder))[0-9a-f]++/', @@ -122,20 +111,26 @@ private static function getProxyManagerVersion(): string */ private function getProxyClassName(Definition $definition): string { - return preg_replace('/^.*\\\\/', '', $definition->getClass()).'_'.$this->getIdentifierSuffix($definition); + $class = $this->proxyGenerator->getProxifiedClass($definition); + + return preg_replace('/^.*\\\\/', '', $class).'_'.$this->getIdentifierSuffix($definition); } private function generateProxyClass(Definition $definition): ClassGenerator { $generatedClass = new ClassGenerator($this->getProxyClassName($definition)); + $class = $this->proxyGenerator->getProxifiedClass($definition); - $this->proxyGenerator->generate(new \ReflectionClass($definition->getClass()), $generatedClass); + $this->proxyGenerator->setFluentSafe($definition->hasTag('proxy')); + $this->proxyGenerator->generate(new \ReflectionClass($class), $generatedClass); return $generatedClass; } private function getIdentifierSuffix(Definition $definition): string { - return substr(hash('sha256', $definition->getClass().$this->salt), -7); + $class = $this->proxyGenerator->getProxifiedClass($definition); + + return substr(hash('sha256', $class.$this->salt), -7); } } diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-factory.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-factory.php new file mode 100644 index 0000000000000..648eb36d4b598 --- /dev/null +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-factory.php @@ -0,0 +1,31 @@ +privates['foo'] = $this->createProxy('SunnyInterface_1eff735', function () { + return \SunnyInterface_1eff735::staticProxyConstructor(function (&$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface $proxy) { + $wrappedInstance = $this->getFooService(false); + + $proxy->setProxyInitializer(null); + + return true; + }); + }); + } + + return new Symfony\Bridge\ProxyManager\Tests\LazyProxy\PhpDumper\DummyClass(); + } + + protected function createProxy($class, \Closure $factory) + { + $this->proxyClass = $class; + + return $factory(); + } +}; diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-implem.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-implem.php new file mode 100644 index 0000000000000..1b5ea69c907e7 --- /dev/null +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-implem.php @@ -0,0 +1,165 @@ +initializer1eff735 && ($this->initializer1eff735->__invoke($valueHolder1eff735, $this, 'dummy', array(), $this->initializer1eff735) || 1) && $this->valueHolder1eff735 = $valueHolder1eff735; + + if ($this->valueHolder1eff735 === $returnValue = $this->valueHolder1eff735->dummy()) { + $returnValue = $this; + } + + return $returnValue; + } + + public function & dummyRef() + { + $this->initializer1eff735 && ($this->initializer1eff735->__invoke($valueHolder1eff735, $this, 'dummyRef', array(), $this->initializer1eff735) || 1) && $this->valueHolder1eff735 = $valueHolder1eff735; + + if ($this->valueHolder1eff735 === $returnValue = &$this->valueHolder1eff735->dummyRef()) { + $returnValue = $this; + } + + return $returnValue; + } + + public function sunny() + { + $this->initializer1eff735 && ($this->initializer1eff735->__invoke($valueHolder1eff735, $this, 'sunny', array(), $this->initializer1eff735) || 1) && $this->valueHolder1eff735 = $valueHolder1eff735; + + if ($this->valueHolder1eff735 === $returnValue = $this->valueHolder1eff735->sunny()) { + $returnValue = $this; + } + + return $returnValue; + } + + public static function staticProxyConstructor($initializer) + { + static $reflection; + + $reflection = $reflection ?: $reflection = new \ReflectionClass(__CLASS__); + $instance = $reflection->newInstanceWithoutConstructor(); + + $instance->initializer1eff735 = $initializer; + + return $instance; + } + + public function __construct() + { + static $reflection; + + if (! $this->valueHolder1eff735) { + $reflection = $reflection ?: new \ReflectionClass(__CLASS__); + $this->valueHolder1eff735 = $reflection->newInstanceWithoutConstructor(); + } + } + + public function & __get($name) + { + $this->initializer1eff735 && ($this->initializer1eff735->__invoke($valueHolder1eff735, $this, '__get', ['name' => $name], $this->initializer1eff735) || 1) && $this->valueHolder1eff735 = $valueHolder1eff735; + + if (isset(self::$publicProperties1eff735[$name])) { + return $this->valueHolder1eff735->$name; + } + + $targetObject = $this->valueHolder1eff735; + + $backtrace = debug_backtrace(false); + trigger_error( + sprintf( + 'Undefined property: %s::$%s in %s on line %s', + __CLASS__, + $name, + $backtrace[0]['file'], + $backtrace[0]['line'] + ), + \E_USER_NOTICE + ); + return $targetObject->$name; + } + + public function __set($name, $value) + { + $this->initializer1eff735 && ($this->initializer1eff735->__invoke($valueHolder1eff735, $this, '__set', array('name' => $name, 'value' => $value), $this->initializer1eff735) || 1) && $this->valueHolder1eff735 = $valueHolder1eff735; + + $targetObject = $this->valueHolder1eff735; + + return $targetObject->$name = $value; + } + + public function __isset($name) + { + $this->initializer1eff735 && ($this->initializer1eff735->__invoke($valueHolder1eff735, $this, '__isset', array('name' => $name), $this->initializer1eff735) || 1) && $this->valueHolder1eff735 = $valueHolder1eff735; + + $targetObject = $this->valueHolder1eff735; + + return isset($targetObject->$name); + } + + public function __unset($name) + { + $this->initializer1eff735 && ($this->initializer1eff735->__invoke($valueHolder1eff735, $this, '__unset', array('name' => $name), $this->initializer1eff735) || 1) && $this->valueHolder1eff735 = $valueHolder1eff735; + + $targetObject = $this->valueHolder1eff735; + + unset($targetObject->$name); +return; + } + + public function __clone() + { + $this->initializer1eff735 && ($this->initializer1eff735->__invoke($valueHolder1eff735, $this, '__clone', array(), $this->initializer1eff735) || 1) && $this->valueHolder1eff735 = $valueHolder1eff735; + + $this->valueHolder1eff735 = clone $this->valueHolder1eff735; + } + + public function __sleep() + { + $this->initializer1eff735 && ($this->initializer1eff735->__invoke($valueHolder1eff735, $this, '__sleep', array(), $this->initializer1eff735) || 1) && $this->valueHolder1eff735 = $valueHolder1eff735; + + return array('valueHolder1eff735'); + } + + public function __wakeup() + { + } + + public function setProxyInitializer(\Closure $initializer = null) + { + $this->initializer1eff735 = $initializer; + } + + public function getProxyInitializer() + { + return $this->initializer1eff735; + } + + public function initializeProxy() : bool + { + return $this->initializer1eff735 && ($this->initializer1eff735->__invoke($valueHolder1eff735, $this, 'initializeProxy', array(), $this->initializer1eff735) || 1) && $this->valueHolder1eff735 = $valueHolder1eff735; + } + + public function isProxyInitialized() : bool + { + return null !== $this->valueHolder1eff735; + } + + public function getWrappedValueHolderValue() + { + return $this->valueHolder1eff735; + } + + +} diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php index e12b25fcb02f8..634ab6627c321 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php @@ -120,6 +120,61 @@ public function testGetProxyFactoryCodeWithoutCustomMethod() $this->dumper->getProxyFactoryCode($definition, 'foo'); } + public function testGetProxyFactoryCodeForInterface() + { + $class = DummyClass::class; + $definition = new Definition($class); + + $definition->setLazy(true); + $definition->addTag('proxy', array('interface' => DummyInterface::class)); + $definition->addTag('proxy', array('interface' => SunnyInterface::class)); + + $implem = "dumper->getProxyCode($definition); + $factory = $this->dumper->getProxyFactoryCode($definition, 'foo', '$this->getFooService(false)'); + $factory = <<proxyClass = \$class; + + return \$factory(); + } +}; + +EOPHP; + + $implem = preg_replace('#\n /\*\*.*?\*/#s', '', $implem); + $implem = str_replace('getWrappedValueHolderValue() : ?object', 'getWrappedValueHolderValue()', $implem); + $implem = str_replace("array(\n \n );", "[\n \n ];", $implem); + $this->assertStringEqualsFile(__DIR__.'/Fixtures/proxy-implem.php', $implem); + $this->assertStringEqualsFile(__DIR__.'/Fixtures/proxy-factory.php', $factory); + + require_once __DIR__.'/Fixtures/proxy-implem.php'; + $factory = require __DIR__.'/Fixtures/proxy-factory.php'; + + $foo = $factory->getFooService(); + + $this->assertInstanceof($factory->proxyClass, $foo); + $this->assertInstanceof(DummyInterface::class, $foo); + $this->assertInstanceof(SunnyInterface::class, $foo); + $this->assertNotInstanceof(DummyClass::class, $foo); + $this->assertSame($foo, $foo->dummy()); + + $foo->dynamicProp = 123; + $this->assertSame(123, @$foo->dynamicProp); + } + /** * @return array */ @@ -142,3 +197,34 @@ function ($definition) { return $definitions; } } + +final class DummyClass implements DummyInterface, SunnyInterface +{ + public function dummy() + { + return $this; + } + + public function sunny() + { + } + + public function &dummyRef() + { + return $this->ref; + } +} + +interface DummyInterface +{ + public function dummy(); + + public function &dummyRef(); +} + +interface SunnyInterface +{ + public function dummy(); + + public function sunny(); +} diff --git a/src/Symfony/Bridge/ProxyManager/composer.json b/src/Symfony/Bridge/ProxyManager/composer.json index a224d5a8a90ad..441389af8271b 100644 --- a/src/Symfony/Bridge/ProxyManager/composer.json +++ b/src/Symfony/Bridge/ProxyManager/composer.json @@ -18,7 +18,7 @@ "require": { "php": "^7.1.3", "symfony/dependency-injection": "~3.4|~4.0", - "ocramius/proxy-manager": "~0.4|~1.0|~2.0" + "ocramius/proxy-manager": "~2.1" }, "require-dev": { "symfony/config": "~3.4|~4.0" diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php index 2ddbe4174428d..37d35c1a09e25 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php @@ -44,6 +44,7 @@ class UnusedTagsPass implements CompilerPassInterface 'messenger.receiver', 'messenger.message_handler', 'monolog.logger', + 'proxy', 'routing.expression_language_provider', 'routing.loader', 'security.expression_language_provider', diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/LazyTrait.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/LazyTrait.php index 2af867c19bb26..d8ea55e82f7f3 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/LazyTrait.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/LazyTrait.php @@ -16,11 +16,16 @@ trait LazyTrait /** * Sets the lazy flag of this service. * + * @param bool|string A FQCN to derivate the lazy proxy from or `true` to make it extend from the definition's class + * * @return $this */ - final public function lazy(bool $lazy = true) + final public function lazy($lazy = true) { - $this->definition->setLazy($lazy); + $this->definition->setLazy((bool) $lazy); + if (\is_string($lazy)) { + $this->definition->addTag('proxy', array('interface' => $lazy)); + } return $this; } diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 71e991c9ff774..584e3992c92ce 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -265,10 +265,17 @@ private function parseDefinition(\DOMElement $service, $file, array $defaults) $definition->setChanges(array()); } - foreach (array('class', 'public', 'shared', 'synthetic', 'lazy', 'abstract') as $key) { + foreach (array('class', 'public', 'shared', 'synthetic', 'abstract') as $key) { if ($value = $service->getAttribute($key)) { $method = 'set'.$key; - $definition->$method(XmlUtils::phpize($value)); + $definition->$method($value = XmlUtils::phpize($value)); + } + } + + if ($value = $service->getAttribute('lazy')) { + $definition->setLazy((bool) $value = XmlUtils::phpize($value)); + if (\is_string($value)) { + $definition->addTag('proxy', array('interface' => $value)); } } diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index 11eb8e45c7507..79b377f0bdf3c 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -407,7 +407,10 @@ private function parseDefinition($id, $service, $file, array $defaults) } if (isset($service['lazy'])) { - $definition->setLazy($service['lazy']); + $definition->setLazy((bool) $service['lazy']); + if (\is_string($service['lazy'])) { + $definition->addTag('proxy', array('interface' => $service['lazy'])); + } } if (isset($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 21e3b593105d2..2e17476cd165b 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 @@ -124,7 +124,7 @@ - + @@ -145,7 +145,7 @@ - + @@ -167,7 +167,7 @@ - + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/lazy_fqcn.expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/lazy_fqcn.expected.yml new file mode 100644 index 0000000000000..d5a272c4bf7ca --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/lazy_fqcn.expected.yml @@ -0,0 +1,12 @@ + +services: + service_container: + class: Symfony\Component\DependencyInjection\ContainerInterface + public: true + synthetic: true + foo: + class: stdClass + public: true + tags: + - { name: proxy, interface: SomeInterface } + lazy: true diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/lazy_fqcn.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/lazy_fqcn.php new file mode 100644 index 0000000000000..7cde4ef2d0699 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/lazy_fqcn.php @@ -0,0 +1,8 @@ +services(); + $di->set('foo', 'stdClass')->lazy('SomeInterface'); +}; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype_array.expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype_array.expected.yml index e8a03691c95c6..ebfe087d779cf 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype_array.expected.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype_array.expected.yml @@ -10,7 +10,7 @@ services: tags: - { name: foo } - { name: baz } - deprecated: "%service_id%" + deprecated: '%service_id%' arguments: [1] factory: f Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Sub\Bar: @@ -19,7 +19,7 @@ services: tags: - { name: foo } - { name: baz } - deprecated: "%service_id%" + deprecated: '%service_id%' lazy: true arguments: [1] factory: f diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_lazy_fqcn.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_lazy_fqcn.xml new file mode 100644 index 0000000000000..f7783a89a8708 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_lazy_fqcn.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_lazy_fqcn.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_lazy_fqcn.yml new file mode 100644 index 0000000000000..3ac3a1a762b6f --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_lazy_fqcn.yml @@ -0,0 +1,3 @@ +services: + foo: + lazy: SomeInterface diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php index b584a6922c625..e7c7f009e3de4 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php @@ -72,9 +72,11 @@ public function provideConfig() yield array('defaults'); yield array('instanceof'); yield array('prototype'); + yield array('prototype_array'); yield array('child'); yield array('php7'); yield array('anonymous'); + yield array('lazy_fqcn'); } /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index 2e87291bf2c15..27a3d8ca94844 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -816,4 +816,14 @@ public function testBindings() '$factory' => 'factory', ), array_map(function (BoundArgument $v) { return $v->getValues()[0]; }, $definition->getBindings())); } + + public function testFqcnLazyProxy() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services_lazy_fqcn.xml'); + + $definition = $container->getDefinition('foo'); + $this->assertSame(array(array('interface' => 'SomeInterface')), $definition->getTag('proxy')); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index 8ddc15f8881c3..4e9ca2a4899e3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -739,4 +739,14 @@ public function testBindings() '$factory' => 'factory', ), array_map(function (BoundArgument $v) { return $v->getValues()[0]; }, $definition->getBindings())); } + + public function testFqcnLazyProxy() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_lazy_fqcn.yml'); + + $definition = $container->getDefinition('foo'); + $this->assertSame(array(array('interface' => 'SomeInterface')), $definition->getTag('proxy')); + } } 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