Augmentation vers version 3.3.0

This commit is contained in:
Gauvain Boiché
2020-03-31 15:31:03 +02:00
parent d926806907
commit a1864c0414
2618 changed files with 406015 additions and 31377 deletions

View File

@@ -16,11 +16,14 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptor\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\ProxyGenerator\Util\Properties;
use ProxyManager\ProxyGenerator\Util\UnsetPropertiesGenerator;
use ReflectionClass;
use ReflectionProperty;
/**
* Magic `__wakeup` for lazy loading value holder objects
@@ -32,19 +35,16 @@ class MagicWakeup extends MagicMethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
*/
public function __construct(ReflectionClass $originalClass)
{
parent::__construct($originalClass, '__wakeup');
/* @var $publicProperties \ReflectionProperty[] */
$publicProperties = $originalClass->getProperties(ReflectionProperty::IS_PUBLIC);
$unsetProperties = array();
foreach ($publicProperties as $publicProperty) {
$unsetProperties[] = '$this->' . $publicProperty->getName();
}
$this->setBody($unsetProperties ? 'unset(' . implode(', ', $unsetProperties) . ");" : '');
$this->setBody(UnsetPropertiesGenerator::generateSnippet(
Properties::fromReflectionClass($originalClass),
'this'
));
}
}

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptor\MethodGenerator;
use Closure;
use ProxyManager\Generator\MethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use Zend\Code\Generator\ParameterGenerator;
use Zend\Code\Generator\PropertyGenerator;
/**
@@ -33,6 +36,10 @@ class SetMethodPrefixInterceptor extends MethodGenerator
{
/**
* Constructor
*
* @param PropertyGenerator $prefixInterceptor
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(PropertyGenerator $prefixInterceptor)
{
@@ -40,11 +47,11 @@ class SetMethodPrefixInterceptor extends MethodGenerator
$interceptor = new ParameterGenerator('prefixInterceptor');
$interceptor->setType('Closure');
$interceptor->setType(Closure::class);
$interceptor->setDefaultValue(null);
$this->setParameter(new ParameterGenerator('methodName'));
$this->setParameter(new ParameterGenerator('methodName', 'string'));
$this->setParameter($interceptor);
$this->setDocblock('{@inheritDoc}');
$this->setDocBlock('{@inheritDoc}');
$this->setBody('$this->' . $prefixInterceptor->getName() . '[$methodName] = $prefixInterceptor;');
}
}

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptor\MethodGenerator;
use Closure;
use ProxyManager\Generator\MethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use Zend\Code\Generator\ParameterGenerator;
use Zend\Code\Generator\PropertyGenerator;
/**
@@ -33,6 +36,10 @@ class SetMethodSuffixInterceptor extends MethodGenerator
{
/**
* Constructor
*
* @param PropertyGenerator $suffixInterceptor
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(PropertyGenerator $suffixInterceptor)
{
@@ -40,11 +47,11 @@ class SetMethodSuffixInterceptor extends MethodGenerator
$interceptor = new ParameterGenerator('suffixInterceptor');
$interceptor->setType('Closure');
$interceptor->setType(Closure::class);
$interceptor->setDefaultValue(null);
$this->setParameter(new ParameterGenerator('methodName'));
$this->setParameter(new ParameterGenerator('methodName', 'string'));
$this->setParameter($interceptor);
$this->setDocblock('{@inheritDoc}');
$this->setDocBlock('{@inheritDoc}');
$this->setBody('$this->' . $suffixInterceptor->getName() . '[$methodName] = $suffixInterceptor;');
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptor\PropertyGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
@@ -31,13 +33,15 @@ class MethodPrefixInterceptors extends PropertyGenerator
{
/**
* Constructor
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct()
{
parent::__construct(UniqueIdentifierGenerator::getIdentifier('methodPrefixInterceptors'));
$this->setDefaultValue(array());
$this->setDefaultValue([]);
$this->setVisibility(self::VISIBILITY_PRIVATE);
$this->setDocblock('@var \\Closure[] map of interceptors to be called per-method before execution');
$this->setDocBlock('@var \\Closure[] map of interceptors to be called per-method before execution');
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptor\PropertyGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
@@ -31,13 +33,15 @@ class MethodSuffixInterceptors extends PropertyGenerator
{
/**
* Constructor
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct()
{
parent::__construct(UniqueIdentifierGenerator::getIdentifier('methodSuffixInterceptors'));
$this->setDefaultValue(array());
$this->setDefaultValue([]);
$this->setVisibility(self::VISIBILITY_PRIVATE);
$this->setDocblock('@var \\Closure[] map of interceptors to be called per-method after execution');
$this->setDocBlock('@var \\Closure[] map of interceptors to be called per-method after execution');
}
}

View File

@@ -0,0 +1,89 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\Util\Properties;
use ReflectionClass;
use Zend\Code\Generator\PropertyGenerator;
/**
* The `bindProxyProperties` method implementation for access interceptor scope localizers
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*/
class BindProxyProperties extends MethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
* @param PropertyGenerator $prefixInterceptors
* @param PropertyGenerator $suffixInterceptors
*/
public function __construct(
ReflectionClass $originalClass,
PropertyGenerator $prefixInterceptors,
PropertyGenerator $suffixInterceptors
) {
parent::__construct(
'bindProxyProperties',
[
new ParameterGenerator('localizedObject', $originalClass->getName()),
new ParameterGenerator('prefixInterceptors', 'array', []),
new ParameterGenerator('suffixInterceptors', 'array', []),
],
static::FLAG_PRIVATE,
null,
"@override constructor to setup interceptors\n\n"
. "@param \\" . $originalClass->getName() . " \$localizedObject\n"
. "@param \\Closure[] \$prefixInterceptors method interceptors to be used before method logic\n"
. "@param \\Closure[] \$suffixInterceptors method interceptors to be used before method logic"
);
$localizedProperties = [];
$properties = Properties::fromReflectionClass($originalClass);
foreach ($properties->getAccessibleProperties() as $property) {
$propertyName = $property->getName();
$localizedProperties[] = '$this->' . $propertyName . ' = & $localizedObject->' . $propertyName . ';';
}
foreach ($properties->getPrivateProperties() as $property) {
$propertyName = $property->getName();
$localizedProperties[] = "\\Closure::bind(function () use (\$localizedObject) {\n "
. '$this->' . $propertyName . ' = & $localizedObject->' . $propertyName . ";\n"
. '}, $this, ' . var_export($property->getDeclaringClass()->getName(), true)
. ')->__invoke();';
}
$this->setBody(
($localizedProperties ? implode("\n\n", $localizedProperties) . "\n\n" : '')
. '$this->' . $prefixInterceptors->getName() . " = \$prefixInterceptors;\n"
. '$this->' . $suffixInterceptors->getName() . " = \$suffixInterceptors;"
);
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
@@ -37,30 +39,31 @@ class InterceptedMethod extends MethodGenerator
* @param \Zend\Code\Generator\PropertyGenerator $suffixInterceptors
*
* @return self
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public static function generateMethod(
MethodReflection $originalMethod,
PropertyGenerator $prefixInterceptors,
PropertyGenerator $suffixInterceptors
) {
) : self {
/* @var $method self */
$method = static::fromReflection($originalMethod);
$forwardedParams = array();
$forwardedParams = [];
foreach ($originalMethod->getParameters() as $parameter) {
$forwardedParams[] = '$' . $parameter->getName();
$forwardedParams[] = ($parameter->isVariadic() ? '...' : '') . '$' . $parameter->getName();
}
$method->setDocblock('{@inheritDoc}');
$method->setBody(
InterceptorGenerator::createInterceptedMethodBody(
'$returnValue = parent::'
. $originalMethod->getName() . '(' . implode(', ', $forwardedParams) . ');',
$method,
$prefixInterceptors,
$suffixInterceptors
)
);
$method->setDocBlock('{@inheritDoc}');
$method->setBody(InterceptorGenerator::createInterceptedMethodBody(
'$returnValue = parent::'
. $originalMethod->getName() . '(' . implode(', ', $forwardedParams) . ');',
$method,
$prefixInterceptors,
$suffixInterceptors,
$originalMethod
));
return $method;
}

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\Util\InterceptorGenerator;
use ProxyManager\ProxyGenerator\Util\GetMethodIfExists;
use ReflectionClass;
use Zend\Code\Generator\PropertyGenerator;
@@ -33,6 +36,10 @@ class MagicClone extends MagicMethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
* @param PropertyGenerator $prefixInterceptors
* @param PropertyGenerator $suffixInterceptors
*/
public function __construct(
ReflectionClass $originalClass,
@@ -41,13 +48,14 @@ class MagicClone extends MagicMethodGenerator
) {
parent::__construct($originalClass, '__clone');
$this->setBody(
InterceptorGenerator::createInterceptedMethodBody(
$originalClass->hasMethod('__clone') ? '$returnValue = parent::__clone();' : '$returnValue = null;',
$this,
$prefixInterceptors,
$suffixInterceptors
)
);
$parent = GetMethodIfExists::get($originalClass, '__clone');
$this->setBody(InterceptorGenerator::createInterceptedMethodBody(
$parent ? '$returnValue = parent::__clone();' : '$returnValue = null;',
$this,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\Util\GetMethodIfExists;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\Util\InterceptorGenerator;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
use ReflectionClass;
@@ -37,21 +40,24 @@ class MagicGet extends MagicMethodGenerator
* @param ReflectionClass $originalClass
* @param PropertyGenerator $prefixInterceptors
* @param PropertyGenerator $suffixInterceptors
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
PropertyGenerator $prefixInterceptors,
PropertyGenerator $suffixInterceptors
) {
parent::__construct($originalClass, '__get', array(new ParameterGenerator('name')));
parent::__construct($originalClass, '__get', [new ParameterGenerator('name')]);
$override = $originalClass->hasMethod('__get');
$parent = GetMethodIfExists::get($originalClass, '__get');
$this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name');
$this->setDocBlock(($parent ? "{@inheritDoc}\n" : '') . '@param string $name');
if ($override) {
$callParent = '$returnValue = & parent::__get($name);';
} else {
$callParent = '$returnValue = & parent::__get($name);';
if (! $parent) {
$callParent = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_GET,
'name',
@@ -61,13 +67,12 @@ class MagicGet extends MagicMethodGenerator
);
}
$this->setBody(
InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors
)
);
$this->setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\Util\GetMethodIfExists;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\Util\InterceptorGenerator;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
use ReflectionClass;
@@ -37,21 +40,24 @@ class MagicIsset extends MagicMethodGenerator
* @param ReflectionClass $originalClass
* @param PropertyGenerator $prefixInterceptors
* @param PropertyGenerator $suffixInterceptors
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
PropertyGenerator $prefixInterceptors,
PropertyGenerator $suffixInterceptors
) {
parent::__construct($originalClass, '__isset', array(new ParameterGenerator('name')));
parent::__construct($originalClass, '__isset', [new ParameterGenerator('name')]);
$override = $originalClass->hasMethod('__isset');
$parent = GetMethodIfExists::get($originalClass, '__isset');
$this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name');
$this->setDocBlock(($parent ? "{@inheritDoc}\n" : '') . '@param string $name');
if ($override) {
$callParent = '$returnValue = & parent::__isset($name);';
} else {
$callParent = '$returnValue = & parent::__isset($name);';
if (! $parent) {
$callParent = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_ISSET,
'name',
@@ -61,13 +67,12 @@ class MagicIsset extends MagicMethodGenerator
);
}
$this->setBody(
InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors
)
);
$this->setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\Util\GetMethodIfExists;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\Util\InterceptorGenerator;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
use ReflectionClass;
@@ -37,6 +40,9 @@ class MagicSet extends MagicMethodGenerator
* @param \ReflectionClass $originalClass
* @param \Zend\Code\Generator\PropertyGenerator $prefixInterceptors
* @param \Zend\Code\Generator\PropertyGenerator $suffixInterceptors
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
@@ -46,16 +52,16 @@ class MagicSet extends MagicMethodGenerator
parent::__construct(
$originalClass,
'__set',
array(new ParameterGenerator('name'), new ParameterGenerator('value'))
[new ParameterGenerator('name'), new ParameterGenerator('value')]
);
$override = $originalClass->hasMethod('__set');
$parent = GetMethodIfExists::get($originalClass, '__set');
$this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name');
$this->setDocBlock(($parent ? "{@inheritDoc}\n" : '') . '@param string $name');
if ($override) {
$callParent = '$returnValue = & parent::__set($name, $value);';
} else {
$callParent = '$returnValue = & parent::__set($name, $value);';
if (! $parent) {
$callParent = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_SET,
'name',
@@ -65,13 +71,12 @@ class MagicSet extends MagicMethodGenerator
);
}
$this->setBody(
InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors
)
);
$this->setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\Util\InterceptorGenerator;
use ProxyManager\ProxyGenerator\Util\GetMethodIfExists;
use ReflectionClass;
use Zend\Code\Generator\PropertyGenerator;
@@ -33,6 +36,10 @@ class MagicSleep extends MagicMethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
* @param PropertyGenerator $prefixInterceptors
* @param PropertyGenerator $suffixInterceptors
*/
public function __construct(
ReflectionClass $originalClass,
@@ -41,17 +48,16 @@ class MagicSleep extends MagicMethodGenerator
) {
parent::__construct($originalClass, '__sleep');
$callParent = $originalClass->hasMethod('__sleep')
? '$returnValue = & parent::__sleep();'
: '$returnValue = array_keys((array) $this);';
$parent = GetMethodIfExists::get($originalClass, '__sleep');
$this->setBody(
InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors
)
);
$callParent = $parent ? '$returnValue = & parent::__sleep();' : '$returnValue = array_keys((array) $this);';
$this->setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\Util\GetMethodIfExists;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\Util\InterceptorGenerator;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
use ReflectionClass;
@@ -37,21 +40,24 @@ class MagicUnset extends MagicMethodGenerator
* @param ReflectionClass $originalClass
* @param PropertyGenerator $prefixInterceptors
* @param PropertyGenerator $suffixInterceptors
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
PropertyGenerator $prefixInterceptors,
PropertyGenerator $suffixInterceptors
) {
parent::__construct($originalClass, '__unset', array(new ParameterGenerator('name')));
parent::__construct($originalClass, '__unset', [new ParameterGenerator('name')]);
$override = $originalClass->hasMethod('__unset');
$parent = GetMethodIfExists::get($originalClass, '__unset');
$this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name');
$this->setDocBlock(($parent ? "{@inheritDoc}\n" : '') . '@param string $name');
if ($override) {
$callParent = '$returnValue = & parent::__unset($name);';
} else {
$callParent = '$returnValue = & parent::__unset($name);';
if (! $parent) {
$callParent = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_UNSET,
'name',
@@ -61,13 +67,12 @@ class MagicUnset extends MagicMethodGenerator
);
}
$this->setBody(
InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors
)
);
$this->setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}

View File

@@ -0,0 +1,76 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
use Zend\Code\Generator\ParameterGenerator;
use ReflectionClass;
/**
* The `staticProxyConstructor` implementation for an access interceptor scope localizer proxy
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*/
class StaticProxyConstructor extends MethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(ReflectionClass $originalClass)
{
parent::__construct('staticProxyConstructor', [], static::FLAG_PUBLIC | static::FLAG_STATIC);
$localizedObject = new ParameterGenerator('localizedObject');
$prefix = new ParameterGenerator('prefixInterceptors');
$suffix = new ParameterGenerator('suffixInterceptors');
$localizedObject->setType($originalClass->getName());
$prefix->setDefaultValue([]);
$suffix->setDefaultValue([]);
$prefix->setType('array');
$suffix->setType('array');
$this->setParameter($localizedObject);
$this->setParameter($prefix);
$this->setParameter($suffix);
$this->setReturnType($originalClass->getName());
$this->setDocBlock(
"Constructor to setup interceptors\n\n"
. "@param \\" . $originalClass->getName() . " \$localizedObject\n"
. "@param \\Closure[] \$prefixInterceptors method interceptors to be used before method logic\n"
. "@param \\Closure[] \$suffixInterceptors method interceptors to be used before method logic\n\n"
. '@return self'
);
$this->setBody(
'static $reflection;' . "\n\n"
. '$reflection = $reflection ?: $reflection = new \ReflectionClass(__CLASS__);' . "\n"
. '$instance = $reflection->newInstanceWithoutConstructor();' . "\n\n"
. '$instance->bindProxyProperties($localizedObject, $prefixInterceptors, $suffixInterceptors);' . "\n\n"
. 'return $instance;'
);
}
}

View File

@@ -16,9 +16,12 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\Util;
use ProxyManager\Generator\MethodGenerator;
use ProxyManager\Generator\Util\ProxiedMethodReturnExpression;
use Zend\Code\Generator\PropertyGenerator;
/**
@@ -27,8 +30,7 @@ use Zend\Code\Generator\PropertyGenerator;
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*
* @internal - this class is just here as a small utility for this component,
* don't use it in your own code
* @private - this class is just here as a small utility for this component, don't use it in your own code
*/
class InterceptorGenerator
{
@@ -39,19 +41,21 @@ class InterceptorGenerator
* @param \ProxyManager\Generator\MethodGenerator $method
* @param \Zend\Code\Generator\PropertyGenerator $prefixInterceptors
* @param \Zend\Code\Generator\PropertyGenerator $suffixInterceptors
* @param \ReflectionMethod|null $originalMethod
*
* @return string
*/
public static function createInterceptedMethodBody(
$methodBody,
string $methodBody,
MethodGenerator $method,
PropertyGenerator $prefixInterceptors,
PropertyGenerator $suffixInterceptors
) {
$name = var_export($method->getName(), true);
$prefixInterceptors = $prefixInterceptors->getName();
$suffixInterceptors = $suffixInterceptors->getName();
$params = array();
PropertyGenerator $suffixInterceptors,
?\ReflectionMethod $originalMethod
) : string {
$name = var_export($method->getName(), true);
$prefixInterceptorsName = $prefixInterceptors->getName();
$suffixInterceptorsName = $suffixInterceptors->getName();
$params = [];
foreach ($method->getParameters() as $parameter) {
$parameterName = $parameter->getName();
@@ -60,23 +64,23 @@ class InterceptorGenerator
$paramsString = 'array(' . implode(', ', $params) . ')';
return "if (isset(\$this->$prefixInterceptors" . "[$name])) {\n"
return "if (isset(\$this->$prefixInterceptorsName" . "[$name])) {\n"
. " \$returnEarly = false;\n"
. " \$prefixReturnValue = \$this->$prefixInterceptors" . "[$name]->__invoke("
. " \$prefixReturnValue = \$this->$prefixInterceptorsName" . "[$name]->__invoke("
. "\$this, \$this, $name, $paramsString, \$returnEarly);\n\n"
. " if (\$returnEarly) {\n"
. " return \$prefixReturnValue;\n"
. ' ' . ProxiedMethodReturnExpression::generate('$prefixReturnValue', $originalMethod) . "\n"
. " }\n"
. "}\n\n"
. $methodBody . "\n\n"
. "if (isset(\$this->$suffixInterceptors" . "[$name])) {\n"
. "if (isset(\$this->$suffixInterceptorsName" . "[$name])) {\n"
. " \$returnEarly = false;\n"
. " \$suffixReturnValue = \$this->$suffixInterceptors" . "[$name]->__invoke("
. " \$suffixReturnValue = \$this->$suffixInterceptorsName" . "[$name]->__invoke("
. "\$this, \$this, $name, $paramsString, \$returnValue, \$returnEarly);\n\n"
. " if (\$returnEarly) {\n"
. " return \$suffixReturnValue;\n"
. ' ' . ProxiedMethodReturnExpression::generate('$suffixReturnValue', $originalMethod) . "\n"
. " }\n"
. "}\n\n"
. "return \$returnValue;";
. ProxiedMethodReturnExpression::generate('$returnValue', $originalMethod);
}
}

View File

@@ -16,13 +16,18 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator;
use ProxyManager\Exception\InvalidProxiedClassException;
use ProxyManager\Generator\Util\ClassGeneratorUtils;
use ProxyManager\Proxy\AccessInterceptorInterface;
use ProxyManager\ProxyGenerator\AccessInterceptor\MethodGenerator\SetMethodPrefixInterceptor;
use ProxyManager\ProxyGenerator\AccessInterceptor\MethodGenerator\SetMethodSuffixInterceptor;
use ProxyManager\ProxyGenerator\AccessInterceptor\PropertyGenerator\MethodPrefixInterceptors;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\Constructor;
use ProxyManager\ProxyGenerator\AccessInterceptor\PropertyGenerator\MethodSuffixInterceptors;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\BindProxyProperties;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\InterceptedMethod;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\MagicClone;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\MagicGet;
@@ -30,6 +35,7 @@ use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\MagicSet;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\MagicSleep;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\MagicUnset;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizer\MethodGenerator\StaticProxyConstructor;
use ProxyManager\ProxyGenerator\Assertion\CanProxyAssertion;
use ProxyManager\ProxyGenerator\Util\ProxiedMethodsFilter;
use ReflectionClass;
@@ -51,15 +57,19 @@ class AccessInterceptorScopeLocalizerGenerator implements ProxyGeneratorInterfac
{
/**
* {@inheritDoc}
*
* @throws \InvalidArgumentException
* @throws InvalidProxiedClassException
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function generate(ReflectionClass $originalClass, ClassGenerator $classGenerator)
{
CanProxyAssertion::assertClassCanBeProxied($originalClass, false);
$classGenerator->setExtendedClass($originalClass->getName());
$classGenerator->setImplementedInterfaces(array('ProxyManager\\Proxy\\AccessInterceptorInterface'));
$classGenerator->setImplementedInterfaces([AccessInterceptorInterface::class]);
$classGenerator->addPropertyFromGenerator($prefixInterceptors = new MethodPrefixInterceptors());
$classGenerator->addPropertyFromGenerator($suffixInterceptors = new MethodPrefixInterceptors());
$classGenerator->addPropertyFromGenerator($suffixInterceptors = new MethodSuffixInterceptors());
array_map(
function (MethodGenerator $generatedMethod) use ($originalClass, $classGenerator) {
@@ -67,20 +77,15 @@ class AccessInterceptorScopeLocalizerGenerator implements ProxyGeneratorInterfac
},
array_merge(
array_map(
function (ReflectionMethod $method) use ($prefixInterceptors, $suffixInterceptors) {
return InterceptedMethod::generateMethod(
new MethodReflection($method->getDeclaringClass()->getName(), $method->getName()),
$prefixInterceptors,
$suffixInterceptors
);
},
$this->buildMethodInterceptor($prefixInterceptors, $suffixInterceptors),
ProxiedMethodsFilter::getProxiedMethods(
$originalClass,
array('__get', '__set', '__isset', '__unset', '__clone', '__sleep')
['__get', '__set', '__isset', '__unset', '__clone', '__sleep']
)
),
array(
new Constructor($originalClass, $prefixInterceptors, $suffixInterceptors),
[
new StaticProxyConstructor($originalClass),
new BindProxyProperties($originalClass, $prefixInterceptors, $suffixInterceptors),
new SetMethodPrefixInterceptor($prefixInterceptors),
new SetMethodSuffixInterceptor($suffixInterceptors),
new MagicGet($originalClass, $prefixInterceptors, $suffixInterceptors),
@@ -89,8 +94,21 @@ class AccessInterceptorScopeLocalizerGenerator implements ProxyGeneratorInterfac
new MagicUnset($originalClass, $prefixInterceptors, $suffixInterceptors),
new MagicSleep($originalClass, $prefixInterceptors, $suffixInterceptors),
new MagicClone($originalClass, $prefixInterceptors, $suffixInterceptors),
)
]
)
);
}
private function buildMethodInterceptor(
MethodPrefixInterceptors $prefixInterceptors,
MethodSuffixInterceptors $suffixInterceptors
) : callable {
return function (ReflectionMethod $method) use ($prefixInterceptors, $suffixInterceptors) : InterceptedMethod {
return InterceptedMethod::generateMethod(
new MethodReflection($method->getDeclaringClass()->getName(), $method->getName()),
$prefixInterceptors,
$suffixInterceptors
);
};
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
@@ -38,32 +40,33 @@ class InterceptedMethod extends MethodGenerator
* @param \Zend\Code\Generator\PropertyGenerator $suffixInterceptors
*
* @return self
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public static function generateMethod(
MethodReflection $originalMethod,
PropertyGenerator $valueHolderProperty,
PropertyGenerator $prefixInterceptors,
PropertyGenerator $suffixInterceptors
) {
) : self {
/* @var $method self */
$method = static::fromReflection($originalMethod);
$forwardedParams = array();
$forwardedParams = [];
foreach ($originalMethod->getParameters() as $parameter) {
$forwardedParams[] = '$' . $parameter->getName();
$forwardedParams[] = ($parameter->isVariadic() ? '...' : '') . '$' . $parameter->getName();
}
$method->setDocblock('{@inheritDoc}');
$method->setBody(
InterceptorGenerator::createInterceptedMethodBody(
'$returnValue = $this->' . $valueHolderProperty->getName() . '->'
. $originalMethod->getName() . '(' . implode(', ', $forwardedParams) . ');',
$method,
$valueHolderProperty,
$prefixInterceptors,
$suffixInterceptors
)
);
$method->setDocBlock('{@inheritDoc}');
$method->setBody(InterceptorGenerator::createInterceptedMethodBody(
'$returnValue = $this->' . $valueHolderProperty->getName() . '->'
. $originalMethod->getName() . '(' . implode(', ', $forwardedParams) . ');',
$method,
$valueHolderProperty,
$prefixInterceptors,
$suffixInterceptors,
$originalMethod
));
return $method;
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
@@ -32,6 +34,11 @@ class MagicClone extends MagicMethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
* @param PropertyGenerator $valueHolderProperty
* @param PropertyGenerator $prefixInterceptors
* @param PropertyGenerator $suffixInterceptors
*/
public function __construct(
ReflectionClass $originalClass,
@@ -52,7 +59,7 @@ class MagicClone extends MagicMethodGenerator
. "}\n\n"
. "foreach (\$this->$suffix as \$key => \$value) {\n"
. " \$this->$suffix" . "[\$key] = clone \$value;\n"
. "}"
. '}'
);
}
}

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\Util\GetMethodIfExists;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\Util\InterceptorGenerator;
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
@@ -36,6 +39,15 @@ class MagicGet extends MagicMethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
* @param PropertyGenerator $valueHolder
* @param PropertyGenerator $prefixInterceptors
* @param PropertyGenerator $suffixInterceptors
* @param PublicPropertiesMap $publicProperties
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
@@ -44,12 +56,12 @@ class MagicGet extends MagicMethodGenerator
PropertyGenerator $suffixInterceptors,
PublicPropertiesMap $publicProperties
) {
parent::__construct($originalClass, '__get', array(new ParameterGenerator('name')));
parent::__construct($originalClass, '__get', [new ParameterGenerator('name')]);
$override = $originalClass->hasMethod('__get');
$parent = GetMethodIfExists::get($originalClass, '__get');
$valueHolderName = $valueHolder->getName();
$this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name');
$this->setDocBlock(($parent ? "{@inheritDoc}\n" : '') . '@param string $name');
$callParent = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_GET,
@@ -65,14 +77,13 @@ class MagicGet extends MagicMethodGenerator
. "\n} else {\n $callParent\n}\n\n";
}
$this->setBody(
InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$valueHolder,
$prefixInterceptors,
$suffixInterceptors
)
);
$this->setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$valueHolder,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\Util\GetMethodIfExists;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\Util\InterceptorGenerator;
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
@@ -36,6 +39,14 @@ class MagicIsset extends MagicMethodGenerator
{
/**
* Constructor
* @param ReflectionClass $originalClass
* @param PropertyGenerator $valueHolder
* @param PropertyGenerator $prefixInterceptors
* @param PropertyGenerator $suffixInterceptors
* @param PublicPropertiesMap $publicProperties
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
@@ -44,12 +55,12 @@ class MagicIsset extends MagicMethodGenerator
PropertyGenerator $suffixInterceptors,
PublicPropertiesMap $publicProperties
) {
parent::__construct($originalClass, '__isset', array(new ParameterGenerator('name')));
parent::__construct($originalClass, '__isset', [new ParameterGenerator('name')]);
$override = $originalClass->hasMethod('__isset');
$parent = GetMethodIfExists::get($originalClass, '__isset');
$valueHolderName = $valueHolder->getName();
$this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name');
$this->setDocBlock(($parent ? "{@inheritDoc}\n" : '') . '@param string $name');
$callParent = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_ISSET,
@@ -65,14 +76,13 @@ class MagicIsset extends MagicMethodGenerator
. "\n} else {\n $callParent\n}\n\n";
}
$this->setBody(
InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$valueHolder,
$prefixInterceptors,
$suffixInterceptors
)
);
$this->setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$valueHolder,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\Util\GetMethodIfExists;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\Util\InterceptorGenerator;
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
@@ -36,6 +39,14 @@ class MagicSet extends MagicMethodGenerator
{
/**
* Constructor
* @param ReflectionClass $originalClass
* @param PropertyGenerator $valueHolder
* @param PropertyGenerator $prefixInterceptors
* @param PropertyGenerator $suffixInterceptors
* @param PublicPropertiesMap $publicProperties
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
@@ -47,13 +58,13 @@ class MagicSet extends MagicMethodGenerator
parent::__construct(
$originalClass,
'__set',
array(new ParameterGenerator('name'), new ParameterGenerator('value'))
[new ParameterGenerator('name'), new ParameterGenerator('value')]
);
$override = $originalClass->hasMethod('__set');
$parent = GetMethodIfExists::get($originalClass, '__set');
$valueHolderName = $valueHolder->getName();
$this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name');
$this->setDocBlock(($parent ? "{@inheritDoc}\n" : '') . '@param string $name');
$callParent = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_SET,
@@ -69,14 +80,13 @@ class MagicSet extends MagicMethodGenerator
. "\n} else {\n $callParent\n}\n\n";
}
$this->setBody(
InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$valueHolder,
$prefixInterceptors,
$suffixInterceptors
)
);
$this->setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$valueHolder,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\Util\GetMethodIfExists;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\Util\InterceptorGenerator;
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
@@ -36,6 +39,14 @@ class MagicUnset extends MagicMethodGenerator
{
/**
* Constructor
* @param ReflectionClass $originalClass
* @param PropertyGenerator $valueHolder
* @param PropertyGenerator $prefixInterceptors
* @param PropertyGenerator $suffixInterceptors
* @param PublicPropertiesMap $publicProperties
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
@@ -44,12 +55,12 @@ class MagicUnset extends MagicMethodGenerator
PropertyGenerator $suffixInterceptors,
PublicPropertiesMap $publicProperties
) {
parent::__construct($originalClass, '__unset', array(new ParameterGenerator('name')));
parent::__construct($originalClass, '__unset', [new ParameterGenerator('name')]);
$override = $originalClass->hasMethod('__unset');
$parent = GetMethodIfExists::get($originalClass, '__unset');
$valueHolderName = $valueHolder->getName();
$this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name');
$this->setDocBlock(($parent ? "{@inheritDoc}\n" : '') . '@param string $name');
$callParent = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_UNSET,
@@ -67,14 +78,13 @@ class MagicUnset extends MagicMethodGenerator
$callParent .= '$returnValue = false;';
$this->setBody(
InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$valueHolder,
$prefixInterceptors,
$suffixInterceptors
)
);
$this->setBody(InterceptorGenerator::createInterceptedMethodBody(
$callParent,
$this,
$valueHolder,
$prefixInterceptors,
$suffixInterceptors,
$parent
));
}
}

View File

@@ -0,0 +1,88 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
use ProxyManager\ProxyGenerator\Util\Properties;
use ProxyManager\ProxyGenerator\Util\UnsetPropertiesGenerator;
use ReflectionClass;
use Zend\Code\Generator\ParameterGenerator;
use Zend\Code\Generator\PropertyGenerator;
/**
* The `staticProxyConstructor` implementation for access interceptor value holders
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*/
class StaticProxyConstructor extends MethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
* @param PropertyGenerator $valueHolder
* @param PropertyGenerator $prefixInterceptors
* @param PropertyGenerator $suffixInterceptors
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
PropertyGenerator $valueHolder,
PropertyGenerator $prefixInterceptors,
PropertyGenerator $suffixInterceptors
) {
parent::__construct('staticProxyConstructor', [], static::FLAG_PUBLIC | static::FLAG_STATIC);
$prefix = new ParameterGenerator('prefixInterceptors');
$suffix = new ParameterGenerator('suffixInterceptors');
$prefix->setDefaultValue([]);
$suffix->setDefaultValue([]);
$prefix->setType('array');
$suffix->setType('array');
$this->setParameter(new ParameterGenerator('wrappedObject'));
$this->setParameter($prefix);
$this->setParameter($suffix);
$this->setReturnType($originalClass->getName());
$this->setDocBlock(
"Constructor to setup interceptors\n\n"
. "@param \\" . $originalClass->getName() . " \$wrappedObject\n"
. "@param \\Closure[] \$prefixInterceptors method interceptors to be used before method logic\n"
. "@param \\Closure[] \$suffixInterceptors method interceptors to be used before method logic\n\n"
. '@return self'
);
$this->setBody(
'static $reflection;' . "\n\n"
. '$reflection = $reflection ?: $reflection = new \ReflectionClass(__CLASS__);' . "\n"
. '$instance = (new \ReflectionClass(get_class()))->newInstanceWithoutConstructor();' . "\n\n"
. UnsetPropertiesGenerator::generateSnippet(Properties::fromReflectionClass($originalClass), 'instance')
. '$instance->' . $valueHolder->getName() . " = \$wrappedObject;\n"
. '$instance->' . $prefixInterceptors->getName() . " = \$prefixInterceptors;\n"
. '$instance->' . $suffixInterceptors->getName() . " = \$suffixInterceptors;\n\n"
. 'return $instance;'
);
}
}

View File

@@ -16,9 +16,12 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\Util;
use ProxyManager\Generator\MethodGenerator;
use ProxyManager\Generator\Util\ProxiedMethodReturnExpression;
use Zend\Code\Generator\PropertyGenerator;
/**
@@ -27,8 +30,7 @@ use Zend\Code\Generator\PropertyGenerator;
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*
* @internal - this class is just here as a small utility for this component,
* don't use it in your own code
* @private - this class is just here as a small utility for this component, don't use it in your own code
*/
class InterceptorGenerator
{
@@ -40,21 +42,23 @@ class InterceptorGenerator
* @param \Zend\Code\Generator\PropertyGenerator $valueHolder
* @param \Zend\Code\Generator\PropertyGenerator $prefixInterceptors
* @param \Zend\Code\Generator\PropertyGenerator $suffixInterceptors
* @param \ReflectionMethod|null $originalMethod
*
* @return string
*/
public static function createInterceptedMethodBody(
$methodBody,
string $methodBody,
MethodGenerator $method,
PropertyGenerator $valueHolder,
PropertyGenerator $prefixInterceptors,
PropertyGenerator $suffixInterceptors
) {
$name = var_export($method->getName(), true);
$valueHolder = $valueHolder->getName();
$prefixInterceptors = $prefixInterceptors->getName();
$suffixInterceptors = $suffixInterceptors->getName();
$params = array();
PropertyGenerator $suffixInterceptors,
?\ReflectionMethod $originalMethod
) : string {
$name = var_export($method->getName(), true);
$valueHolderName = $valueHolder->getName();
$prefixInterceptorsName = $prefixInterceptors->getName();
$suffixInterceptorsName = $suffixInterceptors->getName();
$params = [];
foreach ($method->getParameters() as $parameter) {
$parameterName = $parameter->getName();
@@ -63,23 +67,23 @@ class InterceptorGenerator
$paramsString = 'array(' . implode(', ', $params) . ')';
return "if (isset(\$this->$prefixInterceptors" . "[$name])) {\n"
return "if (isset(\$this->$prefixInterceptorsName" . "[$name])) {\n"
. " \$returnEarly = false;\n"
. " \$prefixReturnValue = \$this->$prefixInterceptors" . "[$name]->__invoke("
. "\$this, \$this->$valueHolder, $name, $paramsString, \$returnEarly);\n\n"
. " \$prefixReturnValue = \$this->$prefixInterceptorsName" . "[$name]->__invoke("
. "\$this, \$this->$valueHolderName, $name, $paramsString, \$returnEarly);\n\n"
. " if (\$returnEarly) {\n"
. " return \$prefixReturnValue;\n"
. ' ' . ProxiedMethodReturnExpression::generate('$prefixReturnValue', $originalMethod) . "\n"
. " }\n"
. "}\n\n"
. $methodBody . "\n\n"
. "if (isset(\$this->$suffixInterceptors" . "[$name])) {\n"
. "if (isset(\$this->$suffixInterceptorsName" . "[$name])) {\n"
. " \$returnEarly = false;\n"
. " \$suffixReturnValue = \$this->$suffixInterceptors" . "[$name]->__invoke("
. "\$this, \$this->$valueHolder, $name, $paramsString, \$returnValue, \$returnEarly);\n\n"
. " \$suffixReturnValue = \$this->$suffixInterceptorsName" . "[$name]->__invoke("
. "\$this, \$this->$valueHolderName, $name, $paramsString, \$returnValue, \$returnEarly);\n\n"
. " if (\$returnEarly) {\n"
. " return \$suffixReturnValue;\n"
. ' ' . ProxiedMethodReturnExpression::generate('$suffixReturnValue', $originalMethod) . "\n"
. " }\n"
. "}\n\n"
. "return \$returnValue;";
. ProxiedMethodReturnExpression::generate('$returnValue', $originalMethod);
}
}

View File

@@ -16,25 +16,31 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator;
use ProxyManager\Exception\InvalidProxiedClassException;
use ProxyManager\Generator\Util\ClassGeneratorUtils;
use ProxyManager\Proxy\AccessInterceptorValueHolderInterface;
use ProxyManager\ProxyGenerator\AccessInterceptor\MethodGenerator\MagicWakeup;
use ProxyManager\ProxyGenerator\AccessInterceptor\MethodGenerator\SetMethodPrefixInterceptor;
use ProxyManager\ProxyGenerator\AccessInterceptor\MethodGenerator\SetMethodSuffixInterceptor;
use ProxyManager\ProxyGenerator\AccessInterceptor\PropertyGenerator\MethodPrefixInterceptors;
use ProxyManager\ProxyGenerator\AccessInterceptor\PropertyGenerator\MethodSuffixInterceptors;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\Constructor;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\InterceptedMethod;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\MagicClone;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\MagicGet;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\MagicIsset;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\MagicSet;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\MagicUnset;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolder\MethodGenerator\StaticProxyConstructor;
use ProxyManager\ProxyGenerator\Assertion\CanProxyAssertion;
use ProxyManager\ProxyGenerator\LazyLoadingValueHolder\PropertyGenerator\ValueHolderProperty;
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
use ProxyManager\ProxyGenerator\Util\Properties;
use ProxyManager\ProxyGenerator\Util\ProxiedMethodsFilter;
use ProxyManager\ProxyGenerator\ValueHolder\MethodGenerator\Constructor;
use ProxyManager\ProxyGenerator\ValueHolder\MethodGenerator\GetWrappedValueHolderValue;
use ProxyManager\ProxyGenerator\ValueHolder\MethodGenerator\MagicSleep;
use ReflectionClass;
@@ -56,16 +62,17 @@ class AccessInterceptorValueHolderGenerator implements ProxyGeneratorInterface
{
/**
* {@inheritDoc}
*
* @throws \InvalidArgumentException
* @throws InvalidProxiedClassException
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function generate(ReflectionClass $originalClass, ClassGenerator $classGenerator)
{
CanProxyAssertion::assertClassCanBeProxied($originalClass);
$publicProperties = new PublicPropertiesMap($originalClass);
$interfaces = array(
'ProxyManager\\Proxy\\AccessInterceptorInterface',
'ProxyManager\\Proxy\\ValueHolderInterface',
);
$publicProperties = new PublicPropertiesMap(Properties::fromReflectionClass($originalClass));
$interfaces = [AccessInterceptorValueHolderInterface::class];
if ($originalClass->isInterface()) {
$interfaces[] = $originalClass->getName();
@@ -85,18 +92,12 @@ class AccessInterceptorValueHolderGenerator implements ProxyGeneratorInterface
},
array_merge(
array_map(
function (ReflectionMethod $method) use ($prefixInterceptors, $suffixInterceptors, $valueHolder) {
return InterceptedMethod::generateMethod(
new MethodReflection($method->getDeclaringClass()->getName(), $method->getName()),
$valueHolder,
$prefixInterceptors,
$suffixInterceptors
);
},
$this->buildMethodInterceptor($prefixInterceptors, $suffixInterceptors, $valueHolder),
ProxiedMethodsFilter::getProxiedMethods($originalClass)
),
array(
new Constructor($originalClass, $valueHolder, $prefixInterceptors, $suffixInterceptors),
[
Constructor::generateMethod($originalClass, $valueHolder),
new StaticProxyConstructor($originalClass, $valueHolder, $prefixInterceptors, $suffixInterceptors),
new GetWrappedValueHolderValue($valueHolder),
new SetMethodPrefixInterceptor($prefixInterceptors),
new SetMethodSuffixInterceptor($suffixInterceptors),
@@ -130,9 +131,24 @@ class AccessInterceptorValueHolderGenerator implements ProxyGeneratorInterface
),
new MagicClone($originalClass, $valueHolder, $prefixInterceptors, $suffixInterceptors),
new MagicSleep($originalClass, $valueHolder),
new MagicWakeup($originalClass, $valueHolder),
)
new MagicWakeup($originalClass),
]
)
);
}
private function buildMethodInterceptor(
MethodPrefixInterceptors $prefixes,
MethodSuffixInterceptors $suffixes,
ValueHolderProperty $valueHolder
) : callable {
return function (ReflectionMethod $method) use ($prefixes, $suffixes, $valueHolder) : InterceptedMethod {
return InterceptedMethod::generateMethod(
new MethodReflection($method->getDeclaringClass()->getName(), $method->getName()),
$valueHolder,
$prefixes,
$suffixes
);
};
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\Assertion;
use BadMethodCallException;
@@ -47,7 +49,7 @@ final class CanProxyAssertion
*
* @throws InvalidProxiedClassException
*/
public static function assertClassCanBeProxied(ReflectionClass $originalClass, $allowInterfaces = true)
public static function assertClassCanBeProxied(ReflectionClass $originalClass, bool $allowInterfaces = true) : void
{
self::isNotFinal($originalClass);
self::hasNoAbstractProtectedMethods($originalClass);
@@ -62,7 +64,7 @@ final class CanProxyAssertion
*
* @throws InvalidProxiedClassException
*/
private static function isNotFinal(ReflectionClass $originalClass)
private static function isNotFinal(ReflectionClass $originalClass) : void
{
if ($originalClass->isFinal()) {
throw InvalidProxiedClassException::finalClassNotSupported($originalClass);
@@ -74,11 +76,11 @@ final class CanProxyAssertion
*
* @throws InvalidProxiedClassException
*/
private static function hasNoAbstractProtectedMethods(ReflectionClass $originalClass)
private static function hasNoAbstractProtectedMethods(ReflectionClass $originalClass) : void
{
$protectedAbstract = array_filter(
$originalClass->getMethods(),
function (ReflectionMethod $method) {
function (ReflectionMethod $method) : bool {
return $method->isAbstract() && $method->isProtected();
}
);
@@ -93,7 +95,7 @@ final class CanProxyAssertion
*
* @throws InvalidProxiedClassException
*/
private static function isNotInterface(ReflectionClass $originalClass)
private static function isNotInterface(ReflectionClass $originalClass) : void
{
if ($originalClass->isInterface()) {
throw InvalidProxiedClassException::interfaceNotSupported($originalClass);

View File

@@ -0,0 +1,61 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoading\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
use ProxyManager\ProxyGenerator\Util\Properties;
use ProxyManager\ProxyGenerator\Util\UnsetPropertiesGenerator;
use Zend\Code\Generator\ParameterGenerator;
use Zend\Code\Generator\PropertyGenerator;
/**
* The `staticProxyConstructor` implementation for lazy loading proxies
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*/
class StaticProxyConstructor extends MethodGenerator
{
/**
* Static constructor
*
* @param PropertyGenerator $initializerProperty
* @param Properties $properties
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(PropertyGenerator $initializerProperty, Properties $properties)
{
parent::__construct('staticProxyConstructor', [], static::FLAG_PUBLIC | static::FLAG_STATIC);
$this->setParameter(new ParameterGenerator('initializer'));
$this->setDocBlock("Constructor for lazy initialization\n\n@param \\Closure|null \$initializer");
$this->setBody(
'static $reflection;' . "\n\n"
. '$reflection = $reflection ?: $reflection = new \ReflectionClass(__CLASS__);' . "\n"
. '$instance = (new \ReflectionClass(get_class()))->newInstanceWithoutConstructor();' . "\n\n"
. UnsetPropertiesGenerator::generateSnippet($properties, 'instance')
. '$instance->' . $initializerProperty->getName() . ' = $initializer;' . "\n\n"
. 'return $instance;'
);
}
}

View File

@@ -16,11 +16,15 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
use ProxyManager\ProxyGenerator\Util\Properties;
use ReflectionProperty;
use Zend\Code\Generator\PropertyGenerator;
/**
@@ -34,34 +38,160 @@ class CallInitializer extends MethodGenerator
{
/**
* Constructor
*
* @param PropertyGenerator $initializerProperty
* @param PropertyGenerator $initTracker
* @param Properties $properties
*/
public function __construct(
PropertyGenerator $initializerProperty,
PropertyGenerator $publicPropsDefaults,
PropertyGenerator $initTracker
PropertyGenerator $initTracker,
Properties $properties
) {
parent::__construct(UniqueIdentifierGenerator::getIdentifier('callInitializer'));
$this->setDocblock("Triggers initialization logic for this ghost object");
$docBlock = <<<'DOCBLOCK'
Triggers initialization logic for this ghost object
$this->setParameters(array(
new ParameterGenerator('methodName'),
new ParameterGenerator('parameters', 'array'),
));
@param string $methodName
@param mixed[] $parameters
$this->setVisibility(static::VISIBILITY_PRIVATE);
@return mixed
DOCBLOCK;
parent::__construct(
UniqueIdentifierGenerator::getIdentifier('callInitializer'),
[
new ParameterGenerator('methodName'),
new ParameterGenerator('parameters', 'array'),
],
static::FLAG_PRIVATE,
null,
$docBlock
);
$initializer = $initializerProperty->getName();
$initialization = $initTracker->getName();
$this->setBody(
'if ($this->' . $initialization . ' || ! $this->' . $initializer . ') {' . "\n return;\n}\n\n"
. "\$this->" . $initialization . " = true;\n\n"
. "foreach (self::\$" . $publicPropsDefaults->getName() . " as \$key => \$default) {\n"
. " \$this->\$key = \$default;\n"
. "}\n\n"
. '$this->' . $initializer . '->__invoke'
. '($this, $methodName, $parameters, $this->' . $initializer . ');' . "\n\n"
. "\$this->" . $initialization . " = false;"
$bodyTemplate = <<<'PHP'
if ($this->%s || ! $this->%s) {
return;
}
$this->%s = true;
%s
%s
$result = $this->%s->__invoke($this, $methodName, $parameters, $this->%s, $properties);
$this->%s = false;
return $result;
PHP;
$this->setBody(sprintf(
$bodyTemplate,
$initialization,
$initializer,
$initialization,
$this->propertiesInitializationCode($properties),
$this->propertiesReferenceArrayCode($properties),
$initializer,
$initializer,
$initialization
));
}
private function propertiesInitializationCode(Properties $properties) : string
{
$assignments = [];
foreach ($properties->getAccessibleProperties() as $property) {
$assignments[] = '$this->'
. $property->getName()
. ' = ' . $this->getExportedPropertyDefaultValue($property)
. ';';
}
foreach ($properties->getGroupedPrivateProperties() as $className => $privateProperties) {
$cacheKey = 'cache' . str_replace('\\', '_', $className);
$assignments[] = 'static $' . $cacheKey . ";\n\n"
. '$' . $cacheKey . ' ?: $' . $cacheKey . " = \\Closure::bind(function (\$instance) {\n"
. $this->getPropertyDefaultsAssignments($privateProperties) . "\n"
. '}, null, ' . var_export($className, true) . ");\n\n"
. '$' . $cacheKey . "(\$this);\n\n";
}
return implode("\n", $assignments) . "\n\n";
}
/**
* @param ReflectionProperty[] $properties
*
* @return string
*/
private function getPropertyDefaultsAssignments(array $properties) : string
{
return implode(
"\n",
array_map(
function (ReflectionProperty $property) : string {
return ' $instance->' . $property->getName()
. ' = ' . $this->getExportedPropertyDefaultValue($property) . ';';
},
$properties
)
);
}
private function propertiesReferenceArrayCode(Properties $properties) : string
{
$assignments = [];
foreach ($properties->getAccessibleProperties() as $propertyInternalName => $property) {
$assignments[] = ' '
. var_export($propertyInternalName, true) . ' => & $this->' . $property->getName()
. ',';
}
$code = "\$properties = [\n" . implode("\n", $assignments) . "\n];\n\n";
// must use assignments, as direct reference during array definition causes a fatal error (not sure why)
foreach ($properties->getGroupedPrivateProperties() as $className => $classPrivateProperties) {
$cacheKey = 'cacheFetch' . str_replace('\\', '_', $className);
$code .= 'static $' . $cacheKey . ";\n\n"
. '$' . $cacheKey . ' ?: $' . $cacheKey
. " = \\Closure::bind(function (\$instance, array & \$properties) {\n"
. $this->generatePrivatePropertiesAssignmentsCode($classPrivateProperties)
. "}, \$this, " . var_export($className, true) . ");\n\n"
. '$' . $cacheKey . "(\$this, \$properties);";
}
return $code;
}
/**
* @param ReflectionProperty[] $properties indexed by internal name
*
* @return string
*/
private function generatePrivatePropertiesAssignmentsCode(array $properties) : string
{
$code = '';
foreach ($properties as $property) {
$key = "\0" . $property->getDeclaringClass()->getName() . "\0" . $property->getName();
$code .= ' $properties[' . var_export($key, true) . '] = '
. '& $instance->' . $property->getName() . ";\n";
}
return $code;
}
private function getExportedPropertyDefaultValue(ReflectionProperty $property) : string
{
$name = $property->getName();
$defaults = $property->getDeclaringClass()->getDefaultProperties();
return var_export($defaults[$name] ?? null, true);
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
@@ -32,11 +34,15 @@ class GetProxyInitializer extends MethodGenerator
{
/**
* Constructor
*
* @param PropertyGenerator $initializerProperty
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(PropertyGenerator $initializerProperty)
{
parent::__construct('getProxyInitializer');
$this->setDocblock('{@inheritDoc}');
$this->setDocBlock('{@inheritDoc}');
$this->setBody('return $this->' . $initializerProperty->getName() . ';');
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
@@ -33,15 +35,21 @@ class InitializeProxy extends MethodGenerator
{
/**
* Constructor
*
* @param PropertyGenerator $initializerProperty
* @param ZendMethodGenerator $callInitializer
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(PropertyGenerator $initializerProperty, ZendMethodGenerator $callInitializer)
{
parent::__construct('initializeProxy');
$this->setDocblock('{@inheritDoc}');
$this->setDocBlock('{@inheritDoc}');
$this->setReturnType('bool');
$this->setBody(
'return $this->' . $initializerProperty->getName() . ' && $this->' . $callInitializer->getName()
. '(\'initializeProxy\', array());'
. '(\'initializeProxy\', []);'
);
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
@@ -32,11 +34,16 @@ class IsProxyInitialized extends MethodGenerator
{
/**
* Constructor
*
* @param PropertyGenerator $initializerProperty
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(PropertyGenerator $initializerProperty)
{
parent::__construct('isProxyInitialized');
$this->setDocblock('{@inheritDoc}');
$this->setDocBlock('{@inheritDoc}');
$this->setReturnType('bool');
$this->setBody('return ! $this->' . $initializerProperty->getName() . ';');
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
@@ -33,6 +35,10 @@ class MagicClone extends MagicMethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
* @param PropertyGenerator $initializerProperty
* @param MethodGenerator $callInitializer
*/
public function __construct(
ReflectionClass $originalClass,
@@ -43,7 +49,7 @@ class MagicClone extends MagicMethodGenerator
$this->setBody(
'$this->' . $initializerProperty->getName() . ' && $this->' . $callInitializer->getName()
. '(\'__clone\', array());'
. '(\'__clone\', []);'
. ($originalClass->hasMethod('__clone') ? "\n\nparent::__clone();" : '')
);
}

View File

@@ -16,10 +16,15 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\InitializationTracker;
use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PrivatePropertiesMap;
use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\ProtectedPropertiesMap;
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
use ReflectionClass;
@@ -35,43 +40,120 @@ use Zend\Code\Generator\PropertyGenerator;
class MagicGet extends MagicMethodGenerator
{
/**
* @param \ReflectionClass $originalClass
* @param \Zend\Code\Generator\PropertyGenerator $initializerProperty
* @param \Zend\Code\Generator\MethodGenerator $callInitializer
* @param \ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap $publicProperties
* @var string
*/
private $callParentTemplate = <<<'PHP'
$this->%s && ! $this->%s && $this->%s('__get', array('name' => $name));
if (isset(self::$%s[$name])) {
return $this->$name;
}
if (isset(self::$%s[$name])) {
if ($this->%s) {
return $this->$name;
}
// check protected property access via compatible class
$callers = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
$caller = isset($callers[1]) ? $callers[1] : [];
$object = isset($caller['object']) ? $caller['object'] : '';
$expectedType = self::$%s[$name];
if ($object instanceof $expectedType) {
return $this->$name;
}
$class = isset($caller['class']) ? $caller['class'] : '';
if ($class === $expectedType || is_subclass_of($class, $expectedType) || $class === 'ReflectionProperty') {
return $this->$name;
}
} elseif (isset(self::$%s[$name])) {
// check private property access via same class
$callers = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
$caller = isset($callers[1]) ? $callers[1] : [];
$class = isset($caller['class']) ? $caller['class'] : '';
static $accessorCache = [];
if (isset(self::$%s[$name][$class])) {
$cacheKey = $class . '#' . $name;
$accessor = isset($accessorCache[$cacheKey])
? $accessorCache[$cacheKey]
: $accessorCache[$cacheKey] = \Closure::bind(function & ($instance) use ($name) {
return $instance->$name;
}, null, $class);
return $accessor($this);
}
if ($this->%s || 'ReflectionProperty' === $class) {
$tmpClass = key(self::$%s[$name]);
$cacheKey = $tmpClass . '#' . $name;
$accessor = isset($accessorCache[$cacheKey])
? $accessorCache[$cacheKey]
: $accessorCache[$cacheKey] = \Closure::bind(function & ($instance) use ($name) {
return $instance->$name;
}, null, $tmpClass);
return $accessor($this);
}
}
%s
PHP;
/**
* @param ReflectionClass $originalClass
* @param PropertyGenerator $initializerProperty
* @param MethodGenerator $callInitializer
* @param PublicPropertiesMap $publicProperties
* @param ProtectedPropertiesMap $protectedProperties
* @param PrivatePropertiesMap $privateProperties
* @param InitializationTracker $initializationTracker
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
PropertyGenerator $initializerProperty,
MethodGenerator $callInitializer,
PublicPropertiesMap $publicProperties
PublicPropertiesMap $publicProperties,
ProtectedPropertiesMap $protectedProperties,
PrivatePropertiesMap $privateProperties,
InitializationTracker $initializationTracker
) {
parent::__construct($originalClass, '__get', array(new ParameterGenerator('name')));
parent::__construct($originalClass, '__get', [new ParameterGenerator('name')]);
$override = $originalClass->hasMethod('__get');
$callParent = '';
$override = $originalClass->hasMethod('__get');
$this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name');
$this->setDocBlock(($override ? "{@inheritDoc}\n" : '') . '@param string $name');
if (! $publicProperties->isEmpty()) {
$callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n"
. ' return $this->$name;'
. "\n}\n\n";
}
$parentAccess = 'return parent::__get($name);';
if ($override) {
$callParent .= 'return parent::__get($name);';
} else {
$callParent .= PublicScopeSimulator::getPublicAccessSimulationCode(
if (! $override) {
$parentAccess = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_GET,
'name'
);
}
$this->setBody(
'$this->' . $initializerProperty->getName() . ' && $this->' . $callInitializer->getName()
. '(\'__get\', array(\'name\' => $name));'
. "\n\n" . $callParent
);
$this->setBody(sprintf(
$this->callParentTemplate,
$initializerProperty->getName(),
$initializationTracker->getName(),
$callInitializer->getName(),
$publicProperties->getName(),
$protectedProperties->getName(),
$initializationTracker->getName(),
$protectedProperties->getName(),
$privateProperties->getName(),
$privateProperties->getName(),
$initializationTracker->getName(),
$privateProperties->getName(),
$parentAccess
));
}
}

View File

@@ -16,10 +16,14 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PrivatePropertiesMap;
use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\ProtectedPropertiesMap;
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
use ReflectionClass;
@@ -35,43 +39,110 @@ use Zend\Code\Generator\PropertyGenerator;
class MagicIsset extends MagicMethodGenerator
{
/**
* @param \ReflectionClass $originalClass
* @param \Zend\Code\Generator\PropertyGenerator $initializerProperty
* @param \Zend\Code\Generator\MethodGenerator $callInitializer
* @param \ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap $publicProperties
* @var string
*/
private $callParentTemplate = <<<'PHP'
%s
if (isset(self::$%s[$name])) {
return isset($this->$name);
}
if (isset(self::$%s[$name])) {
// check protected property access via compatible class
$callers = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
$caller = isset($callers[1]) ? $callers[1] : [];
$object = isset($caller['object']) ? $caller['object'] : '';
$expectedType = self::$%s[$name];
if ($object instanceof $expectedType) {
return isset($this->$name);
}
$class = isset($caller['class']) ? $caller['class'] : '';
if ($class === $expectedType || is_subclass_of($class, $expectedType)) {
return isset($this->$name);
}
} else {
// check private property access via same class
$callers = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
$caller = isset($callers[1]) ? $callers[1] : [];
$class = isset($caller['class']) ? $caller['class'] : '';
static $accessorCache = [];
if (isset(self::$%s[$name][$class])) {
$cacheKey = $class . '#' . $name;
$accessor = isset($accessorCache[$cacheKey])
? $accessorCache[$cacheKey]
: $accessorCache[$cacheKey] = \Closure::bind(function ($instance) use ($name) {
return isset($instance->$name);
}, null, $class);
return $accessor($this);
}
if ('ReflectionProperty' === $class) {
$tmpClass = key(self::$%s[$name]);
$cacheKey = $tmpClass . '#' . $name;
$accessor = isset($accessorCache[$cacheKey])
? $accessorCache[$cacheKey]
: $accessorCache[$cacheKey] = \Closure::bind(function ($instance) use ($name) {
return isset($instance->$name);
}, null, $tmpClass);
return $accessor($this);
}
}
%s
PHP;
/**
* @param ReflectionClass $originalClass
* @param PropertyGenerator $initializerProperty
* @param MethodGenerator $callInitializer
* @param PublicPropertiesMap $publicProperties
* @param ProtectedPropertiesMap $protectedProperties
* @param PrivatePropertiesMap $privateProperties
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
PropertyGenerator $initializerProperty,
MethodGenerator $callInitializer,
PublicPropertiesMap $publicProperties
PublicPropertiesMap $publicProperties,
ProtectedPropertiesMap $protectedProperties,
PrivatePropertiesMap $privateProperties
) {
parent::__construct($originalClass, '__isset', array(new ParameterGenerator('name')));
parent::__construct($originalClass, '__isset', [new ParameterGenerator('name')]);
$override = $originalClass->hasMethod('__isset');
$callParent = '';
$override = $originalClass->hasMethod('__isset');
$this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name');
$this->setDocBlock(($override ? "{@inheritDoc}\n" : '') . '@param string $name');
if (! $publicProperties->isEmpty()) {
$callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n"
. ' return isset($this->$name);'
. "\n}\n\n";
}
$parentAccess = 'return parent::__isset($name);';
if ($override) {
$callParent .= 'return parent::__isset($name);';
} else {
$callParent .= PublicScopeSimulator::getPublicAccessSimulationCode(
if (! $override) {
$parentAccess = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_ISSET,
'name'
);
}
$this->setBody(
$this->setBody(sprintf(
$this->callParentTemplate,
'$this->' . $initializerProperty->getName() . ' && $this->' . $callInitializer->getName()
. '(\'__isset\', array(\'name\' => $name));'
. "\n\n" . $callParent
);
. '(\'__isset\', array(\'name\' => $name));',
$publicProperties->getName(),
$protectedProperties->getName(),
$protectedProperties->getName(),
$privateProperties->getName(),
$privateProperties->getName(),
$parentAccess
));
}
}

View File

@@ -16,10 +16,14 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PrivatePropertiesMap;
use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\ProtectedPropertiesMap;
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
use ReflectionClass;
@@ -35,47 +39,116 @@ use Zend\Code\Generator\PropertyGenerator;
class MagicSet extends MagicMethodGenerator
{
/**
* @param \ReflectionClass $originalClass
* @param \Zend\Code\Generator\PropertyGenerator $initializerProperty
* @param \Zend\Code\Generator\MethodGenerator $callInitializer
* @param \ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap $publicProperties
* @var string
*/
private $callParentTemplate = <<<'PHP'
%s
if (isset(self::$%s[$name])) {
return ($this->$name = $value);
}
if (isset(self::$%s[$name])) {
// check protected property access via compatible class
$callers = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
$caller = isset($callers[1]) ? $callers[1] : [];
$object = isset($caller['object']) ? $caller['object'] : '';
$expectedType = self::$%s[$name];
if ($object instanceof $expectedType) {
return ($this->$name = $value);
}
$class = isset($caller['class']) ? $caller['class'] : '';
if ($class === $expectedType || is_subclass_of($class, $expectedType) || $class === 'ReflectionProperty') {
return ($this->$name = $value);
}
} elseif (isset(self::$%s[$name])) {
// check private property access via same class
$callers = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
$caller = isset($callers[1]) ? $callers[1] : [];
$class = isset($caller['class']) ? $caller['class'] : '';
static $accessorCache = [];
if (isset(self::$%s[$name][$class])) {
$cacheKey = $class . '#' . $name;
$accessor = isset($accessorCache[$cacheKey])
? $accessorCache[$cacheKey]
: $accessorCache[$cacheKey] = \Closure::bind(function ($instance, $value) use ($name) {
return ($instance->$name = $value);
}, null, $class);
return $accessor($this, $value);
}
if ('ReflectionProperty' === $class) {
$tmpClass = key(self::$%s[$name]);
$cacheKey = $tmpClass . '#' . $name;
$accessor = isset($accessorCache[$cacheKey])
? $accessorCache[$cacheKey]
: $accessorCache[$cacheKey] = \Closure::bind(function ($instance, $value) use ($name) {
return ($instance->$name = $value);
}, null, $tmpClass);
return $accessor($this, $value);
}
}
%s
PHP;
/**
* @param ReflectionClass $originalClass
* @param PropertyGenerator $initializerProperty
* @param MethodGenerator $callInitializer
* @param PublicPropertiesMap $publicProperties
* @param ProtectedPropertiesMap $protectedProperties
* @param PrivatePropertiesMap $privateProperties
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
PropertyGenerator $initializerProperty,
MethodGenerator $callInitializer,
PublicPropertiesMap $publicProperties
PublicPropertiesMap $publicProperties,
ProtectedPropertiesMap $protectedProperties,
PrivatePropertiesMap $privateProperties
) {
parent::__construct(
$originalClass,
'__set',
array(new ParameterGenerator('name'), new ParameterGenerator('value'))
[new ParameterGenerator('name'), new ParameterGenerator('value')]
);
$override = $originalClass->hasMethod('__set');
$callParent = '';
$override = $originalClass->hasMethod('__set');
$this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name');
$this->setDocBlock(($override ? "{@inheritDoc}\n" : '') . '@param string $name');
if (! $publicProperties->isEmpty()) {
$callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n"
. ' return ($this->$name = $value);'
. "\n}\n\n";
}
$parentAccess = 'return parent::__set($name, $value);';
if ($override) {
$callParent .= 'return parent::__set($name, $value);';
} else {
$callParent .= PublicScopeSimulator::getPublicAccessSimulationCode(
if (! $override) {
$parentAccess = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_SET,
'name',
'value'
);
}
$this->setBody(
$this->setBody(sprintf(
$this->callParentTemplate,
'$this->' . $initializerProperty->getName() . ' && $this->' . $callInitializer->getName()
. '(\'__set\', array(\'name\' => $name, \'value\' => $value));' . "\n\n" . $callParent
);
. '(\'__set\', array(\'name\' => $name, \'value\' => $value));',
$publicProperties->getName(),
$protectedProperties->getName(),
$protectedProperties->getName(),
$privateProperties->getName(),
$privateProperties->getName(),
$privateProperties->getName(),
$parentAccess
));
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
@@ -33,6 +35,10 @@ class MagicSleep extends MagicMethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
* @param PropertyGenerator $initializerProperty
* @param MethodGenerator $callInitializer
*/
public function __construct(
ReflectionClass $originalClass,
@@ -43,7 +49,7 @@ class MagicSleep extends MagicMethodGenerator
$this->setBody(
'$this->' . $initializerProperty->getName() . ' && $this->' . $callInitializer->getName()
. '(\'__sleep\', array());' . "\n\n"
. '(\'__sleep\', []);' . "\n\n"
. ($originalClass->hasMethod('__sleep') ? 'return parent::__sleep();' : 'return array_keys((array) $this);')
);
}

View File

@@ -16,10 +16,14 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PrivatePropertiesMap;
use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\ProtectedPropertiesMap;
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
use ReflectionClass;
@@ -35,44 +39,117 @@ use Zend\Code\Generator\PropertyGenerator;
class MagicUnset extends MagicMethodGenerator
{
/**
* @param \ReflectionClass $originalClass
* @param \Zend\Code\Generator\PropertyGenerator $initializerProperty
* @param \Zend\Code\Generator\MethodGenerator $callInitializer
* @param \ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap $publicProperties
* @var string
*/
private $callParentTemplate = <<<'PHP'
%s
if (isset(self::$%s[$name])) {
unset($this->$name);
return;
}
if (isset(self::$%s[$name])) {
// check protected property access via compatible class
$callers = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
$caller = isset($callers[1]) ? $callers[1] : [];
$object = isset($caller['object']) ? $caller['object'] : '';
$expectedType = self::$%s[$name];
if ($object instanceof $expectedType) {
unset($this->$name);
return;
}
$class = isset($caller['class']) ? $caller['class'] : '';
if ($class === $expectedType || is_subclass_of($class, $expectedType) || $class === 'ReflectionProperty') {
unset($this->$name);
return;
}
} elseif (isset(self::$%s[$name])) {
// check private property access via same class
$callers = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
$caller = isset($callers[1]) ? $callers[1] : [];
$class = isset($caller['class']) ? $caller['class'] : '';
static $accessorCache = [];
if (isset(self::$%s[$name][$class])) {
$cacheKey = $class . '#' . $name;
$accessor = isset($accessorCache[$cacheKey])
? $accessorCache[$cacheKey]
: $accessorCache[$cacheKey] = \Closure::bind(function ($instance) use ($name) {
unset($instance->$name);
}, null, $class);
return $accessor($this);
}
if ('ReflectionProperty' === $class) {
$tmpClass = key(self::$%s[$name]);
$cacheKey = $tmpClass . '#' . $name;
$accessor = isset($accessorCache[$cacheKey])
? $accessorCache[$cacheKey]
: $accessorCache[$cacheKey] = \Closure::bind(function ($instance) use ($name) {
unset($instance->$name);
}, null, $tmpClass);
return $accessor($this);
}
}
%s
PHP;
/**
* @param ReflectionClass $originalClass
* @param PropertyGenerator $initializerProperty
* @param MethodGenerator $callInitializer
* @param PublicPropertiesMap $publicProperties
* @param ProtectedPropertiesMap $protectedProperties
* @param PrivatePropertiesMap $privateProperties
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
PropertyGenerator $initializerProperty,
MethodGenerator $callInitializer,
PublicPropertiesMap $publicProperties
PublicPropertiesMap $publicProperties,
ProtectedPropertiesMap $protectedProperties,
PrivatePropertiesMap $privateProperties
) {
parent::__construct($originalClass, '__unset', array(new ParameterGenerator('name')));
parent::__construct($originalClass, '__unset', [new ParameterGenerator('name')]);
$override = $originalClass->hasMethod('__unset');
$callParent = '';
$override = $originalClass->hasMethod('__unset');
$this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name');
$this->setDocBlock(($override ? "{@inheritDoc}\n" : '') . '@param string $name');
if (! $publicProperties->isEmpty()) {
$callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n"
. ' unset($this->$name);'
. "\n\n return;"
. "\n}\n\n";
}
$parentAccess = 'return parent::__unset($name);';
if ($override) {
$callParent .= "return parent::__unset(\$name);";
} else {
$callParent .= PublicScopeSimulator::getPublicAccessSimulationCode(
if (! $override) {
$parentAccess = PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_UNSET,
'name'
);
}
$this->setBody(
$this->setBody(sprintf(
$this->callParentTemplate,
'$this->' . $initializerProperty->getName() . ' && $this->' . $callInitializer->getName()
. '(\'__unset\', array(\'name\' => $name));'
. "\n\n" . $callParent
);
. '(\'__unset\', array(\'name\' => $name));',
$publicProperties->getName(),
$protectedProperties->getName(),
$protectedProperties->getName(),
$privateProperties->getName(),
$privateProperties->getName(),
$privateProperties->getName(),
$parentAccess
));
}
}

View File

@@ -16,10 +16,12 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use Zend\Code\Generator\ParameterGenerator;
use Zend\Code\Generator\PropertyGenerator;
/**
@@ -33,17 +35,17 @@ class SetProxyInitializer extends MethodGenerator
{
/**
* Constructor
*
* @param PropertyGenerator $initializerProperty
*/
public function __construct(PropertyGenerator $initializerProperty)
{
parent::__construct('setProxyInitializer');
$initializerParameter = new ParameterGenerator('initializer');
$initializerParameter->setType('Closure');
$initializerParameter->setDefaultValue(null);
$this->setParameter($initializerParameter);
$this->setDocblock('{@inheritDoc}');
$this->setBody('$this->' . $initializerProperty->getName() . ' = $initializer;');
parent::__construct(
'setProxyInitializer',
[(new ParameterGenerator('initializer', 'Closure'))->setDefaultValue(null)],
self::FLAG_PUBLIC,
'$this->' . $initializerProperty->getName() . ' = $initializer;',
'{@inheritDoc}'
);
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
@@ -31,13 +33,15 @@ class InitializationTracker extends PropertyGenerator
{
/**
* Constructor
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct()
{
parent::__construct(UniqueIdentifierGenerator::getIdentifier('initializationTracker'));
$this->setVisibility(self::VISIBILITY_PRIVATE);
$this->setDocblock('@var bool tracks initialization status - true while the object is initializing');
$this->setDocBlock('@var bool tracks initialization status - true while the object is initializing');
$this->setDefaultValue(false);
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
@@ -31,12 +33,14 @@ class InitializerProperty extends PropertyGenerator
{
/**
* Constructor
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct()
{
parent::__construct(UniqueIdentifierGenerator::getIdentifier('initializer'));
$this->setVisibility(self::VISIBILITY_PRIVATE);
$this->setDocblock('@var \\Closure|null initializer responsible for generating the wrapped object');
$this->setDocBlock('@var \\Closure|null initializer responsible for generating the wrapped object');
}
}

View File

@@ -0,0 +1,75 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
use ProxyManager\ProxyGenerator\Util\Properties;
use Zend\Code\Generator\PropertyGenerator;
/**
* Property that contains the initializer for a lazy object
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*/
class PrivatePropertiesMap extends PropertyGenerator
{
const KEY_DEFAULT_VALUE = 'defaultValue';
/**
* Constructor
*
* @param Properties $properties
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(Properties $properties)
{
parent::__construct(
UniqueIdentifierGenerator::getIdentifier('privateProperties')
);
$this->setVisibility(self::VISIBILITY_PRIVATE);
$this->setStatic(true);
$this->setDocBlock(
'@var array[][] visibility and default value of defined properties, indexed by property name and class name'
);
$this->setDefaultValue($this->getMap($properties));
}
/**
* @param Properties $properties
*
* @return int[][]|mixed[][]
*/
private function getMap(Properties $properties) : array
{
$map = [];
foreach ($properties->getPrivateProperties() as $property) {
$propertyKey = & $map[$property->getName()];
$propertyKey[$property->getDeclaringClass()->getName()] = true;
}
return $map;
}
}

View File

@@ -0,0 +1,74 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
use ProxyManager\ProxyGenerator\Util\Properties;
use Zend\Code\Generator\PropertyGenerator;
/**
* Property that contains the protected instance lazy-loadable properties of an object
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*/
class ProtectedPropertiesMap extends PropertyGenerator
{
const KEY_DEFAULT_VALUE = 'defaultValue';
/**
* Constructor
*
* @param Properties $properties
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(Properties $properties)
{
parent::__construct(
UniqueIdentifierGenerator::getIdentifier('protectedProperties')
);
$this->setVisibility(self::VISIBILITY_PRIVATE);
$this->setStatic(true);
$this->setDocBlock(
'@var string[][] declaring class name of defined protected properties, indexed by property name'
);
$this->setDefaultValue($this->getMap($properties));
}
/**
*
* @param Properties $properties
*
* @return int[][]|mixed[][]
*/
private function getMap(Properties $properties) : array
{
$map = [];
foreach ($properties->getProtectedProperties() as $property) {
$map[$property->getName()] = $property->getDeclaringClass()->getName();
}
return $map;
}
}

View File

@@ -16,16 +16,20 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator;
use ProxyManager\Exception\InvalidProxiedClassException;
use ProxyManager\Generator\MethodGenerator as ProxyManagerMethodGenerator;
use ProxyManager\Generator\Util\ClassGeneratorUtils;
use ProxyManager\Proxy\GhostObjectInterface;
use ProxyManager\ProxyGenerator\Assertion\CanProxyAssertion;
use ProxyManager\ProxyGenerator\LazyLoading\MethodGenerator\Constructor;
use ProxyManager\ProxyGenerator\LazyLoading\MethodGenerator\StaticProxyConstructor;
use ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\CallInitializer;
use ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\GetProxyInitializer;
use ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\InitializeProxy;
use ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\IsProxyInitialized;
use ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\LazyLoadingMethodInterceptor;
use ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicClone;
use ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicGet;
use ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicIsset;
@@ -35,8 +39,10 @@ use ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicUnset;
use ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\SetProxyInitializer;
use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\InitializationTracker;
use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\InitializerProperty;
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesDefaults;
use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PrivatePropertiesMap;
use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\ProtectedPropertiesMap;
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
use ProxyManager\ProxyGenerator\Util\Properties;
use ProxyManager\ProxyGenerator\Util\ProxiedMethodsFilter;
use ReflectionClass;
use ReflectionMethod;
@@ -56,59 +62,104 @@ class LazyLoadingGhostGenerator implements ProxyGeneratorInterface
{
/**
* {@inheritDoc}
*
* @throws InvalidProxiedClassException
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function generate(ReflectionClass $originalClass, ClassGenerator $classGenerator)
public function generate(ReflectionClass $originalClass, ClassGenerator $classGenerator, array $proxyOptions = [])
{
CanProxyAssertion::assertClassCanBeProxied($originalClass);
CanProxyAssertion::assertClassCanBeProxied($originalClass, false);
$interfaces = array('ProxyManager\\Proxy\\GhostObjectInterface');
$publicProperties = new PublicPropertiesMap($originalClass);
$publicPropsDefaults = new PublicPropertiesDefaults($originalClass);
$filteredProperties = Properties::fromReflectionClass($originalClass)
->filter($proxyOptions['skippedProperties'] ?? []);
if ($originalClass->isInterface()) {
$interfaces[] = $originalClass->getName();
} else {
$classGenerator->setExtendedClass($originalClass->getName());
}
$publicProperties = new PublicPropertiesMap($filteredProperties);
$privateProperties = new PrivatePropertiesMap($filteredProperties);
$protectedProperties = new ProtectedPropertiesMap($filteredProperties);
$classGenerator->setImplementedInterfaces($interfaces);
$classGenerator->setExtendedClass($originalClass->getName());
$classGenerator->setImplementedInterfaces([GhostObjectInterface::class]);
$classGenerator->addPropertyFromGenerator($initializer = new InitializerProperty());
$classGenerator->addPropertyFromGenerator($initializationTracker = new InitializationTracker());
$classGenerator->addPropertyFromGenerator($publicProperties);
$classGenerator->addPropertyFromGenerator($publicPropsDefaults);
$classGenerator->addPropertyFromGenerator($privateProperties);
$classGenerator->addPropertyFromGenerator($protectedProperties);
$init = new CallInitializer($initializer, $publicPropsDefaults, $initializationTracker);
$init = new CallInitializer($initializer, $initializationTracker, $filteredProperties);
array_map(
function (MethodGenerator $generatedMethod) use ($originalClass, $classGenerator) {
ClassGeneratorUtils::addMethodIfNotFinal($originalClass, $classGenerator, $generatedMethod);
},
array_merge(
array_map(
function (ReflectionMethod $method) use ($initializer, $init) {
return LazyLoadingMethodInterceptor::generateMethod(
new MethodReflection($method->getDeclaringClass()->getName(), $method->getName()),
$initializer,
$init
);
},
ProxiedMethodsFilter::getProxiedMethods($originalClass)
),
array(
$this->getAbstractProxiedMethods($originalClass),
[
$init,
new Constructor($originalClass, $initializer),
new MagicGet($originalClass, $initializer, $init, $publicProperties),
new MagicSet($originalClass, $initializer, $init, $publicProperties),
new MagicIsset($originalClass, $initializer, $init, $publicProperties),
new MagicUnset($originalClass, $initializer, $init, $publicProperties),
new MagicClone($originalClass, $initializer, $init, $publicProperties),
new MagicSleep($originalClass, $initializer, $init, $publicProperties),
new StaticProxyConstructor($initializer, $filteredProperties),
new MagicGet(
$originalClass,
$initializer,
$init,
$publicProperties,
$protectedProperties,
$privateProperties,
$initializationTracker
),
new MagicSet(
$originalClass,
$initializer,
$init,
$publicProperties,
$protectedProperties,
$privateProperties
),
new MagicIsset(
$originalClass,
$initializer,
$init,
$publicProperties,
$protectedProperties,
$privateProperties
),
new MagicUnset(
$originalClass,
$initializer,
$init,
$publicProperties,
$protectedProperties,
$privateProperties
),
new MagicClone($originalClass, $initializer, $init),
new MagicSleep($originalClass, $initializer, $init),
new SetProxyInitializer($initializer),
new GetProxyInitializer($initializer),
new InitializeProxy($initializer, $init),
new IsProxyInitialized($initializer),
)
]
)
);
}
/**
* Retrieves all abstract methods to be proxied
*
* @param ReflectionClass $originalClass
*
* @return MethodGenerator[]
*/
private function getAbstractProxiedMethods(ReflectionClass $originalClass) : array
{
return array_map(
function (ReflectionMethod $method) : ProxyManagerMethodGenerator {
$generated = ProxyManagerMethodGenerator
::fromReflection(new MethodReflection($method->getDeclaringClass()->getName(), $method->getName()));
$generated->setAbstract(false);
return $generated;
},
ProxiedMethodsFilter::getAbstractProxiedMethods($originalClass)
);
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingValueHolder\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
@@ -32,11 +34,15 @@ class GetProxyInitializer extends MethodGenerator
{
/**
* Constructor
*
* @param PropertyGenerator $initializerProperty
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(PropertyGenerator $initializerProperty)
{
parent::__construct('getProxyInitializer');
$this->setDocblock('{@inheritDoc}');
$this->setDocBlock('{@inheritDoc}');
$this->setBody('return $this->' . $initializerProperty->getName() . ';');
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingValueHolder\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
@@ -32,11 +34,17 @@ class InitializeProxy extends MethodGenerator
{
/**
* Constructor
*
* @param PropertyGenerator $initializerProperty
* @param PropertyGenerator $valueHolderProperty
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(PropertyGenerator $initializerProperty, PropertyGenerator $valueHolderProperty)
{
parent::__construct('initializeProxy');
$this->setDocblock('{@inheritDoc}');
$this->setDocBlock('{@inheritDoc}');
$this->setReturnType('bool');
$initializer = $initializerProperty->getName();

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingValueHolder\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
@@ -32,11 +34,16 @@ class IsProxyInitialized extends MethodGenerator
{
/**
* Constructor
*
* @param PropertyGenerator $valueHolderProperty
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(PropertyGenerator $valueHolderProperty)
{
parent::__construct('isProxyInitialized');
$this->setDocblock('{@inheritDoc}');
$this->setDocBlock('{@inheritDoc}');
$this->setReturnType('bool');
$this->setBody('return null !== $this->' . $valueHolderProperty->getName() . ';');
}
}

View File

@@ -16,9 +16,12 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingValueHolder\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
use ProxyManager\Generator\Util\ProxiedMethodReturnExpression;
use Zend\Code\Generator\PropertyGenerator;
use Zend\Code\Reflection\MethodReflection;
@@ -35,26 +38,29 @@ class LazyLoadingMethodInterceptor extends MethodGenerator
* @param \Zend\Code\Generator\PropertyGenerator $initializerProperty
* @param \Zend\Code\Generator\PropertyGenerator $valueHolderProperty
*
* @return LazyLoadingMethodInterceptor|static
* @return self
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public static function generateMethod(
MethodReflection $originalMethod,
PropertyGenerator $initializerProperty,
PropertyGenerator $valueHolderProperty
) {
) : self {
/* @var $method self */
$method = static::fromReflection($originalMethod);
$initializerName = $initializerProperty->getName();
$valueHolderName = $valueHolderProperty->getName();
$parameters = $originalMethod->getParameters();
$methodName = $originalMethod->getName();
$initializerParams = array();
$forwardedParams = array();
$initializerParams = [];
$forwardedParams = [];
foreach ($parameters as $parameter) {
$parameterName = $parameter->getName();
$variadicPrefix = $parameter->isVariadic() ? '...' : '';
$initializerParams[] = var_export($parameterName, true) . ' => $' . $parameterName;
$forwardedParams[] = '$' . $parameterName;
$forwardedParams[] = $variadicPrefix . '$' . $parameterName;
}
$method->setBody(
@@ -62,10 +68,12 @@ class LazyLoadingMethodInterceptor extends MethodGenerator
. ' && $this->' . $initializerName
. '->__invoke($this->' . $valueHolderName . ', $this, ' . var_export($methodName, true)
. ', array(' . implode(', ', $initializerParams) . '), $this->' . $initializerName . ");\n\n"
. 'return $this->' . $valueHolderName . '->'
. $methodName . '(' . implode(', ', $forwardedParams) . ');'
. ProxiedMethodReturnExpression::generate(
'$this->' . $valueHolderName . '->' . $methodName . '(' . implode(', ', $forwardedParams) . ')',
$originalMethod
)
);
$method->setDocblock('{@inheritDoc}');
$method->setDocBlock('{@inheritDoc}');
return $method;
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingValueHolder\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
@@ -32,6 +34,10 @@ class MagicClone extends MagicMethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
* @param PropertyGenerator $initializerProperty
* @param PropertyGenerator $valueHolderProperty
*/
public function __construct(
ReflectionClass $originalClass,

View File

@@ -16,10 +16,12 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingValueHolder\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
use ReflectionClass;
@@ -35,6 +37,14 @@ class MagicGet extends MagicMethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
* @param PropertyGenerator $initializerProperty
* @param PropertyGenerator $valueHolderProperty
* @param PublicPropertiesMap $publicProperties
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
@@ -42,9 +52,11 @@ class MagicGet extends MagicMethodGenerator
PropertyGenerator $valueHolderProperty,
PublicPropertiesMap $publicProperties
) {
parent::__construct($originalClass, '__get', array(new ParameterGenerator('name')));
parent::__construct($originalClass, '__get', [new ParameterGenerator('name')]);
$this->setDocblock(($originalClass->hasMethod('__get') ? "{@inheritDoc}\n" : '') . '@param string $name');
$hasParent = $originalClass->hasMethod('__get');
$this->setDocBlock(($hasParent ? "{@inheritDoc}\n" : '') . '@param string $name');
$initializer = $initializerProperty->getName();
$valueHolder = $valueHolderProperty->getName();
@@ -52,16 +64,33 @@ class MagicGet extends MagicMethodGenerator
. ' return $this->' . $valueHolder . '->$name;'
. "\n}\n\n";
$callParent .= PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_GET,
'name',
null,
$valueHolderProperty
);
if ($hasParent) {
$this->setInitializerBody(
$initializer,
$valueHolder,
$callParent . 'return $this->' . $valueHolder . '->__get($name);'
);
return;
}
$this->setInitializerBody(
$initializer,
$valueHolder,
$callParent . PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_GET,
'name',
null,
$valueHolderProperty
)
);
}
private function setInitializerBody(string $initializer, string $valueHolder, string $callParent) : void
{
$this->setBody(
'$this->' . $initializer . ' && $this->' . $initializer
. '->__invoke($this->' . $valueHolder . ', $this, \'__get\', array(\'name\' => $name), $this->'
. '->__invoke($this->' . $valueHolder . ', $this, \'__get\', [\'name\' => $name], $this->'
. $initializer . ');'
. "\n\n" . $callParent
);

View File

@@ -16,10 +16,12 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingValueHolder\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
use ReflectionClass;
@@ -35,6 +37,14 @@ class MagicIsset extends MagicMethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
* @param PropertyGenerator $initializerProperty
* @param PropertyGenerator $valueHolderProperty
* @param PublicPropertiesMap $publicProperties
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
@@ -42,13 +52,13 @@ class MagicIsset extends MagicMethodGenerator
PropertyGenerator $valueHolderProperty,
PublicPropertiesMap $publicProperties
) {
parent::__construct($originalClass, '__isset', array(new ParameterGenerator('name')));
parent::__construct($originalClass, '__isset', [new ParameterGenerator('name')]);
$initializer = $initializerProperty->getName();
$valueHolder = $valueHolderProperty->getName();
$callParent = '';
$this->setDocblock(($originalClass->hasMethod('__isset') ? "{@inheritDoc}\n" : '') . '@param string $name');
$this->setDocBlock(($originalClass->hasMethod('__isset') ? "{@inheritDoc}\n" : '') . '@param string $name');
if (! $publicProperties->isEmpty()) {
$callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n"

View File

@@ -16,10 +16,12 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingValueHolder\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
use ReflectionClass;
@@ -35,6 +37,14 @@ class MagicSet extends MagicMethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
* @param PropertyGenerator $initializerProperty
* @param PropertyGenerator $valueHolderProperty
* @param PublicPropertiesMap $publicProperties
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
@@ -45,16 +55,15 @@ class MagicSet extends MagicMethodGenerator
parent::__construct(
$originalClass,
'__set',
array(new ParameterGenerator('name'), new ParameterGenerator('value'))
[new ParameterGenerator('name'), new ParameterGenerator('value')]
);
$hasParent = $originalClass->hasMethod('__set');
$initializer = $initializerProperty->getName();
$valueHolder = $valueHolderProperty->getName();
$callParent = '';
$this->setDocblock(
($originalClass->hasMethod('__set') ? "{@inheritDoc}\n" : '') . "@param string \$name\n@param mixed \$value"
);
$this->setDocBlock(($hasParent ? "{@inheritDoc}\n" : '') . "@param string \$name\n@param mixed \$value");
if (! $publicProperties->isEmpty()) {
$callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n"
@@ -62,12 +71,14 @@ class MagicSet extends MagicMethodGenerator
. "\n}\n\n";
}
$callParent .= PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_SET,
'name',
'value',
$valueHolderProperty
);
$callParent .= $hasParent
? 'return $this->' . $valueHolder . '->__set($name, $value);'
: PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_SET,
'name',
'value',
$valueHolderProperty
);
$this->setBody(
'$this->' . $initializer . ' && $this->' . $initializer

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingValueHolder\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
@@ -32,6 +34,10 @@ class MagicSleep extends MagicMethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
* @param PropertyGenerator $initializerProperty
* @param PropertyGenerator $valueHolderProperty
*/
public function __construct(
ReflectionClass $originalClass,

View File

@@ -16,10 +16,12 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingValueHolder\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use Zend\Code\Generator\ParameterGenerator;
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
use ReflectionClass;
@@ -35,6 +37,14 @@ class MagicUnset extends MagicMethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
* @param PropertyGenerator $initializerProperty
* @param PropertyGenerator $valueHolderProperty
* @param PublicPropertiesMap $publicProperties
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function __construct(
ReflectionClass $originalClass,
@@ -42,13 +52,14 @@ class MagicUnset extends MagicMethodGenerator
PropertyGenerator $valueHolderProperty,
PublicPropertiesMap $publicProperties
) {
parent::__construct($originalClass, '__unset', array(new ParameterGenerator('name')));
parent::__construct($originalClass, '__unset', [new ParameterGenerator('name')]);
$hasParent = $originalClass->hasMethod('__unset');
$initializer = $initializerProperty->getName();
$valueHolder = $valueHolderProperty->getName();
$callParent = '';
$this->setDocblock(($originalClass->hasMethod('__isset') ? "{@inheritDoc}\n" : '') . '@param string $name');
$this->setDocBlock(($hasParent ? "{@inheritDoc}\n" : '') . '@param string $name');
if (! $publicProperties->isEmpty()) {
$callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n"
@@ -56,12 +67,14 @@ class MagicUnset extends MagicMethodGenerator
. "\n}\n\n";
}
$callParent .= PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_UNSET,
'name',
null,
$valueHolderProperty
);
$callParent .= $hasParent
? 'return $this->' . $valueHolder . '->__unset($name);'
: PublicScopeSimulator::getPublicAccessSimulationCode(
PublicScopeSimulator::OPERATION_UNSET,
'name',
null,
$valueHolderProperty
);
$this->setBody(
'$this->' . $initializer . ' && $this->' . $initializer

View File

@@ -16,10 +16,13 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingValueHolder\MethodGenerator;
use Closure;
use ProxyManager\Generator\MethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use Zend\Code\Generator\ParameterGenerator;
use Zend\Code\Generator\PropertyGenerator;
/**
@@ -33,6 +36,10 @@ class SetProxyInitializer extends MethodGenerator
{
/**
* Constructor
*
* @param PropertyGenerator $initializerProperty
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(PropertyGenerator $initializerProperty)
{
@@ -40,10 +47,10 @@ class SetProxyInitializer extends MethodGenerator
$initializerParameter = new ParameterGenerator('initializer');
$initializerParameter->setType('Closure');
$initializerParameter->setType(Closure::class);
$initializerParameter->setDefaultValue(null);
$this->setParameter($initializerParameter);
$this->setDocblock('{@inheritDoc}');
$this->setDocBlock('{@inheritDoc}');
$this->setBody('$this->' . $initializerProperty->getName() . ' = $initializer;');
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingValueHolder\PropertyGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
@@ -31,12 +33,14 @@ class InitializerProperty extends PropertyGenerator
{
/**
* Constructor
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct()
{
parent::__construct(UniqueIdentifierGenerator::getIdentifier('initializer'));
$this->setVisibility(self::VISIBILITY_PRIVATE);
$this->setDocblock('@var \\Closure|null initializer responsible for generating the wrapped object');
$this->setDocBlock('@var \\Closure|null initializer responsible for generating the wrapped object');
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\LazyLoadingValueHolder\PropertyGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
@@ -31,12 +33,14 @@ class ValueHolderProperty extends PropertyGenerator
{
/**
* Constructor
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct()
{
parent::__construct(UniqueIdentifierGenerator::getIdentifier('valueHolder'));
$this->setVisibility(self::VISIBILITY_PRIVATE);
$this->setDocblock('@var \\Closure|null initializer responsible for generating the wrapped object');
$this->setDocBlock('@var \\Closure|null initializer responsible for generating the wrapped object');
}
}

View File

@@ -16,12 +16,16 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator;
use ProxyManager\Exception\InvalidProxiedClassException;
use ProxyManager\Generator\Util\ClassGeneratorUtils;
use ProxyManager\Proxy\VirtualProxyInterface;
use ProxyManager\ProxyGenerator\AccessInterceptor\MethodGenerator\MagicWakeup;
use ProxyManager\ProxyGenerator\Assertion\CanProxyAssertion;
use ProxyManager\ProxyGenerator\LazyLoading\MethodGenerator\Constructor;
use ProxyManager\ProxyGenerator\LazyLoading\MethodGenerator\StaticProxyConstructor;
use ProxyManager\ProxyGenerator\LazyLoadingValueHolder\MethodGenerator\GetProxyInitializer;
use ProxyManager\ProxyGenerator\LazyLoadingValueHolder\MethodGenerator\InitializeProxy;
use ProxyManager\ProxyGenerator\LazyLoadingValueHolder\MethodGenerator\IsProxyInitialized;
@@ -36,7 +40,9 @@ use ProxyManager\ProxyGenerator\LazyLoadingValueHolder\MethodGenerator\SetProxyI
use ProxyManager\ProxyGenerator\LazyLoadingValueHolder\PropertyGenerator\InitializerProperty;
use ProxyManager\ProxyGenerator\LazyLoadingValueHolder\PropertyGenerator\ValueHolderProperty;
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
use ProxyManager\ProxyGenerator\Util\Properties;
use ProxyManager\ProxyGenerator\Util\ProxiedMethodsFilter;
use ProxyManager\ProxyGenerator\ValueHolder\MethodGenerator\Constructor;
use ProxyManager\ProxyGenerator\ValueHolder\MethodGenerator\GetWrappedValueHolderValue;
use ReflectionClass;
use ReflectionMethod;
@@ -56,13 +62,17 @@ class LazyLoadingValueHolderGenerator implements ProxyGeneratorInterface
{
/**
* {@inheritDoc}
*
* @throws InvalidProxiedClassException
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
* @throws \InvalidArgumentException
*/
public function generate(ReflectionClass $originalClass, ClassGenerator $classGenerator)
{
CanProxyAssertion::assertClassCanBeProxied($originalClass);
$interfaces = array('ProxyManager\\Proxy\\VirtualProxyInterface');
$publicProperties = new PublicPropertiesMap($originalClass);
$interfaces = [VirtualProxyInterface::class];
$publicProperties = new PublicPropertiesMap(Properties::fromReflectionClass($originalClass));
if ($originalClass->isInterface()) {
$interfaces[] = $originalClass->getName();
@@ -81,17 +91,12 @@ class LazyLoadingValueHolderGenerator implements ProxyGeneratorInterface
},
array_merge(
array_map(
function (ReflectionMethod $method) use ($initializer, $valueHolder) {
return LazyLoadingMethodInterceptor::generateMethod(
new MethodReflection($method->getDeclaringClass()->getName(), $method->getName()),
$initializer,
$valueHolder
);
},
$this->buildLazyLoadingMethodInterceptor($initializer, $valueHolder),
ProxiedMethodsFilter::getProxiedMethods($originalClass)
),
array(
new Constructor($originalClass, $initializer),
[
new StaticProxyConstructor($initializer, Properties::fromReflectionClass($originalClass)),
Constructor::generateMethod($originalClass, $valueHolder),
new MagicGet($originalClass, $initializer, $valueHolder, $publicProperties),
new MagicSet($originalClass, $initializer, $valueHolder, $publicProperties),
new MagicIsset($originalClass, $initializer, $valueHolder, $publicProperties),
@@ -104,8 +109,21 @@ class LazyLoadingValueHolderGenerator implements ProxyGeneratorInterface
new InitializeProxy($initializer, $valueHolder),
new IsProxyInitialized($valueHolder),
new GetWrappedValueHolderValue($valueHolder),
)
]
)
);
}
private function buildLazyLoadingMethodInterceptor(
InitializerProperty $initializer,
ValueHolderProperty $valueHolder
) : callable {
return function (ReflectionMethod $method) use ($initializer, $valueHolder) : LazyLoadingMethodInterceptor {
return LazyLoadingMethodInterceptor::generateMethod(
new MethodReflection($method->getDeclaringClass()->getName(), $method->getName()),
$initializer,
$valueHolder
);
};
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\NullObject\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
@@ -33,21 +35,29 @@ class NullObjectMethodInterceptor extends MethodGenerator
/**
* @param \Zend\Code\Reflection\MethodReflection $originalMethod
*
* @return NullObjectMethodInterceptor|static
* @return self|static
*/
public static function generateMethod(MethodReflection $originalMethod)
public static function generateMethod(MethodReflection $originalMethod) : self
{
/* @var $method self */
$method = static::fromReflection($originalMethod);
if ('void' === (string) $originalMethod->getReturnType()) {
$method->setBody('');
return $method;
}
if ($originalMethod->returnsReference()) {
$reference = UniqueIdentifierGenerator::getIdentifier('ref');
$method->setBody("\$$reference = null;\nreturn \$$reference;");
} else {
$method->setBody('');
return $method;
}
$method->setBody('');
return $method;
}
}

View File

@@ -0,0 +1,63 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\NullObject\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
use ProxyManager\ProxyGenerator\Util\Properties;
use ReflectionClass;
use ReflectionProperty;
/**
* The `staticProxyConstructor` implementation for null object proxies
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*/
class StaticProxyConstructor extends MethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass Reflection of the class to proxy
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(ReflectionClass $originalClass)
{
parent::__construct('staticProxyConstructor', [], static::FLAG_PUBLIC | static::FLAG_STATIC);
$nullableProperties = array_map(
function (ReflectionProperty $publicProperty) : string {
return '$instance->' . $publicProperty->getName() . ' = null;';
},
Properties::fromReflectionClass($originalClass)->getPublicProperties()
);
$this->setDocBlock('Constructor for null object initialization');
$this->setBody(
'static $reflection;' . "\n\n"
. '$reflection = $reflection ?: $reflection = new \ReflectionClass(__CLASS__);' . "\n"
. '$instance = (new \ReflectionClass(get_class()))->newInstanceWithoutConstructor();' . "\n\n"
. ($nullableProperties ? implode("\n", $nullableProperties) . "\n\n" : '')
. 'return $instance;'
);
}
}

View File

@@ -16,12 +16,16 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator;
use ProxyManager\Exception\InvalidProxiedClassException;
use ProxyManager\Generator\Util\ClassGeneratorUtils;
use ProxyManager\Proxy\NullObjectInterface;
use ProxyManager\ProxyGenerator\Assertion\CanProxyAssertion;
use ProxyManager\ProxyGenerator\NullObject\MethodGenerator\Constructor;
use ProxyManager\ProxyGenerator\NullObject\MethodGenerator\NullObjectMethodInterceptor;
use ProxyManager\ProxyGenerator\NullObject\MethodGenerator\StaticProxyConstructor;
use ProxyManager\ProxyGenerator\Util\ProxiedMethodsFilter;
use ReflectionClass;
use Zend\Code\Generator\ClassGenerator;
@@ -39,20 +43,25 @@ class NullObjectGenerator implements ProxyGeneratorInterface
{
/**
* {@inheritDoc}
*
* @throws InvalidProxiedClassException
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function generate(ReflectionClass $originalClass, ClassGenerator $classGenerator)
{
CanProxyAssertion::assertClassCanBeProxied($originalClass);
$interfaces = array('ProxyManager\\Proxy\\NullObjectInterface');
$interfaces = [NullObjectInterface::class];
if ($originalClass->isInterface()) {
$interfaces[] = $originalClass->getName();
} else {
$classGenerator->setExtendedClass($originalClass->getName());
}
$classGenerator->setImplementedInterfaces($interfaces);
foreach (ProxiedMethodsFilter::getProxiedMethods($originalClass) as $method) {
foreach (ProxiedMethodsFilter::getProxiedMethods($originalClass, []) as $method) {
$classGenerator->addMethodFromGenerator(
NullObjectMethodInterceptor::generateMethod(
new MethodReflection($method->getDeclaringClass()->getName(), $method->getName())
@@ -60,6 +69,10 @@ class NullObjectGenerator implements ProxyGeneratorInterface
);
}
ClassGeneratorUtils::addMethodIfNotFinal($originalClass, $classGenerator, new Constructor($originalClass));
ClassGeneratorUtils::addMethodIfNotFinal(
$originalClass,
$classGenerator,
new StaticProxyConstructor($originalClass)
);
}
}

View File

@@ -16,11 +16,12 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\PropertyGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
use ReflectionClass;
use ReflectionProperty;
use ProxyManager\ProxyGenerator\Util\Properties;
use Zend\Code\Generator\PropertyGenerator;
/**
@@ -34,30 +35,32 @@ class PublicPropertiesMap extends PropertyGenerator
/**
* @var bool[]
*/
private $publicProperties = array();
private $publicProperties = [];
/**
* @param \ReflectionClass $originalClass
* @param Properties $properties
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(ReflectionClass $originalClass)
public function __construct(Properties $properties)
{
parent::__construct(UniqueIdentifierGenerator::getIdentifier('publicProperties'));
foreach ($originalClass->getProperties(ReflectionProperty::IS_PUBLIC) as $publicProperty) {
foreach ($properties->getPublicProperties() as $publicProperty) {
$this->publicProperties[$publicProperty->getName()] = true;
}
$this->setDefaultValue($this->publicProperties);
$this->setVisibility(self::VISIBILITY_PRIVATE);
$this->setStatic(true);
$this->setDocblock('@var bool[] map of public properties of the parent class');
$this->setDocBlock('@var bool[] map of public properties of the parent class');
}
/**
* @return bool whether there are no public properties
*/
public function isEmpty()
public function isEmpty() : bool
{
return empty($this->publicProperties);
return ! $this->publicProperties;
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator;
use ReflectionClass;

View File

@@ -16,10 +16,12 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\RemoteObject\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use Zend\Code\Generator\ParameterGenerator;
use ReflectionClass;
use Zend\Code\Generator\PropertyGenerator;
@@ -35,12 +37,14 @@ class MagicGet extends MagicMethodGenerator
* Constructor
* @param ReflectionClass $originalClass
* @param \Zend\Code\Generator\PropertyGenerator $adapterProperty
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(ReflectionClass $originalClass, PropertyGenerator $adapterProperty)
{
parent::__construct($originalClass, '__get', array(new ParameterGenerator('name')));
parent::__construct($originalClass, '__get', [new ParameterGenerator('name')]);
$this->setDocblock('@param string $name');
$this->setDocBlock('@param string $name');
$this->setBody(
'$return = $this->' . $adapterProperty->getName() . '->call(' . var_export($originalClass->getName(), true)
. ', \'__get\', array($name));' . "\n\n" . 'return $return;'

View File

@@ -16,10 +16,12 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\RemoteObject\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use Zend\Code\Generator\ParameterGenerator;
use ReflectionClass;
use Zend\Code\Generator\PropertyGenerator;
@@ -35,12 +37,14 @@ class MagicIsset extends MagicMethodGenerator
* Constructor
* @param ReflectionClass $originalClass
* @param \Zend\Code\Generator\PropertyGenerator $adapterProperty
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(ReflectionClass $originalClass, PropertyGenerator $adapterProperty)
{
parent::__construct($originalClass, '__isset', array(new ParameterGenerator('name')));
parent::__construct($originalClass, '__isset', [new ParameterGenerator('name')]);
$this->setDocblock('@param string $name');
$this->setDocBlock('@param string $name');
$this->setBody(
'$return = $this->' . $adapterProperty->getName() . '->call(' . var_export($originalClass->getName(), true)
. ', \'__isset\', array($name));' . "\n\n"

View File

@@ -16,10 +16,12 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\RemoteObject\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use Zend\Code\Generator\ParameterGenerator;
use ReflectionClass;
use Zend\Code\Generator\PropertyGenerator;
@@ -35,16 +37,18 @@ class MagicSet extends MagicMethodGenerator
* Constructor
* @param ReflectionClass $originalClass
* @param \Zend\Code\Generator\PropertyGenerator $adapterProperty
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(ReflectionClass $originalClass, PropertyGenerator $adapterProperty)
{
parent::__construct(
$originalClass,
'__set',
array(new ParameterGenerator('name'), new ParameterGenerator('value'))
[new ParameterGenerator('name'), new ParameterGenerator('value')]
);
$this->setDocblock('@param string \$name\n@param mixed \$value');
$this->setDocBlock('@param string \$name\n@param mixed \$value');
$this->setBody(
'$return = $this->' . $adapterProperty->getName() . '->call(' . var_export($originalClass->getName(), true)
. ', \'__set\', array($name, $value));' . "\n\n"

View File

@@ -16,10 +16,12 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\RemoteObject\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
use ProxyManager\Generator\ParameterGenerator;
use Zend\Code\Generator\ParameterGenerator;
use ReflectionClass;
use Zend\Code\Generator\PropertyGenerator;
@@ -36,12 +38,14 @@ class MagicUnset extends MagicMethodGenerator
*
* @param ReflectionClass $originalClass
* @param PropertyGenerator $adapterProperty
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(ReflectionClass $originalClass, PropertyGenerator $adapterProperty)
{
parent::__construct($originalClass, '__unset', array(new ParameterGenerator('name')));
parent::__construct($originalClass, '__unset', [new ParameterGenerator('name')]);
$this->setDocblock('@param string $name');
$this->setDocBlock('@param string $name');
$this->setBody(
'$return = $this->' . $adapterProperty->getName() . '->call(' . var_export($originalClass->getName(), true)
. ', \'__unset\', array($name));' . "\n\n"

View File

@@ -16,10 +16,14 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\RemoteObject\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
use ProxyManager\Generator\Util\ProxiedMethodReturnExpression;
use ReflectionClass;
use Zend\Code\Generator\ParameterGenerator;
use Zend\Code\Generator\PropertyGenerator;
use Zend\Code\Reflection\MethodReflection;
@@ -36,26 +40,27 @@ class RemoteObjectMethod extends MethodGenerator
* @param \Zend\Code\Generator\PropertyGenerator $adapterProperty
* @param \ReflectionClass $originalClass
*
* @return RemoteObjectMethod|static
* @return self|static
*/
public static function generateMethod(
MethodReflection $originalMethod,
PropertyGenerator $adapterProperty,
ReflectionClass $originalClass
) {
) : self {
/* @var $method self */
$method = static::fromReflection($originalMethod);
$parameters = $originalMethod->getParameters();
$list = array();
foreach ($parameters as $parameter) {
$list[] = '$' . $parameter->getName();
}
$list = array_values(array_map(
function (ParameterGenerator $parameter) : string {
return '$' . $parameter->getName();
},
$method->getParameters()
));
$method->setBody(
'$return = $this->' . $adapterProperty->getName() . '->call(' . var_export($originalClass->getName(), true)
'$return = $this->' . $adapterProperty->getName()
. '->call(' . var_export($originalClass->getName(), true)
. ', ' . var_export($originalMethod->getName(), true) . ', array('. implode(', ', $list) .'));' . "\n\n"
. 'return $return;'
. ProxiedMethodReturnExpression::generate('$return', $originalMethod)
);
return $method;

View File

@@ -0,0 +1,67 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\RemoteObject\MethodGenerator;
use ProxyManager\Factory\RemoteObject\AdapterInterface;
use ProxyManager\Generator\MethodGenerator;
use ProxyManager\ProxyGenerator\Util\Properties;
use ProxyManager\ProxyGenerator\Util\UnsetPropertiesGenerator;
use ReflectionClass;
use Zend\Code\Generator\ParameterGenerator;
use Zend\Code\Generator\PropertyGenerator;
/**
* The `staticProxyConstructor` implementation for remote object proxies
*
* @author Vincent Blanchon <blanchon.vincent@gmail.com>
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*/
class StaticProxyConstructor extends MethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass Reflection of the class to proxy
* @param PropertyGenerator $adapter Adapter property
*/
public function __construct(ReflectionClass $originalClass, PropertyGenerator $adapter)
{
$adapterName = $adapter->getName();
parent::__construct(
'staticProxyConstructor',
[new ParameterGenerator($adapterName, AdapterInterface::class)],
MethodGenerator::FLAG_PUBLIC | MethodGenerator::FLAG_STATIC,
null,
'Constructor for remote object control\n\n'
. '@param \\ProxyManager\\Factory\\RemoteObject\\AdapterInterface \$adapter'
);
$body = 'static $reflection;' . "\n\n"
. '$reflection = $reflection ?: $reflection = new \ReflectionClass(__CLASS__);' . "\n"
. '$instance = (new \ReflectionClass(get_class()))->newInstanceWithoutConstructor();' . "\n\n"
. '$instance->' . $adapterName . ' = $' . $adapterName . ";\n\n"
. UnsetPropertiesGenerator::generateSnippet(Properties::fromReflectionClass($originalClass), 'instance');
$this->setBody($body . "\n\nreturn \$instance;");
}
}

View File

@@ -16,8 +16,11 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\RemoteObject\PropertyGenerator;
use ProxyManager\Factory\RemoteObject\AdapterInterface;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
use Zend\Code\Generator\PropertyGenerator;
@@ -31,12 +34,14 @@ class AdapterProperty extends PropertyGenerator
{
/**
* Constructor
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct()
{
parent::__construct(UniqueIdentifierGenerator::getIdentifier('adapter'));
$this->setVisibility(self::VISIBILITY_PRIVATE);
$this->setDocblock('@var \\ProxyManager\\Factory\\RemoteObject\\AdapterInterface Remote web service adapter');
$this->setDocBlock('@var \\' . AdapterInterface::class . ' Remote web service adapter');
}
}

View File

@@ -16,16 +16,20 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator;
use ProxyManager\Exception\InvalidProxiedClassException;
use ProxyManager\Generator\Util\ClassGeneratorUtils;
use ProxyManager\Proxy\RemoteObjectInterface;
use ProxyManager\ProxyGenerator\Assertion\CanProxyAssertion;
use ProxyManager\ProxyGenerator\RemoteObject\MethodGenerator\Constructor;
use ProxyManager\ProxyGenerator\RemoteObject\MethodGenerator\MagicGet;
use ProxyManager\ProxyGenerator\RemoteObject\MethodGenerator\MagicIsset;
use ProxyManager\ProxyGenerator\RemoteObject\MethodGenerator\MagicSet;
use ProxyManager\ProxyGenerator\RemoteObject\MethodGenerator\MagicUnset;
use ProxyManager\ProxyGenerator\RemoteObject\MethodGenerator\RemoteObjectMethod;
use ProxyManager\ProxyGenerator\RemoteObject\MethodGenerator\StaticProxyConstructor;
use ProxyManager\ProxyGenerator\RemoteObject\PropertyGenerator\AdapterProperty;
use ProxyManager\ProxyGenerator\Util\ProxiedMethodsFilter;
use ReflectionClass;
@@ -46,12 +50,15 @@ class RemoteObjectGenerator implements ProxyGeneratorInterface
{
/**
* {@inheritDoc}
*
* @throws InvalidProxiedClassException
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function generate(ReflectionClass $originalClass, ClassGenerator $classGenerator)
{
CanProxyAssertion::assertClassCanBeProxied($originalClass);
$interfaces = array('ProxyManager\\Proxy\\RemoteObjectInterface');
$interfaces = [RemoteObjectInterface::class];
if ($originalClass->isInterface()) {
$interfaces[] = $originalClass->getName();
@@ -68,7 +75,7 @@ class RemoteObjectGenerator implements ProxyGeneratorInterface
},
array_merge(
array_map(
function (ReflectionMethod $method) use ($adapter, $originalClass) {
function (ReflectionMethod $method) use ($adapter, $originalClass) : RemoteObjectMethod {
return RemoteObjectMethod::generateMethod(
new MethodReflection($method->getDeclaringClass()->getName(), $method->getName()),
$adapter,
@@ -77,16 +84,16 @@ class RemoteObjectGenerator implements ProxyGeneratorInterface
},
ProxiedMethodsFilter::getProxiedMethods(
$originalClass,
array('__get', '__set', '__isset', '__unset')
['__get', '__set', '__isset', '__unset']
)
),
array(
new Constructor($originalClass, $adapter),
[
new StaticProxyConstructor($originalClass, $adapter),
new MagicGet($originalClass, $adapter),
new MagicSet($originalClass, $adapter),
new MagicIsset($originalClass, $adapter),
new MagicUnset($originalClass, $adapter),
)
]
)
);
}

View File

@@ -0,0 +1,42 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\Util;
use ReflectionClass;
use ReflectionMethod;
/**
* Internal utility class - allows fetching a method from a given class, if it exists
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*/
final class GetMethodIfExists
{
private function __construct()
{
}
public static function get(ReflectionClass $class, string $method) : ?ReflectionMethod
{
return $class->hasMethod($method) ? $class->getMethod($method) : null;
}
}

View File

@@ -0,0 +1,166 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\Util;
use ReflectionClass;
use ReflectionProperty;
/**
* DTO containing the list of all non-static proxy properties and utility methods to access them
* in various formats/collections
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*/
final class Properties
{
/**
* @var array|\ReflectionProperty[]
*/
private $properties;
/**
* @param ReflectionProperty[] $properties
*/
private function __construct(array $properties)
{
$this->properties = $properties;
}
public static function fromReflectionClass(ReflectionClass $reflection) : self
{
$class = $reflection;
$properties = [];
do {
$properties = array_merge(
$properties,
array_values(array_filter(
$class->getProperties(),
function (ReflectionProperty $property) use ($class) : bool {
return $class->getName() === $property->getDeclaringClass()->getName()
&& ! $property->isStatic();
}
))
);
} while ($class = $class->getParentClass());
return new self($properties);
}
/**
* @param string[] $excludedProperties
*
* @return self
*/
public function filter(array $excludedProperties) : self
{
$properties = $this->getInstanceProperties();
foreach ($excludedProperties as $propertyName) {
unset($properties[$propertyName]);
}
return new self($properties);
}
/**
* @return ReflectionProperty[] indexed by the property internal visibility-aware name
*/
public function getPublicProperties() : array
{
$publicProperties = [];
foreach ($this->properties as $property) {
if ($property->isPublic()) {
$publicProperties[$property->getName()] = $property;
}
}
return $publicProperties;
}
/**
* @return ReflectionProperty[] indexed by the property internal visibility-aware name (\0*\0propertyName)
*/
public function getProtectedProperties() : array
{
$protectedProperties = [];
foreach ($this->properties as $property) {
if ($property->isProtected()) {
$protectedProperties["\0*\0" . $property->getName()] = $property;
}
}
return $protectedProperties;
}
/**
* @return ReflectionProperty[] indexed by the property internal visibility-aware name (\0ClassName\0propertyName)
*/
public function getPrivateProperties() : array
{
$privateProperties = [];
foreach ($this->properties as $property) {
if ($property->isPrivate()) {
$declaringClass = $property->getDeclaringClass()->getName();
$privateProperties["\0" . $declaringClass . "\0" . $property->getName()] = $property;
}
}
return $privateProperties;
}
/**
* @return ReflectionProperty[] indexed by the property internal visibility-aware name (\0*\0propertyName)
*/
public function getAccessibleProperties() : array
{
return array_merge($this->getPublicProperties(), $this->getProtectedProperties());
}
/**
* @return ReflectionProperty[][] indexed by class name and property name
*/
public function getGroupedPrivateProperties() : array
{
$propertiesMap = [];
foreach ($this->getPrivateProperties() as $property) {
$class = & $propertiesMap[$property->getDeclaringClass()->getName()];
$class[$property->getName()] = $property;
}
return $propertiesMap;
}
/**
* @return ReflectionProperty[] indexed by the property internal visibility-aware name (\0*\0propertyName)
*/
public function getInstanceProperties() : array
{
return array_merge($this->getAccessibleProperties(), $this->getPrivateProperties());
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\Util;
use ReflectionClass;
@@ -27,30 +29,74 @@ use ReflectionMethod;
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*/
class ProxiedMethodsFilter
final class ProxiedMethodsFilter
{
/**
* @var string[]
*/
private static $defaultExcluded = [
'__get',
'__set',
'__isset',
'__unset',
'__clone',
'__sleep',
'__wakeup',
];
/**
* @param ReflectionClass $class reflection class from which methods should be extracted
* @param string[] $excluded methods to be ignored
*
* @return ReflectionMethod[]
*/
public static function getProxiedMethods(
ReflectionClass $class,
array $excluded = array('__get', '__set', '__isset', '__unset', '__clone', '__sleep', '__wakeup')
) {
public static function getProxiedMethods(ReflectionClass $class, array $excluded = null) : array
{
return self::doFilter($class, (null === $excluded) ? self::$defaultExcluded : $excluded);
}
/**
* @param ReflectionClass $class reflection class from which methods should be extracted
* @param string[] $excluded methods to be ignored
*
* @return ReflectionMethod[]
*/
public static function getAbstractProxiedMethods(ReflectionClass $class, array $excluded = null) : array
{
return self::doFilter($class, (null === $excluded) ? self::$defaultExcluded : $excluded, true);
}
/**
* @param ReflectionClass $class
* @param string[] $excluded
* @param bool $requireAbstract
*
* @return ReflectionMethod[]
*/
private static function doFilter(ReflectionClass $class, array $excluded, bool $requireAbstract = false) : array
{
$ignored = array_flip(array_map('strtolower', $excluded));
return array_filter(
$class->getMethods(ReflectionMethod::IS_PUBLIC),
function (ReflectionMethod $method) use ($ignored) {
return ! (
$method->isConstructor()
|| isset($ignored[strtolower($method->getName())])
|| $method->isFinal()
|| $method->isStatic()
function (ReflectionMethod $method) use ($ignored, $requireAbstract) : bool {
return (! $requireAbstract || $method->isAbstract()) && ! (
\array_key_exists(strtolower($method->getName()), $ignored)
|| self::methodCannotBeProxied($method)
);
}
);
}
/**
* Checks whether the method cannot be proxied
*
* @param ReflectionMethod $method
*
* @return bool
*/
private static function methodCannotBeProxied(ReflectionMethod $method) : bool
{
return $method->isConstructor() || $method->isFinal() || $method->isStatic();
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\Util;
use Zend\Code\Generator\PropertyGenerator;
@@ -53,12 +55,12 @@ class PublicScopeSimulator
* @throws \InvalidArgumentException
*/
public static function getPublicAccessSimulationCode(
$operationType,
$nameParameter,
string $operationType,
string $nameParameter,
$valueParameter = null,
PropertyGenerator $valueHolder = null,
$returnPropertyName = null
) {
) : string {
$byRef = self::getByRefReturnValue($operationType);
$value = static::OPERATION_SET === $operationType ? ', $value' : '';
$target = '$this';
@@ -71,7 +73,7 @@ class PublicScopeSimulator
. 'if (! $realInstanceReflection->hasProperty($' . $nameParameter . ')) {' . "\n"
. ' $targetObject = ' . $target . ';' . "\n\n"
. self::getUndefinedPropertyNotice($operationType, $nameParameter)
. ' ' . self::getOperation($operationType, $nameParameter, $valueParameter) . ";\n"
. ' ' . self::getOperation($operationType, $nameParameter, $valueParameter) . "\n"
. " return;\n"
. '}' . "\n\n"
. '$targetObject = ' . self::getTargetObject($valueHolder) . ";\n"
@@ -94,18 +96,23 @@ class PublicScopeSimulator
*
* @return string
*/
private static function getUndefinedPropertyNotice($operationType, $nameParameter)
private static function getUndefinedPropertyNotice(string $operationType, string $nameParameter) : string
{
if (static::OPERATION_GET !== $operationType) {
return '';
}
//
return ' $backtrace = debug_backtrace(false);' . "\n"
. ' trigger_error(\'Undefined property: \' . get_parent_class($this) . \'::$\' . $'
. $nameParameter
. ' . \' in \' . $backtrace[0][\'file\'] . \' on line \' . $backtrace[0][\'line\'], \E_USER_NOTICE);'
. "\n";
. ' trigger_error(' . "\n"
. ' sprintf(' . "\n"
. ' \'Undefined property: %s::$%s in %s on line %s\',' . "\n"
. ' get_parent_class($this),' . "\n"
. ' $' . $nameParameter . ',' . "\n"
. ' $backtrace[0][\'file\'],' . "\n"
. ' $backtrace[0][\'line\']' . "\n"
. ' ),' . "\n"
. ' \E_USER_NOTICE' . "\n"
. ' );' . "\n";
}
/**
@@ -119,7 +126,7 @@ class PublicScopeSimulator
*
* @return string
*/
private static function getByRefReturnValue($operationType)
private static function getByRefReturnValue(string $operationType) : string
{
return (static::OPERATION_GET === $operationType || static::OPERATION_SET === $operationType) ? '& ' : '';
}
@@ -131,7 +138,7 @@ class PublicScopeSimulator
*
* @return string
*/
private static function getTargetObject(PropertyGenerator $valueHolder = null)
private static function getTargetObject(PropertyGenerator $valueHolder = null) : string
{
if ($valueHolder) {
return '$this->' . $valueHolder->getName();
@@ -149,11 +156,11 @@ class PublicScopeSimulator
*
* @throws \InvalidArgumentException
*/
private static function getOperation($operationType, $nameParameter, $valueParameter)
private static function getOperation(string $operationType, string $nameParameter, $valueParameter) : string
{
switch ($operationType) {
case static::OPERATION_GET:
return 'return $targetObject->$' . $nameParameter . ";";
return 'return $targetObject->$' . $nameParameter . ';';
case static::OPERATION_SET:
if (! $valueParameter) {
throw new \InvalidArgumentException('Parameter $valueParameter not provided');
@@ -170,21 +177,15 @@ class PublicScopeSimulator
}
/**
* Generates code to bind operations to the parent scope if supported by the current PHP implementation
* Generates code to bind operations to the parent scope
*
* @return string
*/
private static function getScopeReBind()
private static function getScopeReBind() : string
{
if (! method_exists('Closure', 'bind')) {
// @codeCoverageIgnoreStart
return '';
// @codeCoverageIgnoreEnd
}
return ' $backtrace = debug_backtrace(true);' . "\n"
. ' $scopeObject = isset($backtrace[1][\'object\'])'
. ' ? $backtrace[1][\'object\'] : new \stdClass();' . "\n"
. ' $accessor = $accessor->bindTo($scopeObject, get_class($scopeObject));' . "\n";
return '$backtrace = debug_backtrace(true);' . "\n"
. '$scopeObject = isset($backtrace[1][\'object\'])'
. ' ? $backtrace[1][\'object\'] : new \ProxyManager\Stub\EmptyClassStub();' . "\n"
. '$accessor = $accessor->bindTo($scopeObject, get_class($scopeObject));' . "\n";
}
}

View File

@@ -0,0 +1,110 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\Util;
/**
* Generates code necessary to unset all the given properties from a particular given instance string name
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*/
final class UnsetPropertiesGenerator
{
private static $closureTemplate = <<<'PHP'
\Closure::bind(function (\%s $instance) {
%s
}, $%s, %s)->__invoke($%s);
PHP;
public static function generateSnippet(Properties $properties, string $instanceName) : string
{
return self::generateUnsetAccessiblePropertiesCode($properties, $instanceName)
. self::generateUnsetPrivatePropertiesCode($properties, $instanceName);
}
private static function generateUnsetAccessiblePropertiesCode(Properties $properties, string $instanceName) : string
{
$accessibleProperties = $properties->getAccessibleProperties();
if (! $accessibleProperties) {
return '';
}
return self::generateUnsetStatement($accessibleProperties, $instanceName) . "\n\n";
}
private static function generateUnsetPrivatePropertiesCode(Properties $properties, string $instanceName) : string
{
$groups = $properties->getGroupedPrivateProperties();
if (! $groups) {
return '';
}
$unsetClosureCalls = [];
/* @var $privateProperties \ReflectionProperty[] */
foreach ($groups as $privateProperties) {
/* @var $firstProperty \ReflectionProperty */
$firstProperty = reset($privateProperties);
$unsetClosureCalls[] = self::generateUnsetClassPrivatePropertiesBlock(
$firstProperty->getDeclaringClass(),
$privateProperties,
$instanceName
);
}
return implode("\n\n", $unsetClosureCalls) . "\n\n";
}
private static function generateUnsetClassPrivatePropertiesBlock(
\ReflectionClass $declaringClass,
array $properties,
string $instanceName
) : string {
$declaringClassName = $declaringClass->getName();
return sprintf(
self::$closureTemplate,
$declaringClassName,
self::generateUnsetStatement($properties, 'instance'),
$instanceName,
var_export($declaringClassName, true),
$instanceName
);
}
private static function generateUnsetStatement(array $properties, string $instanceName) : string
{
return 'unset('
. implode(
', ',
array_map(
function (\ReflectionProperty $property) use ($instanceName) : string {
return '$' . $instanceName . '->' . $property->getName();
},
$properties
)
)
. ');';
}
}

View File

@@ -0,0 +1,123 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\ValueHolder\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
use ProxyManager\ProxyGenerator\Util\Properties;
use ProxyManager\ProxyGenerator\Util\UnsetPropertiesGenerator;
use ReflectionClass;
use Zend\Code\Generator\ParameterGenerator;
use Zend\Code\Generator\PropertyGenerator;
use Zend\Code\Reflection\MethodReflection;
/**
* The `__construct` implementation for lazy loading proxies
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*/
class Constructor extends MethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
* @param PropertyGenerator $valueHolder
*
* @return self
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public static function generateMethod(ReflectionClass $originalClass, PropertyGenerator $valueHolder) : self
{
$originalConstructor = self::getConstructor($originalClass);
$constructor = $originalConstructor
? self::fromReflection($originalConstructor)
: new self('__construct');
$constructor->setDocBlock('{@inheritDoc}');
$constructor->setBody(
'static $reflection;' . "\n\n"
. 'if (! $this->' . $valueHolder->getName() . ') {' . "\n"
. ' $reflection = $reflection ?: new \ReflectionClass('
. var_export($originalClass->getName(), true)
. ");\n"
. ' $this->' . $valueHolder->getName() . ' = $reflection->newInstanceWithoutConstructor();' . "\n"
. UnsetPropertiesGenerator::generateSnippet(Properties::fromReflectionClass($originalClass), 'this')
. '}'
. self::generateOriginalConstructorCall($originalClass, $valueHolder)
);
return $constructor;
}
private static function generateOriginalConstructorCall(
ReflectionClass $class,
PropertyGenerator $valueHolder
) : string {
$originalConstructor = self::getConstructor($class);
if (! $originalConstructor) {
return '';
}
$constructor = self::fromReflection($originalConstructor);
return "\n\n"
. '$this->' . $valueHolder->getName() . '->' . $constructor->getName() . '('
. implode(
', ',
array_map(
function (ParameterGenerator $parameter) : string {
return ($parameter->getVariadic() ? '...' : '') . '$' . $parameter->getName();
},
$constructor->getParameters()
)
)
. ');';
}
/**
* @param ReflectionClass $class
*
* @return MethodReflection|null
*/
private static function getConstructor(ReflectionClass $class)
{
$constructors = array_map(
function (\ReflectionMethod $method) : MethodReflection {
return new MethodReflection(
$method->getDeclaringClass()->getName(),
$method->getName()
);
},
array_filter(
$class->getMethods(),
function (\ReflectionMethod $method) : bool {
return $method->isConstructor();
}
)
);
return reset($constructors) ?: null;
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\ValueHolder\MethodGenerator;
use ProxyManager\Generator\MethodGenerator;
@@ -32,11 +34,15 @@ class GetWrappedValueHolderValue extends MethodGenerator
{
/**
* Constructor
*
* @param PropertyGenerator $valueHolderProperty
*
* @throws \Zend\Code\Generator\Exception\InvalidArgumentException
*/
public function __construct(PropertyGenerator $valueHolderProperty)
{
parent::__construct('getWrappedValueHolderValue');
$this->setDocblock('{@inheritDoc}');
$this->setDocBlock('{@inheritDoc}');
$this->setBody('return $this->' . $valueHolderProperty->getName() . ';');
}
}

View File

@@ -16,6 +16,8 @@
* and is licensed under the MIT license.
*/
declare(strict_types=1);
namespace ProxyManager\ProxyGenerator\ValueHolder\MethodGenerator;
use ProxyManager\Generator\MagicMethodGenerator;
@@ -32,6 +34,9 @@ class MagicSleep extends MagicMethodGenerator
{
/**
* Constructor
*
* @param ReflectionClass $originalClass
* @param PropertyGenerator $valueHolderProperty
*/
public function __construct(ReflectionClass $originalClass, PropertyGenerator $valueHolderProperty)
{