[ProxyManagerBridge][DI] allow proxifying interfaces with "lazy: Some\ProxifiedInterface"
This commit is contained in:
parent
f20eaf26c2
commit
1d9f1d1b70
@ -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',
|
||||
|
@ -1,6 +1,11 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
4.2.0
|
||||
-----
|
||||
|
||||
* allowed creating lazy-proxies from interfaces
|
||||
|
||||
3.3.0
|
||||
-----
|
||||
|
||||
|
@ -18,14 +18,14 @@ use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\LazyLoadingValueHolderGenera
|
||||
/**
|
||||
* @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();
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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();
|
||||
}
|
||||
}
|
@ -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 @@ use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\InstantiatorInt
|
||||
*/
|
||||
class RuntimeInstantiator implements InstantiatorInterface
|
||||
{
|
||||
/**
|
||||
* @var LazyLoadingValueHolderFactory
|
||||
*/
|
||||
private $factory;
|
||||
|
||||
public function __construct()
|
||||
@ -36,11 +32,7 @@ class RuntimeInstantiator implements InstantiatorInterface
|
||||
$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 @@ class RuntimeInstantiator implements InstantiatorInterface
|
||||
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);
|
||||
|
||||
|
@ -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 @@ use Zend\Code\Generator\ClassGenerator;
|
||||
*/
|
||||
class LazyLoadingValueHolderGenerator extends BaseGenerator
|
||||
{
|
||||
private $fluentSafe = false;
|
||||
|
||||
public function setFluentSafe(bool $fluentSafe)
|
||||
{
|
||||
$this->fluentSafe = $fluentSafe;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@ -26,6 +34,52 @@ class LazyLoadingValueHolderGenerator extends BaseGenerator
|
||||
{
|
||||
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 @@ class LazyLoadingValueHolderGenerator extends BaseGenerator
|
||||
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,6 @@ namespace Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper;
|
||||
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 @@ class ProxyDumper implements DumperInterface
|
||||
*/
|
||||
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 @@ class ProxyDumper implements DumperInterface
|
||||
|
||||
$proxyClass = $this->getProxyClassName($definition);
|
||||
|
||||
$hasStaticConstructor = $this->generateProxyClass($definition)->hasMethod('staticProxyConstructor');
|
||||
|
||||
$constructorCall = sprintf($hasStaticConstructor ? '%s::staticProxyConstructor' : 'new %s', '\\'.$proxyClass);
|
||||
|
||||
return <<<EOF
|
||||
if (\$lazyLoad) {
|
||||
$instantiation \$this->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 @@ EOF;
|
||||
{
|
||||
$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 @@ EOF;
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
return new class
|
||||
{
|
||||
public $proxyClass;
|
||||
private $privates = array();
|
||||
|
||||
public function getFooService($lazyLoad = true)
|
||||
{
|
||||
if ($lazyLoad) {
|
||||
return $this->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();
|
||||
}
|
||||
};
|
@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
class SunnyInterface_1eff735 implements \ProxyManager\Proxy\VirtualProxyInterface, \Symfony\Bridge\ProxyManager\Tests\LazyProxy\PhpDumper\DummyInterface, \Symfony\Bridge\ProxyManager\Tests\LazyProxy\PhpDumper\SunnyInterface
|
||||
{
|
||||
|
||||
private $valueHolder1eff735 = null;
|
||||
|
||||
private $initializer1eff735 = null;
|
||||
|
||||
private static $publicProperties1eff735 = [
|
||||
|
||||
];
|
||||
|
||||
public function dummy()
|
||||
{
|
||||
$this->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;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -120,6 +120,61 @@ class ProxyDumperTest extends TestCase
|
||||
$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 = "<?php\n\n".$this->dumper->getProxyCode($definition);
|
||||
$factory = $this->dumper->getProxyFactoryCode($definition, 'foo', '$this->getFooService(false)');
|
||||
$factory = <<<EOPHP
|
||||
<?php
|
||||
|
||||
return new class
|
||||
{
|
||||
public \$proxyClass;
|
||||
private \$privates = array();
|
||||
|
||||
public function getFooService(\$lazyLoad = true)
|
||||
{
|
||||
{$factory} return new {$class}();
|
||||
}
|
||||
|
||||
protected function createProxy(\$class, \Closure \$factory)
|
||||
{
|
||||
\$this->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 @@ class ProxyDumperTest extends TestCase
|
||||
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();
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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',
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -265,10 +265,17 @@ class XmlFileLoader extends FileLoader
|
||||
$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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -407,7 +407,10 @@ class YamlFileLoader extends FileLoader
|
||||
}
|
||||
|
||||
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'])) {
|
||||
|
@ -124,7 +124,7 @@
|
||||
<xsd:attribute name="shared" type="boolean" />
|
||||
<xsd:attribute name="public" type="boolean" />
|
||||
<xsd:attribute name="synthetic" type="boolean" />
|
||||
<xsd:attribute name="lazy" type="boolean" />
|
||||
<xsd:attribute name="lazy" type="xsd:string" />
|
||||
<xsd:attribute name="abstract" type="boolean" />
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="parent" type="xsd:string" />
|
||||
@ -145,7 +145,7 @@
|
||||
<xsd:attribute name="id" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="shared" type="boolean" />
|
||||
<xsd:attribute name="public" type="boolean" />
|
||||
<xsd:attribute name="lazy" type="boolean" />
|
||||
<xsd:attribute name="lazy" type="xsd:string" />
|
||||
<xsd:attribute name="autowire" type="boolean" />
|
||||
<xsd:attribute name="autoconfigure" type="boolean" />
|
||||
</xsd:complexType>
|
||||
@ -167,7 +167,7 @@
|
||||
<xsd:attribute name="exclude" type="xsd:string" />
|
||||
<xsd:attribute name="shared" type="boolean" />
|
||||
<xsd:attribute name="public" type="boolean" />
|
||||
<xsd:attribute name="lazy" type="boolean" />
|
||||
<xsd:attribute name="lazy" type="xsd:string" />
|
||||
<xsd:attribute name="abstract" type="boolean" />
|
||||
<xsd:attribute name="parent" type="xsd:string" />
|
||||
<xsd:attribute name="autowire" type="boolean" />
|
||||
|
@ -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
|
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
|
||||
|
||||
return function (ContainerConfigurator $c) {
|
||||
$di = $c->services();
|
||||
$di->set('foo', 'stdClass')->lazy('SomeInterface');
|
||||
};
|
@ -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
|
||||
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
<services>
|
||||
<service id="foo" lazy="SomeInterface" />
|
||||
</services>
|
||||
</container>
|
@ -0,0 +1,3 @@
|
||||
services:
|
||||
foo:
|
||||
lazy: SomeInterface
|
@ -72,9 +72,11 @@ class PhpFileLoaderTest extends TestCase
|
||||
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');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -816,4 +816,14 @@ class XmlFileLoaderTest extends TestCase
|
||||
'$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'));
|
||||
}
|
||||
}
|
||||
|
@ -739,4 +739,14 @@ class YamlFileLoaderTest extends TestCase
|
||||
'$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'));
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user