From ffa590188a4d77b30a3d71e8f1f6e1db858ba5f7 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 21 Nov 2016 23:52:50 +0100 Subject: [PATCH 1/7] #346 Test asset: class with heavily hinted magic methods --- .../ClassWithHintedMagicMethods.php | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 tests/ProxyManagerTestAsset/ClassWithHintedMagicMethods.php diff --git a/tests/ProxyManagerTestAsset/ClassWithHintedMagicMethods.php b/tests/ProxyManagerTestAsset/ClassWithHintedMagicMethods.php new file mode 100644 index 000000000..68ed34557 --- /dev/null +++ b/tests/ProxyManagerTestAsset/ClassWithHintedMagicMethods.php @@ -0,0 +1,63 @@ + + * @license MIT + */ +class ClassWithHintedMagicMethods +{ + public function __set(string $name, bool $value) : array + { + return [$name => $value]; + } + + public function __get(string $name) : string + { + return $name; + } + + public function __isset(string $name) : bool + { + return (bool) $name; + } + + public function __unset(string $name) : bool + { + return (bool) $name; + } + + public function __sleep() : array + { + return []; + } + + public function __wakeup() : void + { + } + + public function __clone() + { + } +} From 2e3bac17de2bd40bb828a3f6f4f8ce731d55f48a Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 21 Nov 2016 23:53:00 +0100 Subject: [PATCH 2/7] #346 Test asset: interface with heavily hinted magic methods --- .../InterfaceWithHintedMagicMethods.php | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/ProxyManagerTestAsset/InterfaceWithHintedMagicMethods.php diff --git a/tests/ProxyManagerTestAsset/InterfaceWithHintedMagicMethods.php b/tests/ProxyManagerTestAsset/InterfaceWithHintedMagicMethods.php new file mode 100644 index 000000000..ea6b24edc --- /dev/null +++ b/tests/ProxyManagerTestAsset/InterfaceWithHintedMagicMethods.php @@ -0,0 +1,38 @@ + + * @license MIT + */ +interface InterfaceWithHintedMagicMethods +{ + public function __set(string $name, bool $value) : array; + public function __get(string $name) : string; + public function __isset(string $name) : bool; + public function __unset(string $name) : bool; + public function __sleep() : array; + public function __wakeup() : void; + public function __clone(); +} From d8e3e018aabe47b64725eb02e2040cd32ab34c6c Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 21 Nov 2016 23:53:36 +0100 Subject: [PATCH 3/7] #346 Test: code generation should succeed also with heavily hinted magic methods, as hints should be preserved --- .../Functional/MultipleProxyGenerationTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/ProxyManagerTest/Functional/MultipleProxyGenerationTest.php b/tests/ProxyManagerTest/Functional/MultipleProxyGenerationTest.php index 3840f18ef..119bb64aa 100644 --- a/tests/ProxyManagerTest/Functional/MultipleProxyGenerationTest.php +++ b/tests/ProxyManagerTest/Functional/MultipleProxyGenerationTest.php @@ -34,6 +34,7 @@ use ProxyManagerTestAsset\ClassWithCollidingPrivateInheritedProperties; use ProxyManagerTestAsset\ClassWithFinalMagicMethods; use ProxyManagerTestAsset\ClassWithFinalMethods; +use ProxyManagerTestAsset\ClassWithHintedMagicMethods; use ProxyManagerTestAsset\ClassWithMagicMethods; use ProxyManagerTestAsset\ClassWithMethodWithByRefVariadicFunction; use ProxyManagerTestAsset\ClassWithMethodWithVariadicFunction; @@ -45,6 +46,7 @@ use ProxyManagerTestAsset\ClassWithSelfHint; use ProxyManagerTestAsset\EmptyClass; use ProxyManagerTestAsset\HydratedObject; +use ProxyManagerTestAsset\InterfaceWithHintedMagicMethods; use ProxyManagerTestAsset\IterableTypeHintClass; use ProxyManagerTestAsset\ReturnTypeHintedClass; use ProxyManagerTestAsset\ScalarTypeHintedClass; @@ -119,6 +121,8 @@ public function getTestedClasses() : array return [ [BaseClass::class], [ClassWithMagicMethods::class], + [ClassWithHintedMagicMethods::class], + //[InterfaceWithHintedMagicMethods::class], [ClassWithFinalMethods::class], [ClassWithFinalMagicMethods::class], [ClassWithByRefMagicMethods::class], From ce5aa9bf5bfed50158dd4fdd097f1f9e609967ea Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 22 Nov 2016 01:16:50 +0100 Subject: [PATCH 4/7] #346 modified `PublicScopeSimulator` to take code generation of return blocks into account (varies depending on `void` or non-void return hint) --- .../Util/PublicScopeSimulator.php | 58 ++++++++++--------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/Util/PublicScopeSimulator.php b/src/ProxyManager/ProxyGenerator/Util/PublicScopeSimulator.php index 253b73a31..93a08684d 100644 --- a/src/ProxyManager/ProxyGenerator/Util/PublicScopeSimulator.php +++ b/src/ProxyManager/ProxyGenerator/Util/PublicScopeSimulator.php @@ -20,6 +20,7 @@ namespace ProxyManager\ProxyGenerator\Util; +use ProxyManager\Generator\Util\ProxiedMethodReturnExpression; use Zend\Code\Generator\PropertyGenerator; /** @@ -42,19 +43,21 @@ class PublicScopeSimulator * This is done by introspecting `debug_backtrace()` and then binding a closure to the scope * of the parent caller. * - * @param string $operationType operation to execute: one of 'get', 'set', 'isset' or 'unset' - * @param string $nameParameter name of the `name` parameter of the magic method - * @param string|null $valueParameter name of the `value` parameter of the magic method - * @param PropertyGenerator $valueHolder name of the property containing the target object from which - * to read the property. `$this` if none provided - * @param string|null $returnPropertyName name of the property to which we want to assign the result of - * the operation. Return directly if none provided + * @param \ReflectionMethod|null $originalMethod the original method, if it exists + * @param string $operationType operation to execute: one of 'get', 'set', 'isset' or 'unset' + * @param string $nameParameter name of the `name` parameter of the magic method + * @param string|null $valueParameter name of the `value` parameter of the magic method + * @param PropertyGenerator $valueHolder name of the property containing the target object from which + * to read the property. `$this` if none provided + * @param string|null $returnPropertyName name of the property to which we want to assign the result of + * the operation. Return directly if none provided * * @return string * * @throws \InvalidArgumentException */ public static function getPublicAccessSimulationCode( + ?\ReflectionMethod $originalMethod, string $operationType, string $nameParameter, $valueParameter = null, @@ -73,12 +76,11 @@ public static function getPublicAccessSimulationCode( . 'if (! $realInstanceReflection->hasProperty($' . $nameParameter . ')) {' . "\n" . ' $targetObject = ' . $target . ';' . "\n\n" . self::getUndefinedPropertyNotice($operationType, $nameParameter) - . ' ' . self::getOperation($operationType, $nameParameter, $valueParameter) . "\n" - . " return;\n" + . ' ' . self::getOperation($originalMethod, $operationType, $nameParameter, $valueParameter) . "\n" . '}' . "\n\n" . '$targetObject = ' . self::getTargetObject($valueHolder) . ";\n" . '$accessor = function ' . $byRef . '() use ($targetObject, $name' . $value . ') {' . "\n" - . ' ' . self::getOperation($operationType, $nameParameter, $valueParameter) . "\n" + . ' ' . self::getOperation($originalMethod, $operationType, $nameParameter, $valueParameter) . "\n" . "};\n" . self::getScopeReBind() . ( @@ -134,11 +136,11 @@ private static function getByRefReturnValue(string $operationType) : string /** * Retrieves the logic to fetch the object on which access should be attempted * - * @param PropertyGenerator $valueHolder + * @param PropertyGenerator|null $valueHolder * * @return string */ - private static function getTargetObject(PropertyGenerator $valueHolder = null) : string + private static function getTargetObject(?PropertyGenerator $valueHolder) : string { if ($valueHolder) { return '$this->' . $valueHolder->getName(); @@ -147,30 +149,32 @@ private static function getTargetObject(PropertyGenerator $valueHolder = null) : return 'unserialize(sprintf(\'O:%d:"%s":0:{}\', strlen(get_parent_class($this)), get_parent_class($this)))'; } - /** - * @param string $operationType - * @param string $nameParameter - * @param string|null $valueParameter - * - * @return string - * - * @throws \InvalidArgumentException - */ - private static function getOperation(string $operationType, string $nameParameter, $valueParameter) : string - { + private static function getOperation( + ?\ReflectionMethod $originalMethod, + string $operationType, + string $nameParameter, + ?string $valueParameter + ) : string { switch ($operationType) { case static::OPERATION_GET: - return 'return $targetObject->$' . $nameParameter . ';'; + return ProxiedMethodReturnExpression::generate('$targetObject->$' . $nameParameter, $originalMethod); case static::OPERATION_SET: if (! $valueParameter) { throw new \InvalidArgumentException('Parameter $valueParameter not provided'); } - return 'return $targetObject->$' . $nameParameter . ' = $' . $valueParameter . ';'; + return ProxiedMethodReturnExpression::generate( + '$targetObject->$' . $nameParameter . ' = $' . $valueParameter, + $originalMethod + ); case static::OPERATION_ISSET: - return 'return isset($targetObject->$' . $nameParameter . ');'; + return ProxiedMethodReturnExpression::generate( + 'isset($targetObject->$' . $nameParameter . ')', + $originalMethod + ); case static::OPERATION_UNSET: - return 'unset($targetObject->$' . $nameParameter . ');'; + return 'unset($targetObject->$' . $nameParameter . ');' + . ProxiedMethodReturnExpression::generate('true', $originalMethod); } throw new \InvalidArgumentException(sprintf('Invalid operation "%s" provided', $operationType)); From 9063ae4fd1bbd6e89d1c9d803b4a206492570952 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 22 Nov 2016 01:17:47 +0100 Subject: [PATCH 5/7] #346 `MagicMethodGenerator` now preserves parameter and return type hint of a previously existing class, if given, and replaces names only --- .../Generator/MagicMethodGenerator.php | 55 +++++++++++++++---- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/src/ProxyManager/Generator/MagicMethodGenerator.php b/src/ProxyManager/Generator/MagicMethodGenerator.php index 328ba1336..b8f8def4b 100644 --- a/src/ProxyManager/Generator/MagicMethodGenerator.php +++ b/src/ProxyManager/Generator/MagicMethodGenerator.php @@ -21,6 +21,8 @@ namespace ProxyManager\Generator; use ReflectionClass; +use Zend\Code\Generator\ParameterGenerator; +use Zend\Code\Reflection\ParameterReflection; /** * Method generator for magic methods @@ -31,24 +33,55 @@ class MagicMethodGenerator extends MethodGenerator { /** - * @param ReflectionClass $originalClass - * @param string $name - * @param array $parameters + * @param ReflectionClass $originalClass + * @param string $name + * @param ParameterGenerator[] $parameters + * + * @throws \Zend\Code\Generator\Exception\InvalidArgumentException */ public function __construct(ReflectionClass $originalClass, string $name, array $parameters = []) { - parent::__construct( - $name, - $parameters, - static::FLAG_PUBLIC, - null, - $originalClass->hasMethod($name) ? '{@inheritDoc}' : null - ); + parent::__construct($name, [], static::FLAG_PUBLIC); $this->setReturnsReference(strtolower($name) === '__get'); if ($originalClass->hasMethod($name)) { - $this->setReturnsReference($originalClass->getMethod($name)->returnsReference()); + $originalMethod = $originalClass->getMethod($name); + + $this->setDocBlock('{@inheritDoc}'); + $this->setReturnsReference($originalMethod->returnsReference()); + $this->setReturnType(((string) $originalMethod->getReturnType()) ?: null); + $this->addOrReplaceParameterNames($originalMethod, ...$parameters); + } else { + $this->setParameters($parameters); } } + + private function addOrReplaceParameterNames( + \ReflectionMethod $originalMethod, + ParameterGenerator ...$newParameters + ) : void { + $originalParameters = array_values(array_map( + function (\ReflectionParameter $parameter) use ($originalMethod) : ParameterGenerator { + return ParameterGenerator::fromReflection(new ParameterReflection( + [$originalMethod->getDeclaringClass()->getName(), $originalMethod->getName()], + $parameter->getPosition() + )); + }, + $originalMethod->getParameters() + )); + + $parametersCount = max(count($originalParameters), count($newParameters)); + $parameters = []; + + for ($idx = 0; $idx < $parametersCount; $idx += 1) { + $parameter = $originalParameters[$idx] ?? $newParameters[$idx]; + + $parameter->setName(($newParameters[$idx] ?? $originalParameters[$idx])->getName()); + + $parameters[] = $parameter; + } + + $this->setParameters($parameters); + } } From acbc96fc205fa1955d80da74f22d997ad8e2bb8a Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 22 Nov 2016 01:18:16 +0100 Subject: [PATCH 6/7] #346 Adapting currently existing code to the `PublicScopeSimulator` code generator API --- .../MethodGenerator/MagicGet.php | 1 + .../MethodGenerator/MagicIsset.php | 1 + .../MethodGenerator/MagicSet.php | 1 + .../MethodGenerator/MagicUnset.php | 1 + .../MethodGenerator/MagicGet.php | 1 + .../MethodGenerator/MagicIsset.php | 1 + .../MethodGenerator/MagicSet.php | 1 + .../MethodGenerator/MagicUnset.php | 1 + .../MethodGenerator/MagicGet.php | 1 + .../MethodGenerator/MagicIsset.php | 1 + .../MethodGenerator/MagicSet.php | 1 + .../MethodGenerator/MagicUnset.php | 75 +++++++++++-------- .../MethodGenerator/MagicGet.php | 3 +- .../MethodGenerator/MagicIsset.php | 4 +- .../MethodGenerator/MagicSet.php | 3 +- .../MethodGenerator/MagicUnset.php | 3 +- 16 files changed, 63 insertions(+), 36 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicGet.php b/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicGet.php index 8f95e912f..6d5846ece 100644 --- a/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicGet.php +++ b/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicGet.php @@ -59,6 +59,7 @@ public function __construct( if (! $parent) { $callParent = PublicScopeSimulator::getPublicAccessSimulationCode( + $parent, PublicScopeSimulator::OPERATION_GET, 'name', null, diff --git a/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicIsset.php b/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicIsset.php index 01774d3ce..cccace0b0 100644 --- a/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicIsset.php +++ b/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicIsset.php @@ -59,6 +59,7 @@ public function __construct( if (! $parent) { $callParent = PublicScopeSimulator::getPublicAccessSimulationCode( + $parent, PublicScopeSimulator::OPERATION_ISSET, 'name', null, diff --git a/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicSet.php b/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicSet.php index 2e1181a1e..a9e8a15c5 100644 --- a/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicSet.php +++ b/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicSet.php @@ -63,6 +63,7 @@ public function __construct( if (! $parent) { $callParent = PublicScopeSimulator::getPublicAccessSimulationCode( + $parent, PublicScopeSimulator::OPERATION_SET, 'name', 'value', diff --git a/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicUnset.php b/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicUnset.php index 26315ed9f..3d40e48f4 100644 --- a/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicUnset.php +++ b/src/ProxyManager/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicUnset.php @@ -59,6 +59,7 @@ public function __construct( if (! $parent) { $callParent = PublicScopeSimulator::getPublicAccessSimulationCode( + $parent, PublicScopeSimulator::OPERATION_UNSET, 'name', null, diff --git a/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicGet.php b/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicGet.php index df47b8ba2..9e0136929 100644 --- a/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicGet.php +++ b/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicGet.php @@ -64,6 +64,7 @@ public function __construct( $this->setDocBlock(($parent ? "{@inheritDoc}\n" : '') . '@param string $name'); $callParent = PublicScopeSimulator::getPublicAccessSimulationCode( + $parent, PublicScopeSimulator::OPERATION_GET, 'name', 'value', diff --git a/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicIsset.php b/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicIsset.php index c9e66ca3d..18d91a48b 100644 --- a/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicIsset.php +++ b/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicIsset.php @@ -63,6 +63,7 @@ public function __construct( $this->setDocBlock(($parent ? "{@inheritDoc}\n" : '') . '@param string $name'); $callParent = PublicScopeSimulator::getPublicAccessSimulationCode( + $parent, PublicScopeSimulator::OPERATION_ISSET, 'name', 'value', diff --git a/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicSet.php b/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicSet.php index 61fee6488..730990564 100644 --- a/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicSet.php +++ b/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicSet.php @@ -67,6 +67,7 @@ public function __construct( $this->setDocBlock(($parent ? "{@inheritDoc}\n" : '') . '@param string $name'); $callParent = PublicScopeSimulator::getPublicAccessSimulationCode( + $parent, PublicScopeSimulator::OPERATION_SET, 'name', 'value', diff --git a/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicUnset.php b/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicUnset.php index 9fb6a3ec6..130da3371 100644 --- a/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicUnset.php +++ b/src/ProxyManager/ProxyGenerator/AccessInterceptorValueHolder/MethodGenerator/MagicUnset.php @@ -63,6 +63,7 @@ public function __construct( $this->setDocBlock(($parent ? "{@inheritDoc}\n" : '') . '@param string $name'); $callParent = PublicScopeSimulator::getPublicAccessSimulationCode( + $parent, PublicScopeSimulator::OPERATION_UNSET, 'name', 'value', diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php index b02c6d9f4..17ee9b684 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php @@ -135,6 +135,7 @@ public function __construct( if (! $override) { $parentAccess = PublicScopeSimulator::getPublicAccessSimulationCode( + null, PublicScopeSimulator::OPERATION_GET, 'name' ); diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php index 516fd7b32..1f4c0f650 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php @@ -128,6 +128,7 @@ public function __construct( if (! $override) { $parentAccess = PublicScopeSimulator::getPublicAccessSimulationCode( + null, PublicScopeSimulator::OPERATION_ISSET, 'name' ); diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php index 72f97e717..4a450150f 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php @@ -132,6 +132,7 @@ public function __construct( if (! $override) { $parentAccess = PublicScopeSimulator::getPublicAccessSimulationCode( + null, PublicScopeSimulator::OPERATION_SET, 'name', 'value' diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php index 20e352f34..a103add55 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php @@ -21,6 +21,7 @@ namespace ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator; use ProxyManager\Generator\MagicMethodGenerator; +use ProxyManager\Generator\Util\ProxiedMethodReturnExpression; use Zend\Code\Generator\ParameterGenerator; use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PrivatePropertiesMap; use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\ProtectedPropertiesMap; @@ -42,25 +43,25 @@ class MagicUnset extends MagicMethodGenerator * @var string */ private $callParentTemplate = <<<'PHP' -%s +%init -if (isset(self::$%s[$name])) { +if (isset(self::$%publicProperties[$name])) { unset($this->$name); - return; + %voidReturn1 } -if (isset(self::$%s[$name])) { +if (isset(self::$%protectedProperties1[$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]; + $expectedType = self::$%protectedProperties2[$name]; if ($object instanceof $expectedType) { unset($this->$name); - return; + %voidReturn2 } $class = isset($caller['class']) ? $caller['class'] : ''; @@ -68,9 +69,9 @@ class MagicUnset extends MagicMethodGenerator if ($class === $expectedType || is_subclass_of($class, $expectedType) || $class === 'ReflectionProperty') { unset($this->$name); - return; + %voidReturn3 } -} elseif (isset(self::$%s[$name])) { +} elseif (isset(self::$%privateProperties1[$name])) { // check private property access via same class $callers = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 2); $caller = isset($callers[1]) ? $callers[1] : []; @@ -78,7 +79,7 @@ class MagicUnset extends MagicMethodGenerator static $accessorCache = []; - if (isset(self::$%s[$name][$class])) { + if (isset(self::$%privateProperties2[$name][$class])) { $cacheKey = $class . '#' . $name; $accessor = isset($accessorCache[$cacheKey]) ? $accessorCache[$cacheKey] @@ -86,11 +87,11 @@ class MagicUnset extends MagicMethodGenerator unset($instance->$name); }, null, $class); - return $accessor($this); + %accessorReturn1 } if ('ReflectionProperty' === $class) { - $tmpClass = key(self::$%s[$name]); + $tmpClass = key(self::$%privateProperties3[$name]); $cacheKey = $tmpClass . '#' . $name; $accessor = isset($accessorCache[$cacheKey]) ? $accessorCache[$cacheKey] @@ -98,11 +99,11 @@ class MagicUnset extends MagicMethodGenerator unset($instance->$name); }, null, $tmpClass); - return $accessor($this); + %accessorReturn2 } } -%s +%parentAccess PHP; /** @@ -126,30 +127,40 @@ public function __construct( ) { parent::__construct($originalClass, '__unset', [new ParameterGenerator('name')]); - $override = $originalClass->hasMethod('__unset'); + $existingMethod = $originalClass->hasMethod('__unset') ? $originalClass->getMethod('__unset') : null; - $this->setDocBlock(($override ? "{@inheritDoc}\n" : '') . '@param string $name'); + $this->setDocBlock(($existingMethod ? "{@inheritDoc}\n" : '') . '@param string $name'); - $parentAccess = 'return parent::__unset($name);'; - - if (! $override) { - $parentAccess = PublicScopeSimulator::getPublicAccessSimulationCode( + $parentAccess = $existingMethod + ? ProxiedMethodReturnExpression::generate('parent::__unset($name)', $existingMethod) + : PublicScopeSimulator::getPublicAccessSimulationCode( + $existingMethod, PublicScopeSimulator::OPERATION_UNSET, 'name' ); - } - - $this->setBody(sprintf( - $this->callParentTemplate, - '$this->' . $initializerProperty->getName() . ' && $this->' . $callInitializer->getName() - . '(\'__unset\', array(\'name\' => $name));', - $publicProperties->getName(), - $protectedProperties->getName(), - $protectedProperties->getName(), - $privateProperties->getName(), - $privateProperties->getName(), - $privateProperties->getName(), - $parentAccess + + $symbols = [ + '%init' => '$this->' . $initializerProperty->getName() + . ' && $this->' . $callInitializer->getName() + . '(\'__unset\', array(\'name\' => $name));', + '%publicProperties' => $publicProperties->getName(), + '%protectedProperties1' => $protectedProperties->getName(), + '%protectedProperties2' => $protectedProperties->getName(), + '%privateProperties1' => $privateProperties->getName(), + '%privateProperties2' => $privateProperties->getName(), + '%privateProperties3' => $privateProperties->getName(), + '%parentAccess' => $parentAccess, + '%voidReturn1' => ProxiedMethodReturnExpression::generate('true', $existingMethod), + '%voidReturn2' => ProxiedMethodReturnExpression::generate('true', $existingMethod), + '%voidReturn3' => ProxiedMethodReturnExpression::generate('true', $existingMethod), + '%accessorReturn1' => ProxiedMethodReturnExpression::generate('$accessor($this)', $existingMethod), + '%accessorReturn2' => ProxiedMethodReturnExpression::generate('$accessor($this)', $existingMethod), + ]; + + $this->setBody(str_replace( + array_keys($symbols), + array_values($symbols), + $this->callParentTemplate )); } } diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicGet.php b/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicGet.php index e4e6648aa..3a3079b2d 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicGet.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicGet.php @@ -54,7 +54,7 @@ public function __construct( ) { parent::__construct($originalClass, '__get', [new ParameterGenerator('name')]); - $hasParent = $originalClass->hasMethod('__get'); + $hasParent = $originalClass->hasMethod('__get') ? $originalClass->getMethod('__get') : null; $this->setDocBlock(($hasParent ? "{@inheritDoc}\n" : '') . '@param string $name'); @@ -78,6 +78,7 @@ public function __construct( $initializer, $valueHolder, $callParent . PublicScopeSimulator::getPublicAccessSimulationCode( + $hasParent, PublicScopeSimulator::OPERATION_GET, 'name', null, diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicIsset.php b/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicIsset.php index 9ebc1def6..d662b2504 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicIsset.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicIsset.php @@ -57,8 +57,9 @@ public function __construct( $initializer = $initializerProperty->getName(); $valueHolder = $valueHolderProperty->getName(); $callParent = ''; + $parent = $originalClass->hasMethod('__isset') ? $originalClass->getMethod('__isset') : null; - $this->setDocBlock(($originalClass->hasMethod('__isset') ? "{@inheritDoc}\n" : '') . '@param string $name'); + $this->setDocBlock(($parent ? "{@inheritDoc}\n" : '') . '@param string $name'); if (! $publicProperties->isEmpty()) { $callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n" @@ -67,6 +68,7 @@ public function __construct( } $callParent .= PublicScopeSimulator::getPublicAccessSimulationCode( + $parent, PublicScopeSimulator::OPERATION_ISSET, 'name', null, diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicSet.php b/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicSet.php index 9ffe7184b..fb96bc08d 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicSet.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicSet.php @@ -58,7 +58,7 @@ public function __construct( [new ParameterGenerator('name'), new ParameterGenerator('value')] ); - $hasParent = $originalClass->hasMethod('__set'); + $hasParent = $originalClass->hasMethod('__set') ? $originalClass->getMethod('__set') : null; $initializer = $initializerProperty->getName(); $valueHolder = $valueHolderProperty->getName(); $callParent = ''; @@ -74,6 +74,7 @@ public function __construct( $callParent .= $hasParent ? 'return $this->' . $valueHolder . '->__set($name, $value);' : PublicScopeSimulator::getPublicAccessSimulationCode( + $hasParent, PublicScopeSimulator::OPERATION_SET, 'name', 'value', diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicUnset.php b/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicUnset.php index ba5316bed..0fd1730c4 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicUnset.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolder/MethodGenerator/MagicUnset.php @@ -54,7 +54,7 @@ public function __construct( ) { parent::__construct($originalClass, '__unset', [new ParameterGenerator('name')]); - $hasParent = $originalClass->hasMethod('__unset'); + $hasParent = $originalClass->hasMethod('__unset') ? $originalClass->getMethod('__unset') : null; $initializer = $initializerProperty->getName(); $valueHolder = $valueHolderProperty->getName(); $callParent = ''; @@ -70,6 +70,7 @@ public function __construct( $callParent .= $hasParent ? 'return $this->' . $valueHolder . '->__unset($name);' : PublicScopeSimulator::getPublicAccessSimulationCode( + $hasParent, PublicScopeSimulator::OPERATION_UNSET, 'name', null, From 900b70b10b4fbd41bd19c94ffdde6aeca16a0155 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Thu, 4 May 2017 13:53:56 +0200 Subject: [PATCH 7/7] [BC BREAK] correcting static calls to `PublicScopeSimulator::getPublicAccessSimulationCode()`, as the parameter count changed --- .../ProxyGenerator/Util/PublicScopeSimulatorTest.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/Util/PublicScopeSimulatorTest.php b/tests/ProxyManagerTest/ProxyGenerator/Util/PublicScopeSimulatorTest.php index 9852bffa6..057329827 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/Util/PublicScopeSimulatorTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/Util/PublicScopeSimulatorTest.php @@ -72,6 +72,7 @@ public function testSimpleGet() : void self::assertSame( $expected, PublicScopeSimulator::getPublicAccessSimulationCode( + null, PublicScopeSimulator::OPERATION_GET, 'foo', null, @@ -106,6 +107,7 @@ public function testSimpleSet() : void self::assertSame( $expected, PublicScopeSimulator::getPublicAccessSimulationCode( + null, PublicScopeSimulator::OPERATION_SET, 'foo', 'baz', @@ -140,6 +142,7 @@ public function testSimpleIsset() : void self::assertSame( $expected, PublicScopeSimulator::getPublicAccessSimulationCode( + null, PublicScopeSimulator::OPERATION_ISSET, 'foo', null, @@ -174,6 +177,7 @@ public function testSimpleUnset() : void self::assertSame( $expected, PublicScopeSimulator::getPublicAccessSimulationCode( + null, PublicScopeSimulator::OPERATION_UNSET, 'foo', null, @@ -188,6 +192,7 @@ public function testSetRequiresValueParameterName() : void $this->expectException(InvalidArgumentException::class); PublicScopeSimulator::getPublicAccessSimulationCode( + null, PublicScopeSimulator::OPERATION_SET, 'foo', null, @@ -221,6 +226,7 @@ public function testDelegatesToValueHolderWhenAvailable() : void self::assertSame( $expected, PublicScopeSimulator::getPublicAccessSimulationCode( + null, PublicScopeSimulator::OPERATION_SET, 'foo', 'baz', @@ -234,7 +240,7 @@ public function testSetRequiresValidOperation() : void { $this->expectException(InvalidArgumentException::class); - PublicScopeSimulator::getPublicAccessSimulationCode('invalid', 'foo'); + PublicScopeSimulator::getPublicAccessSimulationCode(null, 'invalid', 'foo'); } public function testWillReturnDirectlyWithNoReturnParam() : void @@ -275,6 +281,7 @@ public function testWillReturnDirectlyWithNoReturnParam() : void self::assertSame( $expected, PublicScopeSimulator::getPublicAccessSimulationCode( + null, PublicScopeSimulator::OPERATION_GET, 'foo' )