From 90992538196f3fa3936ac78f894deb3ab6a609ae Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 27 Oct 2014 06:46:19 +0100 Subject: [PATCH 001/153] POC of lazy-loading of ghost objects implemented only via properties existence --- .../MethodGenerator/Constructor.php | 34 +++++++-- .../MethodGenerator/CallInitializer.php | 1 + .../MethodGenerator/MagicGet.php | 10 +-- .../MethodGenerator/MagicIsset.php | 10 +-- .../MethodGenerator/MagicSet.php | 10 +-- .../MethodGenerator/MagicUnset.php | 10 +-- .../LazyLoadingGhostGenerator.php | 6 +- .../PublicPropertiesDefaults.php | 2 +- .../LazyLoadingGhostFunctionalTest.php | 69 ++++++++++++++++++- tests/ProxyManagerTestAsset/BaseClass.php | 24 +++++++ 10 files changed, 145 insertions(+), 31 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/Constructor.php b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/Constructor.php index c5145ed32..fc41b3c27 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/Constructor.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/Constructor.php @@ -44,18 +44,42 @@ public function __construct(ReflectionClass $originalClass, PropertyGenerator $i $this->setParameter(new ParameterGenerator('initializer')); - /* @var $publicProperties \ReflectionProperty[] */ - $publicProperties = $originalClass->getProperties(ReflectionProperty::IS_PUBLIC); - $unsetProperties = []; + /* @var $allProperties \ReflectionProperty[] */ + $allProperties = []; + $class = $originalClass; - foreach ($publicProperties as $publicProperty) { + do { + foreach ($class->getProperties() as $property) { + $allProperties[$property->getDeclaringClass()->getName() . '#' . $property->getName()] = $property; + } + } while ($class = $class->getParentClass()); + + $unsetProperties = array(); + + foreach ($allProperties as $publicProperty) { $unsetProperties[] = '$this->' . $publicProperty->getName(); } $this->setDocblock("@override constructor for lazy initialization\n\n@param \\Closure|null \$initializer"); $this->setBody( - ($unsetProperties ? 'unset(' . implode(', ', $unsetProperties) . ");\n\n" : '') + implode("\n", array_map([$this, 'getUnsetPropertyCode'], $allProperties)) . '$this->' . $initializerProperty->getName() . ' = $initializer;' ); } + + /** + * @param ReflectionProperty $property + * + * @return string + */ + private function getUnsetPropertyCode(ReflectionProperty $property) + { + if (! $property->isPrivate()) { + return 'unset($this->' . $property->getName() . ");\n"; + } + + return "\\Closure::bind(function () {\n" + . ' unset($this->' . $property->getName() . ");\n" + . '}, $this, ' . var_export($property->getDeclaringClass()->getName(), true) . ")->__invoke();\n"; + } } diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php index 1d82fb5c7..132910a9f 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php @@ -61,6 +61,7 @@ public function __construct( 'if ($this->' . $initialization . ' || ! $this->' . $initializer . ') {' . "\n return;\n}\n\n" . "\$this->" . $initialization . " = true;\n\n" . "foreach (self::\$" . $publicPropsDefaults->getName() . " as \$key => \$default) {\n" + // @todo should use closures for private properties here . " \$this->\$key = \$default;\n" . "}\n\n" . '$this->' . $initializer . '->__invoke' diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php index 6fa32895d..e4491930f 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php @@ -53,11 +53,11 @@ public function __construct( $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"; - } + //if (! $publicProperties->isEmpty()) { + $callParent = '' //'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n" + . ' return $this->$name;'; + //. "\n}\n\n"; + //} if ($override) { $callParent .= 'return parent::__get($name);'; diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php index 28b68ae83..4011ef7f7 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php @@ -53,11 +53,11 @@ public function __construct( $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"; - } + //if (! $publicProperties->isEmpty()) { + $callParent = '' //'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n" + . ' return isset($this->$name);'; + //. "\n}\n\n"; + //} if ($override) { $callParent .= 'return parent::__isset($name);'; diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php index 4d1b2dc4e..82a5e100e 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php @@ -57,11 +57,11 @@ public function __construct( $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"; - } + //if (! $publicProperties->isEmpty()) { + $callParent = '' //'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n" + . ' return ($this->$name = $value);'; + //. "\n}\n\n"; + //} if ($override) { $callParent .= 'return parent::__set($name, $value);'; diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php index 037f360da..24973b6ff 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php @@ -53,12 +53,12 @@ public function __construct( $this->setDocblock(($override ? "{@inheritDoc}\n" : '') . '@param string $name'); - if (! $publicProperties->isEmpty()) { - $callParent = 'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n" + //if (! $publicProperties->isEmpty()) { + $callParent = '' //'if (isset(self::$' . $publicProperties->getName() . "[\$name])) {\n" . ' unset($this->$name);' - . "\n\n return;" - . "\n}\n\n"; - } + . "\n\n return;"; + //. "\n}\n\n"; + //} if ($override) { $callParent .= "return parent::__unset(\$name);"; diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php index b5ee3b1f5..f36b65927 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php @@ -84,7 +84,7 @@ public function generate(ReflectionClass $originalClass, ClassGenerator $classGe function (MethodGenerator $generatedMethod) use ($originalClass, $classGenerator) { ClassGeneratorUtils::addMethodIfNotFinal($originalClass, $classGenerator, $generatedMethod); }, - array_merge( + /*array_merge( array_map( function (ReflectionMethod $method) use ($initializer, $init) { return LazyLoadingMethodInterceptor::generateMethod( @@ -94,7 +94,7 @@ function (ReflectionMethod $method) use ($initializer, $init) { ); }, ProxiedMethodsFilter::getProxiedMethods($originalClass) - ), + ),*/ [ $init, new StaticProxyConstructor($originalClass, $initializer), @@ -109,7 +109,7 @@ function (ReflectionMethod $method) use ($initializer, $init) { new InitializeProxy($initializer, $init), new IsProxyInitialized($initializer), ] - ) + //) ); } } diff --git a/src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesDefaults.php b/src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesDefaults.php index 2aa395ce7..915f92099 100644 --- a/src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesDefaults.php +++ b/src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesDefaults.php @@ -45,7 +45,7 @@ public function __construct(ReflectionClass $originalClass) $defaults = $originalClass->getDefaultProperties(); - foreach ($originalClass->getProperties(ReflectionProperty::IS_PUBLIC) as $publicProperty) { + foreach ($originalClass->getProperties() as $publicProperty) { $name = $publicProperty->getName(); $this->publicProperties[$name] = $defaults[$name]; } diff --git a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php index f43bbc0ac..4d72f4e3c 100644 --- a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php +++ b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php @@ -47,9 +47,9 @@ class LazyLoadingGhostFunctionalTest extends PHPUnit_Framework_TestCase { /** - * @dataProvider getProxyMethods + * @dataProvider getProxyInitializingMethods */ - public function testMethodCalls($className, $instance, $method, $params, $expectedValue) + public function testMethodCallsThatLazyLoadTheObject($className, $instance, $method, $params, $expectedValue) { $proxyName = $this->generateProxy($className); @@ -61,6 +61,24 @@ public function testMethodCalls($className, $instance, $method, $params, $expect $this->assertTrue($proxy->isProxyInitialized()); } + /** + * @dataProvider getProxyNonInitializingMethods + */ + public function testMethodCallsThatDoNotLazyLoadTheObject($className, $instance, $method, $params, $expectedValue) + { + $proxyName = $this->generateProxy($className); + $initializeMatcher = $this->getMock('stdClass', ['__invoke']); + + $initializeMatcher->expects($this->never())->method('__invoke'); // should not initialize the proxy + + /* @var $proxy \ProxyManager\Proxy\GhostObjectInterface|BaseClass */ + $proxy = new $proxyName($this->createInitializer($className, $instance, $initializeMatcher)); + + $this->assertFalse($proxy->isProxyInitialized()); + $this->assertSame($expectedValue, call_user_func_array(array($proxy, $method), $params)); + $this->assertFalse($proxy->isProxyInitialized()); + } + /** * @dataProvider getProxyMethods */ @@ -435,6 +453,53 @@ public function getProxyMethods() ]; } + /** + * Generates a list of object | invoked method | parameters | expected result for methods that cause lazy-loading + * of a ghost object + * + * @return array + */ + public function getProxyInitializingMethods() + { + $selfHintParam = new ClassWithSelfHint(); + + $data = array( + array( + 'ProxyManagerTestAsset\\BaseClass', + new BaseClass(), + 'publicPropertyGetter', + array(), + 'publicPropertyDefault' + ), + array( + 'ProxyManagerTestAsset\\BaseClass', + new BaseClass(), + 'protectedPropertyGetter', + array(), + 'protectedPropertyDefault' + ), + array( + 'ProxyManagerTestAsset\\BaseClass', + new BaseClass(), + 'privatePropertyGetter', + array(), + 'privatePropertyDefault' + ), + ); + + return $data; + } + + /** + * Generates a list of object | invoked method | parameters | expected result for methods DON'T cause lazy-loading + * + * @return array + */ + public function getProxyNonInitializingMethods() + { + return $this->getProxyMethods(); + } + /** * Generates proxies and instances with a public property to feed to the property accessor methods * diff --git a/tests/ProxyManagerTestAsset/BaseClass.php b/tests/ProxyManagerTestAsset/BaseClass.php index 4d58a1164..4c9f6b7cb 100644 --- a/tests/ProxyManagerTestAsset/BaseClass.php +++ b/tests/ProxyManagerTestAsset/BaseClass.php @@ -49,6 +49,30 @@ public function publicMethod() return 'publicMethodDefault'; } + /** + * @return string + */ + public function publicPropertyGetter() + { + return $this->publicProperty; + } + + /** + * @return string + */ + public function protectedPropertyGetter() + { + return $this->protectedProperty; + } + + /** + * @return string + */ + public function privatePropertyGetter() + { + return $this->privateProperty; + } + /** * @return string */ From 7efeffc9626e4bd0f25379841be714ee46bd22fb Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 22:10:17 +0100 Subject: [PATCH 002/153] `ProxiedMethodsFilter` should be `final` --- src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php b/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php index 4da8f0c63..f59659a6d 100644 --- a/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php +++ b/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php @@ -27,7 +27,7 @@ * @author Marco Pivetta * @license MIT */ -class ProxiedMethodsFilter +final class ProxiedMethodsFilter { /** * @param ReflectionClass $class reflection class from which methods should be extracted From d2c51040d08ce19e2f070bed379747d0934ad0ef Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 22:32:42 +0100 Subject: [PATCH 003/153] `ProxiedMethodsFilter` should provide API to filter only for abstract public methods that need implementation --- .../Util/ProxiedMethodsFilterTest.php | 100 +++++++++++++++++- 1 file changed, 97 insertions(+), 3 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/Util/ProxiedMethodsFilterTest.php b/tests/ProxyManagerTest/ProxyGenerator/Util/ProxiedMethodsFilterTest.php index 00ccbaaa6..58cb96d0b 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/Util/ProxiedMethodsFilterTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/Util/ProxiedMethodsFilterTest.php @@ -21,6 +21,8 @@ use PHPUnit_Framework_TestCase; use ProxyManager\ProxyGenerator\Util\ProxiedMethodsFilter; use ProxyManagerTestAsset\BaseClass; +use ProxyManagerTestAsset\ClassWithAbstractProtectedMethod; +use ProxyManagerTestAsset\ClassWithAbstractPublicMethod; use ProxyManagerTestAsset\EmptyClass; use ProxyManagerTestAsset\HydratedObject; use ProxyManagerTestAsset\LazyLoadingMock; @@ -40,17 +42,53 @@ class ProxiedMethodsFilterTest extends PHPUnit_Framework_TestCase { /** * @dataProvider expectedMethods + * + * @param ReflectionClass $reflectionClass + * @param array|null $excludes + * @param array $expectedMethods */ public function testFiltering(ReflectionClass $reflectionClass, $excludes, array $expectedMethods) { - if (is_array($excludes)) { + if (null === $excludes) { + $filtered = ProxiedMethodsFilter::getProxiedMethods($reflectionClass); + } else { $filtered = ProxiedMethodsFilter::getProxiedMethods($reflectionClass, $excludes); + } + + foreach ($filtered as $method) { + $this->assertInstanceOf(ReflectionMethod::class, $method); + } + + $keys = array_map( + function (ReflectionMethod $method) { + return $method->getName(); + }, + $filtered + ); + + sort($keys); + sort($expectedMethods); + + $this->assertSame($keys, $expectedMethods); + } + + /** + * @dataProvider expectedAbstractPublicMethods + * + * @param ReflectionClass $reflectionClass + * @param array|null $excludes + * @param array $expectedMethods + */ + public function testFilteringOfAbstractPublic(ReflectionClass $reflectionClass, $excludes, array $expectedMethods) + { + if (null === $excludes) { + $filtered = ProxiedMethodsFilter::getAbstractProxiedMethods($reflectionClass); } else { - $filtered = ProxiedMethodsFilter::getProxiedMethods($reflectionClass); + $filtered = ProxiedMethodsFilter::getAbstractProxiedMethods($reflectionClass, $excludes); } foreach ($filtered as $method) { - $this->assertInstanceOf('ReflectionMethod', $method); + $this->assertInstanceOf(ReflectionMethod::class, $method); } $keys = array_map( @@ -67,6 +105,8 @@ function (ReflectionMethod $method) { } /** + * Data provider + * * @return array[][] */ public function expectedMethods() @@ -76,10 +116,13 @@ public function expectedMethods() new ReflectionClass(BaseClass::class), null, [ + 'privatePropertyGetter', + 'protectedPropertyGetter', 'publicArrayHintedMethod', 'publicByReferenceMethod', 'publicByReferenceParameterMethod', 'publicMethod', + 'publicPropertyGetter', 'publicTypeHintedMethod', ], ], @@ -113,6 +156,57 @@ public function expectedMethods() [], ['doFoo', '__get'], ], + [ + new ReflectionClass(ClassWithAbstractProtectedMethod::class), + null, + [], + ], + [ + new ReflectionClass(ClassWithAbstractPublicMethod::class), + null, + ['publicAbstractMethod'], + ], + [ + new ReflectionClass(ClassWithAbstractPublicMethod::class), + ['publicAbstractMethod'], + [], + ], + ]; + } + + /** + * Data provider + * + * @return array[][] + */ + public function expectedAbstractPublicMethods() + { + return [ + [ + new ReflectionClass(BaseClass::class), + null, + [], + ], + [ + new ReflectionClass(EmptyClass::class), + null, + [], + ], + [ + new ReflectionClass(ClassWithAbstractProtectedMethod::class), + null, + [], + ], + [ + new ReflectionClass(ClassWithAbstractPublicMethod::class), + null, + ['publicAbstractMethod'], + ], + [ + new ReflectionClass(ClassWithAbstractPublicMethod::class), + ['publicAbstractMethod'], + [], + ], ]; } } From a3ba461eab09c93d0405f771a18caa770f864ef4 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 22:33:06 +0100 Subject: [PATCH 004/153] New test asset: class with an abstract public method --- .../ClassWithAbstractPublicMethod.php | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 tests/ProxyManagerTestAsset/ClassWithAbstractPublicMethod.php diff --git a/tests/ProxyManagerTestAsset/ClassWithAbstractPublicMethod.php b/tests/ProxyManagerTestAsset/ClassWithAbstractPublicMethod.php new file mode 100644 index 000000000..6ae894441 --- /dev/null +++ b/tests/ProxyManagerTestAsset/ClassWithAbstractPublicMethod.php @@ -0,0 +1,33 @@ + + * @license MIT + */ +abstract class ClassWithAbstractPublicMethod +{ + /** + * @return void + */ + abstract public function publicAbstractMethod(); +} From a610f0733bb6b0141a30394cd3d62d71e48772c1 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 22:36:07 +0100 Subject: [PATCH 005/153] New test asset: class with all magic methods as abstracts --- .../ClassWithAbstractMagicMethods.php | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 tests/ProxyManagerTestAsset/ClassWithAbstractMagicMethods.php diff --git a/tests/ProxyManagerTestAsset/ClassWithAbstractMagicMethods.php b/tests/ProxyManagerTestAsset/ClassWithAbstractMagicMethods.php new file mode 100644 index 000000000..37396dd0f --- /dev/null +++ b/tests/ProxyManagerTestAsset/ClassWithAbstractMagicMethods.php @@ -0,0 +1,63 @@ + + * @license MIT + */ +abstract class ClassWithAbstractMagicMethods +{ + /** + * {@inheritDoc} + */ + abstract public function __set($name, $value); + + /** + * {@inheritDoc} + */ + abstract public function __get($name); + + /** + * {@inheritDoc} + */ + abstract public function __isset($name); + + /** + * {@inheritDoc} + */ + abstract public function __unset($name); + + /** + * {@inheritDoc} + */ + abstract public function __sleep(); + + /** + * {@inheritDoc} + */ + abstract public function __wakeup(); + + /** + * {@inheritDoc} + */ + abstract public function __clone(); +} From 6b4e432166d2e78bfdf3eab645b57021bad59240 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 22:37:30 +0100 Subject: [PATCH 006/153] Verifying filtering against the new `ClassWithAbstractMagicMethods` test asset --- .../Util/ProxiedMethodsFilterTest.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/ProxyManagerTest/ProxyGenerator/Util/ProxiedMethodsFilterTest.php b/tests/ProxyManagerTest/ProxyGenerator/Util/ProxiedMethodsFilterTest.php index 58cb96d0b..fd754e78f 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/Util/ProxiedMethodsFilterTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/Util/ProxiedMethodsFilterTest.php @@ -21,6 +21,7 @@ use PHPUnit_Framework_TestCase; use ProxyManager\ProxyGenerator\Util\ProxiedMethodsFilter; use ProxyManagerTestAsset\BaseClass; +use ProxyManagerTestAsset\ClassWithAbstractMagicMethods; use ProxyManagerTestAsset\ClassWithAbstractProtectedMethod; use ProxyManagerTestAsset\ClassWithAbstractPublicMethod; use ProxyManagerTestAsset\EmptyClass; @@ -207,6 +208,24 @@ public function expectedAbstractPublicMethods() ['publicAbstractMethod'], [], ], + [ + new ReflectionClass(ClassWithAbstractMagicMethods::class), + null, + [], + ], + [ + new ReflectionClass(ClassWithAbstractMagicMethods::class), + [], + [ + '__clone', + '__get', + '__isset', + '__set', + '__sleep', + '__unset', + '__wakeup', + ], + ], ]; } } From 1739ad5d2b5f8a706b46fd8563cf1b68d292176d Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 22:38:03 +0100 Subject: [PATCH 007/153] Verifying filtering against the new `ClassWithAbstractMagicMethods` test asset --- .../Util/ProxiedMethodsFilterTest.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/ProxyManagerTest/ProxyGenerator/Util/ProxiedMethodsFilterTest.php b/tests/ProxyManagerTest/ProxyGenerator/Util/ProxiedMethodsFilterTest.php index fd754e78f..2cd28f829 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/Util/ProxiedMethodsFilterTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/Util/ProxiedMethodsFilterTest.php @@ -172,6 +172,24 @@ public function expectedMethods() ['publicAbstractMethod'], [], ], + [ + new ReflectionClass(ClassWithAbstractMagicMethods::class), + null, + [], + ], + [ + new ReflectionClass(ClassWithAbstractMagicMethods::class), + [], + [ + '__clone', + '__get', + '__isset', + '__set', + '__sleep', + '__unset', + '__wakeup', + ], + ], ]; } From b63e29f8039117195393d6bd4a1441e0f35147c1 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 22:38:53 +0100 Subject: [PATCH 008/153] Verifying that only abstract methods are fetched, even when magic methods are involved --- .../ProxyGenerator/Util/ProxiedMethodsFilterTest.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/ProxyManagerTest/ProxyGenerator/Util/ProxiedMethodsFilterTest.php b/tests/ProxyManagerTest/ProxyGenerator/Util/ProxiedMethodsFilterTest.php index 2cd28f829..dfafccc0d 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/Util/ProxiedMethodsFilterTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/Util/ProxiedMethodsFilterTest.php @@ -24,6 +24,7 @@ use ProxyManagerTestAsset\ClassWithAbstractMagicMethods; use ProxyManagerTestAsset\ClassWithAbstractProtectedMethod; use ProxyManagerTestAsset\ClassWithAbstractPublicMethod; +use ProxyManagerTestAsset\ClassWithMagicMethods; use ProxyManagerTestAsset\EmptyClass; use ProxyManagerTestAsset\HydratedObject; use ProxyManagerTestAsset\LazyLoadingMock; @@ -226,6 +227,11 @@ public function expectedAbstractPublicMethods() ['publicAbstractMethod'], [], ], + [ + new ReflectionClass(ClassWithMagicMethods::class), + [], + [], + ], [ new ReflectionClass(ClassWithAbstractMagicMethods::class), null, From 88f0a1840f8c6621c69525aa3ff6827d5c2ad70f Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 22:42:33 +0100 Subject: [PATCH 009/153] Lazy loading ghost should only implement `abstract public` methods, and avoid overriding any other existing method --- .../ProxyGenerator/LazyLoadingGhostGenerator.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php index f36b65927..0c05c54a5 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php @@ -84,7 +84,7 @@ public function generate(ReflectionClass $originalClass, ClassGenerator $classGe function (MethodGenerator $generatedMethod) use ($originalClass, $classGenerator) { ClassGeneratorUtils::addMethodIfNotFinal($originalClass, $classGenerator, $generatedMethod); }, - /*array_merge( + array_merge( array_map( function (ReflectionMethod $method) use ($initializer, $init) { return LazyLoadingMethodInterceptor::generateMethod( @@ -93,8 +93,8 @@ function (ReflectionMethod $method) use ($initializer, $init) { $init ); }, - ProxiedMethodsFilter::getProxiedMethods($originalClass) - ),*/ + ProxiedMethodsFilter::getAbstractProxiedMethods($originalClass) + ), [ $init, new StaticProxyConstructor($originalClass, $initializer), @@ -109,7 +109,7 @@ function (ReflectionMethod $method) use ($initializer, $init) { new InitializeProxy($initializer, $init), new IsProxyInitialized($initializer), ] - //) + ) ); } } From a627262865cf11202f5fdf01deec93295dfee970 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 22:44:06 +0100 Subject: [PATCH 010/153] Implementing `getAbstractProxiedMethods` as per test specification and usage --- .../Util/ProxiedMethodsFilter.php | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php b/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php index f59659a6d..9af18e05e 100644 --- a/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php +++ b/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php @@ -53,4 +53,31 @@ function (ReflectionMethod $method) use ($ignored) { } ); } + + /** + * @param ReflectionClass $class reflection class from which methods should be extracted + * @param string[] $excluded methods to be ignored + * + * @return ReflectionMethod[] + * + * @todo to be refactored due to code duplication + */ + public static function getAbstractProxiedMethods( + ReflectionClass $class, + array $excluded = ['__get', '__set', '__isset', '__unset', '__clone', '__sleep', '__wakeup'] + ) { + $ignored = array_flip(array_map('strtolower', $excluded)); + + return array_filter( + $class->getMethods(ReflectionMethod::IS_PUBLIC), + function (ReflectionMethod $method) use ($ignored) { + return $method->isAbstract() && ! ( + $method->isConstructor() + || isset($ignored[strtolower($method->getName())]) + || $method->isFinal() + || $method->isStatic() + ); + } + ); + } } From 704a124d9f0e9857ceb4de33ce6ca524a11c34b1 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 22:53:30 +0100 Subject: [PATCH 011/153] All properties should be unset in a lazy loading ghost: this has to happen in the static constructor, for consistency --- .../StaticProxyConstructor.php | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php index 5ecc76052..69006d06b 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php @@ -52,14 +52,40 @@ public function __construct(ReflectionClass $originalClass, PropertyGenerator $i $unsetProperties[] = '$instance->' . $publicProperty->getName(); } + /* @var $allProperties \ReflectionProperty[] */ + $allProperties = []; + $class = $originalClass; + + // @todo move this filter to a separate class + do { + foreach ($class->getProperties() as $property) { + $allProperties[$property->getDeclaringClass()->getName() . '#' . $property->getName()] = $property; + } + } while ($class = $class->getParentClass()); + $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" - . ($unsetProperties ? 'unset(' . implode(', ', $unsetProperties) . ");\n\n" : '') + . implode("\n", array_map([$this, 'getUnsetPropertyCode'], $allProperties)) . '$instance->' . $initializerProperty->getName() . ' = $initializer;' . "\n\n" . 'return $instance;' ); } + + /** + * @param ReflectionProperty $property + * + * @return string + */ + private function getUnsetPropertyCode(ReflectionProperty $property) + { + if (! $property->isPrivate()) { + return 'unset($instance->' . $property->getName() . ");\n"; + } + return "\\Closure::bind(function (\$instance) {\n" + . ' unset($instance->' . $property->getName() . ");\n" + . '}, $this, ' . var_export($property->getDeclaringClass()->getName(), true) . ")->__invoke(\$instance);\n"; + } } From 2baf01fed48f380c609d4ac9eea5661591d0cc81 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 22:55:56 +0100 Subject: [PATCH 012/153] Correct binding of closures that `unset()` proxy properties --- .../LazyLoading/MethodGenerator/StaticProxyConstructor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php index 69006d06b..fc60ac525 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php @@ -86,6 +86,6 @@ private function getUnsetPropertyCode(ReflectionProperty $property) } return "\\Closure::bind(function (\$instance) {\n" . ' unset($instance->' . $property->getName() . ");\n" - . '}, $this, ' . var_export($property->getDeclaringClass()->getName(), true) . ")->__invoke(\$instance);\n"; + . '}, null, ' . var_export($property->getDeclaringClass()->getName(), true) . ")->__invoke(\$instance);\n"; } } From 78d242280ea12100b6b35d075bb2265fc4b9e41a Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 23:01:00 +0100 Subject: [PATCH 013/153] Reverting changes to `ProxyManager\ProxyGenerator\LazyLoading\MethodGenerator\Constructor`, as it is unused in a lazy loading ghost --- .../MethodGenerator/Constructor.php | 34 +++---------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/Constructor.php b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/Constructor.php index fc41b3c27..c5145ed32 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/Constructor.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/Constructor.php @@ -44,42 +44,18 @@ public function __construct(ReflectionClass $originalClass, PropertyGenerator $i $this->setParameter(new ParameterGenerator('initializer')); - /* @var $allProperties \ReflectionProperty[] */ - $allProperties = []; - $class = $originalClass; + /* @var $publicProperties \ReflectionProperty[] */ + $publicProperties = $originalClass->getProperties(ReflectionProperty::IS_PUBLIC); + $unsetProperties = []; - do { - foreach ($class->getProperties() as $property) { - $allProperties[$property->getDeclaringClass()->getName() . '#' . $property->getName()] = $property; - } - } while ($class = $class->getParentClass()); - - $unsetProperties = array(); - - foreach ($allProperties as $publicProperty) { + foreach ($publicProperties as $publicProperty) { $unsetProperties[] = '$this->' . $publicProperty->getName(); } $this->setDocblock("@override constructor for lazy initialization\n\n@param \\Closure|null \$initializer"); $this->setBody( - implode("\n", array_map([$this, 'getUnsetPropertyCode'], $allProperties)) + ($unsetProperties ? 'unset(' . implode(', ', $unsetProperties) . ");\n\n" : '') . '$this->' . $initializerProperty->getName() . ' = $initializer;' ); } - - /** - * @param ReflectionProperty $property - * - * @return string - */ - private function getUnsetPropertyCode(ReflectionProperty $property) - { - if (! $property->isPrivate()) { - return 'unset($this->' . $property->getName() . ");\n"; - } - - return "\\Closure::bind(function () {\n" - . ' unset($this->' . $property->getName() . ");\n" - . '}, $this, ' . var_export($property->getDeclaringClass()->getName(), true) . ")->__invoke();\n"; - } } From bf1848a404310a3e6a43bc22487b8b137575e08d Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 23:06:05 +0100 Subject: [PATCH 014/153] Re-introducing `ClassWithSelfHint` test that was squashed away by a rebase --- .../Functional/LazyLoadingGhostFunctionalTest.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php index 4d72f4e3c..518d5d846 100644 --- a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php +++ b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php @@ -461,9 +461,7 @@ public function getProxyMethods() */ public function getProxyInitializingMethods() { - $selfHintParam = new ClassWithSelfHint(); - - $data = array( + return array( array( 'ProxyManagerTestAsset\\BaseClass', new BaseClass(), @@ -485,9 +483,14 @@ public function getProxyInitializingMethods() array(), 'privatePropertyDefault' ), + array( + 'ProxyManagerTestAsset\\ClassWithSelfHint', + new ClassWithSelfHint(), + 'selfHintMethod', + array('parameter' => $selfHintParam), + $selfHintParam + ), ); - - return $data; } /** From da0d4d99f85dfce9b7a9efeaed4f2f3ee6d2011f Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 23:06:55 +0100 Subject: [PATCH 015/153] Fixed undefined parameter --- .../Functional/LazyLoadingGhostFunctionalTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php index 518d5d846..374b643b5 100644 --- a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php +++ b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php @@ -461,6 +461,8 @@ public function getProxyMethods() */ public function getProxyInitializingMethods() { + $selfHintParam = new ClassWithSelfHint(); + return array( array( 'ProxyManagerTestAsset\\BaseClass', From a15ec12c6fded873a309468028f68a8fd12d5a83 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 23:08:39 +0100 Subject: [PATCH 016/153] `::class` over string reference --- .../Functional/LazyLoadingGhostFunctionalTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php index 374b643b5..f798c0b0f 100644 --- a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php +++ b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php @@ -465,28 +465,28 @@ public function getProxyInitializingMethods() return array( array( - 'ProxyManagerTestAsset\\BaseClass', + BaseClass::class, new BaseClass(), 'publicPropertyGetter', array(), 'publicPropertyDefault' ), array( - 'ProxyManagerTestAsset\\BaseClass', + BaseClass::class, new BaseClass(), 'protectedPropertyGetter', array(), 'protectedPropertyDefault' ), array( - 'ProxyManagerTestAsset\\BaseClass', + BaseClass::class, new BaseClass(), 'privatePropertyGetter', array(), 'privatePropertyDefault' ), array( - 'ProxyManagerTestAsset\\ClassWithSelfHint', + ClassWithSelfHint::class, new ClassWithSelfHint(), 'selfHintMethod', array('parameter' => $selfHintParam), From 5cd913630b49c033191cf2a21d027efdd9c1b090 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 23:11:53 +0100 Subject: [PATCH 017/153] `ClassWithSelfHint#selfHintMethod()` does not access any properties of the object, therefore doesn't trigger lazy-loading --- .../Functional/LazyLoadingGhostFunctionalTest.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php index f798c0b0f..71b0d9c1f 100644 --- a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php +++ b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php @@ -461,8 +461,6 @@ public function getProxyMethods() */ public function getProxyInitializingMethods() { - $selfHintParam = new ClassWithSelfHint(); - return array( array( BaseClass::class, @@ -485,13 +483,6 @@ public function getProxyInitializingMethods() array(), 'privatePropertyDefault' ), - array( - ClassWithSelfHint::class, - new ClassWithSelfHint(), - 'selfHintMethod', - array('parameter' => $selfHintParam), - $selfHintParam - ), ); } From d73bf97fc7623dcd92e3e79eb43373cb5c258104 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 23:12:12 +0100 Subject: [PATCH 018/153] Short array syntax --- .../LazyLoadingGhostFunctionalTest.php | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php index 71b0d9c1f..7d9be1d60 100644 --- a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php +++ b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php @@ -461,29 +461,29 @@ public function getProxyMethods() */ public function getProxyInitializingMethods() { - return array( - array( + return [ + [ BaseClass::class, new BaseClass(), 'publicPropertyGetter', - array(), + [], 'publicPropertyDefault' - ), - array( + ], + [ BaseClass::class, new BaseClass(), 'protectedPropertyGetter', - array(), + [], 'protectedPropertyDefault' - ), - array( + ], + [ BaseClass::class, new BaseClass(), 'privatePropertyGetter', - array(), + [], 'privatePropertyDefault' - ), - ); + ], + ]; } /** From 74f949cea53d4257bc3594ac0ef48dd9239ad047 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 23:23:51 +0100 Subject: [PATCH 019/153] Refactoring `generateProxy` method --- .../Functional/LazyLoadingGhostFunctionalTest.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php index 7d9be1d60..38f2d8af5 100644 --- a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php +++ b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php @@ -355,12 +355,10 @@ public function testWillBehaveLikeObjectWithNormalConstructor() private function generateProxy($parentClassName) { $generatedClassName = __NAMESPACE__ . '\\' . UniqueIdentifierGenerator::getIdentifier('Foo'); - $generator = new LazyLoadingGhostGenerator(); $generatedClass = new ClassGenerator($generatedClassName); - $strategy = new EvaluatingGeneratorStrategy(); - $generator->generate(new ReflectionClass($parentClassName), $generatedClass); - $strategy->generate($generatedClass); + (new LazyLoadingGhostGenerator())->generate(new ReflectionClass($parentClassName), $generatedClass); + (new EvaluatingGeneratorStrategy())->generate($generatedClass); return $generatedClassName; } From d51d11152448e11ea5bded6f488aac1d189c15b5 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 23:26:11 +0100 Subject: [PATCH 020/153] CS: fixed indentation in generated code --- .../LazyLoading/MethodGenerator/StaticProxyConstructor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php index fc60ac525..6241461a4 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php @@ -85,7 +85,7 @@ private function getUnsetPropertyCode(ReflectionProperty $property) return 'unset($instance->' . $property->getName() . ");\n"; } return "\\Closure::bind(function (\$instance) {\n" - . ' unset($instance->' . $property->getName() . ");\n" + . ' unset($instance->' . $property->getName() . ");\n" . '}, null, ' . var_export($property->getDeclaringClass()->getName(), true) . ")->__invoke(\$instance);\n"; } } From f4571098acb17066ba1a916b53a2daa07c071bb1 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 23:27:26 +0100 Subject: [PATCH 021/153] CS: fixed indentation in generated code --- .../LazyLoading/MethodGenerator/StaticProxyConstructor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php index 6241461a4..c96d82a36 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php @@ -85,7 +85,7 @@ private function getUnsetPropertyCode(ReflectionProperty $property) return 'unset($instance->' . $property->getName() . ");\n"; } return "\\Closure::bind(function (\$instance) {\n" - . ' unset($instance->' . $property->getName() . ");\n" + . ' unset($instance->' . $property->getName() . ");\n" . '}, null, ' . var_export($property->getDeclaringClass()->getName(), true) . ")->__invoke(\$instance);\n"; } } From 01d3f45071d82bd728089d32717087313944577b Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 23:29:58 +0100 Subject: [PATCH 022/153] Proxy should be instantiated via its static constructor --- .../Functional/LazyLoadingGhostFunctionalTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php index 38f2d8af5..b86301ed6 100644 --- a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php +++ b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php @@ -72,7 +72,9 @@ public function testMethodCallsThatDoNotLazyLoadTheObject($className, $instance, $initializeMatcher->expects($this->never())->method('__invoke'); // should not initialize the proxy /* @var $proxy \ProxyManager\Proxy\GhostObjectInterface|BaseClass */ - $proxy = new $proxyName($this->createInitializer($className, $instance, $initializeMatcher)); + $proxy = $proxyName::staticProxyConstructor( + $this->createInitializer($className, $instance, $initializeMatcher) + ); $this->assertFalse($proxy->isProxyInitialized()); $this->assertSame($expectedValue, call_user_func_array(array($proxy, $method), $params)); From f5b12ecfb3c4deb80a74b850f902a1d2fc65ade9 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 23:36:00 +0100 Subject: [PATCH 023/153] Moving default methods that are filtered out to a private static property --- .../Util/ProxiedMethodsFilter.php | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php b/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php index 9af18e05e..3e417f78f 100644 --- a/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php +++ b/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php @@ -29,6 +29,19 @@ */ 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 @@ -37,9 +50,10 @@ final class ProxiedMethodsFilter */ public static function getProxiedMethods( ReflectionClass $class, - array $excluded = ['__get', '__set', '__isset', '__unset', '__clone', '__sleep', '__wakeup'] + array $excluded = null ) { - $ignored = array_flip(array_map('strtolower', $excluded)); + $excluded = (null === $excluded) ? self::$defaultExcluded : $excluded; + $ignored = array_flip(array_map('strtolower', $excluded)); return array_filter( $class->getMethods(ReflectionMethod::IS_PUBLIC), @@ -64,9 +78,10 @@ function (ReflectionMethod $method) use ($ignored) { */ public static function getAbstractProxiedMethods( ReflectionClass $class, - array $excluded = ['__get', '__set', '__isset', '__unset', '__clone', '__sleep', '__wakeup'] + array $excluded = null ) { - $ignored = array_flip(array_map('strtolower', $excluded)); + $excluded = (null === $excluded) ? self::$defaultExcluded : $excluded; + $ignored = array_flip(array_map('strtolower', $excluded)); return array_filter( $class->getMethods(ReflectionMethod::IS_PUBLIC), From c73beb355fe48305d57b7004a42d703b4dbb432e Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 23:40:09 +0100 Subject: [PATCH 024/153] Refactored `ProxiedMethodsFilter` to avoid code duplication --- .../Util/ProxiedMethodsFilter.php | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php b/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php index 3e417f78f..697fe5e39 100644 --- a/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php +++ b/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php @@ -52,20 +52,7 @@ public static function getProxiedMethods( ReflectionClass $class, array $excluded = null ) { - $excluded = (null === $excluded) ? self::$defaultExcluded : $excluded; - $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() - ); - } - ); + return self::doFilter($class, (null === $excluded) ? self::$defaultExcluded : $excluded); } /** @@ -80,13 +67,24 @@ public static function getAbstractProxiedMethods( ReflectionClass $class, array $excluded = null ) { - $excluded = (null === $excluded) ? self::$defaultExcluded : $excluded; - $ignored = array_flip(array_map('strtolower', $excluded)); + return self::doFilter($class, (null === $excluded) ? self::$defaultExcluded : $excluded, true); + } + + /** + * @param ReflectionClass $class + * @param array $excluded + * @param bool $requireAbstract + * + * @return ReflectionMethod[] + */ + private static function doFilter(ReflectionClass $class, array $excluded, $requireAbstract = false) + { + $ignored = array_flip(array_map('strtolower', $excluded)); return array_filter( $class->getMethods(ReflectionMethod::IS_PUBLIC), - function (ReflectionMethod $method) use ($ignored) { - return $method->isAbstract() && ! ( + function (ReflectionMethod $method) use ($ignored, $requireAbstract) { + return (! $requireAbstract || $method->isAbstract()) && ! ( $method->isConstructor() || isset($ignored[strtolower($method->getName())]) || $method->isFinal() From e63ea9a8ee221083c2fdbe224ba97088918b421d Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 23:41:02 +0100 Subject: [PATCH 025/153] CS: no need for multiline method definition --- .../ProxyGenerator/Util/ProxiedMethodsFilter.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php b/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php index 697fe5e39..1985f9608 100644 --- a/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php +++ b/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php @@ -48,10 +48,8 @@ final class ProxiedMethodsFilter * * @return ReflectionMethod[] */ - public static function getProxiedMethods( - ReflectionClass $class, - array $excluded = null - ) { + public static function getProxiedMethods(ReflectionClass $class, array $excluded = null) + { return self::doFilter($class, (null === $excluded) ? self::$defaultExcluded : $excluded); } @@ -63,10 +61,8 @@ public static function getProxiedMethods( * * @todo to be refactored due to code duplication */ - public static function getAbstractProxiedMethods( - ReflectionClass $class, - array $excluded = null - ) { + public static function getAbstractProxiedMethods(ReflectionClass $class, array $excluded = null) + { return self::doFilter($class, (null === $excluded) ? self::$defaultExcluded : $excluded, true); } From a4f76790d4cdc9f4f8af14eea7ab0293048c6596 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 23:41:19 +0100 Subject: [PATCH 026/153] Removing fixed `@todo` --- src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php b/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php index 1985f9608..36231a86d 100644 --- a/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php +++ b/src/ProxyManager/ProxyGenerator/Util/ProxiedMethodsFilter.php @@ -58,8 +58,6 @@ public static function getProxiedMethods(ReflectionClass $class, array $excluded * @param string[] $excluded methods to be ignored * * @return ReflectionMethod[] - * - * @todo to be refactored due to code duplication */ public static function getAbstractProxiedMethods(ReflectionClass $class, array $excluded = null) { From c7e28a427ad4d94dabf63b74d62616717ee69e1f Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 14 Dec 2014 23:42:35 +0100 Subject: [PATCH 027/153] Refactored test now that `ProxiedMethodsFilter::get*ProxiedMethods()` accept `null` `$excludes` --- .../ProxyGenerator/Util/ProxiedMethodsFilterTest.php | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/Util/ProxiedMethodsFilterTest.php b/tests/ProxyManagerTest/ProxyGenerator/Util/ProxiedMethodsFilterTest.php index dfafccc0d..f94d50155 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/Util/ProxiedMethodsFilterTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/Util/ProxiedMethodsFilterTest.php @@ -51,11 +51,7 @@ class ProxiedMethodsFilterTest extends PHPUnit_Framework_TestCase */ public function testFiltering(ReflectionClass $reflectionClass, $excludes, array $expectedMethods) { - if (null === $excludes) { - $filtered = ProxiedMethodsFilter::getProxiedMethods($reflectionClass); - } else { - $filtered = ProxiedMethodsFilter::getProxiedMethods($reflectionClass, $excludes); - } + $filtered = ProxiedMethodsFilter::getProxiedMethods($reflectionClass, $excludes); foreach ($filtered as $method) { $this->assertInstanceOf(ReflectionMethod::class, $method); @@ -83,11 +79,7 @@ function (ReflectionMethod $method) { */ public function testFilteringOfAbstractPublic(ReflectionClass $reflectionClass, $excludes, array $expectedMethods) { - if (null === $excludes) { - $filtered = ProxiedMethodsFilter::getAbstractProxiedMethods($reflectionClass); - } else { - $filtered = ProxiedMethodsFilter::getAbstractProxiedMethods($reflectionClass, $excludes); - } + $filtered = ProxiedMethodsFilter::getAbstractProxiedMethods($reflectionClass, $excludes); foreach ($filtered as $method) { $this->assertInstanceOf(ReflectionMethod::class, $method); From 8c8f6a28b2185df3559f27dfcb3aa5ee7ebfaf72 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 01:27:19 +0100 Subject: [PATCH 028/153] Quick implementation of access protection for the new property-based LazyLoadingGhost objects --- .../MethodGenerator/MagicGet.php | 66 +++++++-- .../PrivatePropertiesMap.php | 95 +++++++++++++ .../PropertyGenerator/PropertiesMap.php | 132 ++++++++++++++++++ .../ProtectedPropertiesMap.php | 93 ++++++++++++ .../LazyLoadingGhostGenerator.php | 11 +- 5 files changed, 384 insertions(+), 13 deletions(-) create mode 100644 src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMap.php create mode 100644 src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PropertiesMap.php create mode 100644 src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMap.php diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php index e4491930f..1a3f6dafd 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php @@ -20,6 +20,9 @@ use ProxyManager\Generator\MagicMethodGenerator; use ProxyManager\Generator\ParameterGenerator; +use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PrivatePropertiesMap; +use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PropertiesMap; +use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\ProtectedPropertiesMap; use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap; use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator; use ReflectionClass; @@ -35,29 +38,68 @@ 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 + * @param ReflectionClass $originalClass + * @param PropertyGenerator $initializerProperty + * @param MethodGenerator $callInitializer + * @param PublicPropertiesMap $publicProperties + * @param PrivatePropertiesMap $privateProperties + * @param ProtectedPropertiesMap $protectedProperties */ public function __construct( ReflectionClass $originalClass, PropertyGenerator $initializerProperty, MethodGenerator $callInitializer, - PublicPropertiesMap $publicProperties + PublicPropertiesMap $publicProperties, + PrivatePropertiesMap $privateProperties, + ProtectedPropertiesMap $protectedProperties ) { parent::__construct($originalClass, '__get', [new ParameterGenerator('name')]); - $override = $originalClass->hasMethod('__get'); - $callParent = ''; + $override = $originalClass->hasMethod('__get'); $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"; - //} + $callParentTemplate = <<<'PHP' +if (isset(self::$%s[$name])) { + return $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['class'] : ''; + $expectedType = self::$%s[$name]; + + if ($object instanceof $expectedType) { + return $this->$name; + } + + $class = isset($caller['object']) ? $caller['class'] : ''; + + if ($class === $expectedType || is_subclass_of($class, $expectedType)) { + return $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'] : ''; + + if (isset(self::$%s[$class][$name])) { + return $this->$name; + } +} + +PHP; + + $callParent = sprintf( + $callParentTemplate, + $publicProperties->getName(), + $protectedProperties->getName(), + $protectedProperties->getName(), + $privateProperties->getName() + ); if ($override) { $callParent .= 'return parent::__get($name);'; diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMap.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMap.php new file mode 100644 index 000000000..685f62346 --- /dev/null +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMap.php @@ -0,0 +1,95 @@ + + * @license MIT + */ +class PrivatePropertiesMap extends PropertyGenerator +{ + const KEY_DEFAULT_VALUE = 'defaultValue'; + + /** + * Constructor + */ + public function __construct(\ReflectionClass $originalClass) + { + 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 class name and property name' + ); + $this->setDefaultValue($this->getMap($originalClass)); + } + + /** + * @param \ReflectionClass $originalClass + * + * @return int[][]|mixed[][] + */ + private function getMap(\ReflectionClass $originalClass) + { + $map = []; + + foreach ($this->getProperties($originalClass) as $property) { + $class = & $map[$property->getDeclaringClass()->getName()]; + + $class[$property->getName()] = true; + } + + return $map; + } + + /** + * @param \ReflectionClass $originalClass + * + * @return \ReflectionProperty[] + */ + private function getProperties(\ReflectionClass $originalClass) + { + $class = $originalClass; + $properties = []; + + do { + $properties = array_merge( + $properties, + array_values(array_filter( + $class->getProperties(), + function (\ReflectionProperty $property) use ($class) { + return $property->getDeclaringClass()->getName() === $class->getName() + && ! $property->isStatic() + && $property->isPrivate(); + } + )) + ); + } while ($class = $class->getParentClass()); + + return $properties; + } +} diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PropertiesMap.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PropertiesMap.php new file mode 100644 index 000000000..beb18956f --- /dev/null +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PropertiesMap.php @@ -0,0 +1,132 @@ + + * @license MIT + */ +class PropertiesMap extends PropertyGenerator +{ + const KEY_VISIBILITY = 'visibility'; + const KEY_DEFAULT_VALUE = 'defaultValue'; + /** + * Constructor + */ + public function __construct(\ReflectionClass $originalClass) + { + parent::__construct( + UniqueIdentifierGenerator::getIdentifier('propertiesMap') + ); + + $this->setVisibility(self::VISIBILITY_PRIVATE); + $this->setStatic(true); + $this->setDocblock( + '@var array[][] visibility and default value of defined properties, indexed by class name and property name' + ); + $this->setDefaultValue($this->getMap($originalClass)); + } + + /** + * @param \ReflectionClass $originalClass + * + * @return int[][]|mixed[][] + */ + private function getMap(\ReflectionClass $originalClass) + { + $map = []; + + foreach ($this->getProperties($originalClass) as $property) { + $class = & $map[$property->getDeclaringClass()->getName()]; + + $class[$property->getName()] = [ + 'visibility' => $this->getPropertyVisibility($property), + 'defaultValue' => $this->getPropertyDefaultValue($property), + ]; + } + + return $map; + } + + /** + * @param \ReflectionProperty $property + * + * @return int + */ + private function getPropertyVisibility(\ReflectionProperty $property) + { + if ($property->isPrivate()) { + return \ReflectionProperty::IS_PRIVATE; + } + + if ($property->isProtected()) { + return \ReflectionProperty::IS_PROTECTED; + } + + return \ReflectionProperty::IS_PUBLIC; + } + + /** + * @param \ReflectionProperty $property + * + * @return mixed + */ + private function getPropertyDefaultValue(\ReflectionProperty $property) + { + $propertyName = $property->getName(); + $defaultValues = $property->getDeclaringClass()->getDefaultProperties(); + + if (! isset($defaultValues[$propertyName])) { + return null; + } + + return $defaultValues[$propertyName]; + } + + /** + * @param \ReflectionClass $originalClass + * + * @return \ReflectionProperty[] + */ + private function getProperties(\ReflectionClass $originalClass) + { + $class = $originalClass; + $properties = []; + + do { + $properties = array_merge( + $properties, + array_values(array_filter( + $class->getProperties(), + function (\ReflectionProperty $property) use ($class) { + return $property->getDeclaringClass()->getName() === $class->getName() + && ! $property->isStatic(); + } + )) + ); + } while ($class = $class->getParentClass()); + + return $properties; + } +} diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMap.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMap.php new file mode 100644 index 000000000..67fe3c0fa --- /dev/null +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMap.php @@ -0,0 +1,93 @@ + + * @license MIT + */ +class ProtectedPropertiesMap extends PropertyGenerator +{ + const KEY_DEFAULT_VALUE = 'defaultValue'; + + /** + * Constructor + */ + public function __construct(\ReflectionClass $originalClass) + { + parent::__construct( + UniqueIdentifierGenerator::getIdentifier('privateProperties') + ); + + $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($originalClass)); + } + + /** + * @param \ReflectionClass $originalClass + * + * @return int[][]|mixed[][] + */ + private function getMap(\ReflectionClass $originalClass) + { + $map = []; + + foreach ($this->getProperties($originalClass) as $property) { + $map[$property->getName()] = $property->getDeclaringClass()->getName(); + } + + return $map; + } + + /** + * @param \ReflectionClass $originalClass + * + * @return \ReflectionProperty[] + */ + private function getProperties(\ReflectionClass $originalClass) + { + $class = $originalClass; + $properties = []; + + do { + $properties = array_merge( + $properties, + array_values(array_filter( + $class->getProperties(), + function (\ReflectionProperty $property) use ($class) { + return $property->getDeclaringClass()->getName() === $class->getName() + && ! $property->isStatic() + && $property->isProtected(); + } + )) + ); + } while ($class = $class->getParentClass()); + + return $properties; + } +} diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php index 0c05c54a5..13d5e59dd 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php @@ -36,6 +36,9 @@ use ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\SetProxyInitializer; use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\InitializationTracker; use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\InitializerProperty; +use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PrivatePropertiesMap; +use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PropertiesMap; +use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\ProtectedPropertiesMap; use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesDefaults; use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap; use ProxyManager\ProxyGenerator\Util\ProxiedMethodsFilter; @@ -64,6 +67,9 @@ public function generate(ReflectionClass $originalClass, ClassGenerator $classGe $interfaces = [GhostObjectInterface::class]; $publicProperties = new PublicPropertiesMap($originalClass); + $allProperties = new PropertiesMap($originalClass); + $privateProperties = new PrivatePropertiesMap($originalClass); + $protectedProperties = new ProtectedPropertiesMap($originalClass); $publicPropsDefaults = new PublicPropertiesDefaults($originalClass); if ($originalClass->isInterface()) { @@ -77,6 +83,9 @@ public function generate(ReflectionClass $originalClass, ClassGenerator $classGe $classGenerator->addPropertyFromGenerator($initializationTracker = new InitializationTracker()); $classGenerator->addPropertyFromGenerator($publicProperties); $classGenerator->addPropertyFromGenerator($publicPropsDefaults); + $classGenerator->addPropertyFromGenerator($allProperties); + $classGenerator->addPropertyFromGenerator($privateProperties); + $classGenerator->addPropertyFromGenerator($protectedProperties); $init = new CallInitializer($initializer, $publicPropsDefaults, $initializationTracker); @@ -98,7 +107,7 @@ function (ReflectionMethod $method) use ($initializer, $init) { [ $init, new StaticProxyConstructor($originalClass, $initializer), - new MagicGet($originalClass, $initializer, $init, $publicProperties), + new MagicGet($originalClass, $initializer, $init, $publicProperties, $privateProperties, $protectedProperties), new MagicSet($originalClass, $initializer, $init, $publicProperties), new MagicIsset($originalClass, $initializer, $init, $publicProperties), new MagicUnset($originalClass, $initializer, $init, $publicProperties), From 51b59fde977470ec372d75da79ede3be8aa2bfbb Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 01:30:26 +0100 Subject: [PATCH 029/153] Swapping private/protected properties map parameters --- .../LazyLoadingGhost/MethodGenerator/MagicGet.php | 6 +++--- .../ProxyGenerator/LazyLoadingGhostGenerator.php | 9 ++++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php index 1a3f6dafd..8814fe0b0 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php @@ -42,16 +42,16 @@ class MagicGet extends MagicMethodGenerator * @param PropertyGenerator $initializerProperty * @param MethodGenerator $callInitializer * @param PublicPropertiesMap $publicProperties - * @param PrivatePropertiesMap $privateProperties * @param ProtectedPropertiesMap $protectedProperties + * @param PrivatePropertiesMap $privateProperties */ public function __construct( ReflectionClass $originalClass, PropertyGenerator $initializerProperty, MethodGenerator $callInitializer, PublicPropertiesMap $publicProperties, - PrivatePropertiesMap $privateProperties, - ProtectedPropertiesMap $protectedProperties + ProtectedPropertiesMap $protectedProperties, + PrivatePropertiesMap $privateProperties ) { parent::__construct($originalClass, '__get', [new ParameterGenerator('name')]); diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php index 13d5e59dd..992952562 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php @@ -107,7 +107,14 @@ function (ReflectionMethod $method) use ($initializer, $init) { [ $init, new StaticProxyConstructor($originalClass, $initializer), - new MagicGet($originalClass, $initializer, $init, $publicProperties, $privateProperties, $protectedProperties), + new MagicGet( + $originalClass, + $initializer, + $init, + $publicProperties, + $protectedProperties, + $privateProperties + ), new MagicSet($originalClass, $initializer, $init, $publicProperties), new MagicIsset($originalClass, $initializer, $init, $publicProperties), new MagicUnset($originalClass, $initializer, $init, $publicProperties), From 4599b680b960e4ee11a63c318753cc46f0b8c888 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 01:32:34 +0100 Subject: [PATCH 030/153] Protecting `isset()` access to private/protected properties of ghost objects --- .../MethodGenerator/MagicIsset.php | 63 +++++++++++++++---- .../LazyLoadingGhostGenerator.php | 9 ++- 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php index 4011ef7f7..5008aa9fd 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php @@ -20,6 +20,8 @@ use ProxyManager\Generator\MagicMethodGenerator; use ProxyManager\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,29 +37,68 @@ 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 + * @param ReflectionClass $originalClass + * @param PropertyGenerator $initializerProperty + * @param MethodGenerator $callInitializer + * @param PublicPropertiesMap $publicProperties + * @param ProtectedPropertiesMap $protectedProperties + * @param PrivatePropertiesMap $privateProperties */ public function __construct( ReflectionClass $originalClass, PropertyGenerator $initializerProperty, MethodGenerator $callInitializer, - PublicPropertiesMap $publicProperties + PublicPropertiesMap $publicProperties, + ProtectedPropertiesMap $protectedProperties, + PrivatePropertiesMap $privateProperties ) { parent::__construct($originalClass, '__isset', [new ParameterGenerator('name')]); $override = $originalClass->hasMethod('__isset'); - $callParent = ''; $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"; - //} + $callParentTemplate = <<<'PHP' +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['class'] : ''; + $expectedType = self::$%s[$name]; + + if ($object instanceof $expectedType) { + return $this->$name; + } + + $class = isset($caller['object']) ? $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'] : ''; + + if (isset(self::$%s[$class][$name])) { + return isset($this->$name); + } +} + +PHP; + + $callParent = sprintf( + $callParentTemplate, + $publicProperties->getName(), + $protectedProperties->getName(), + $protectedProperties->getName(), + $privateProperties->getName() + ); if ($override) { $callParent .= 'return parent::__isset($name);'; diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php index 992952562..546920f67 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php @@ -116,7 +116,14 @@ function (ReflectionMethod $method) use ($initializer, $init) { $privateProperties ), new MagicSet($originalClass, $initializer, $init, $publicProperties), - new MagicIsset($originalClass, $initializer, $init, $publicProperties), + new MagicIsset( + $originalClass, + $initializer, + $init, + $publicProperties, + $protectedProperties, + $privateProperties + ), new MagicUnset($originalClass, $initializer, $init, $publicProperties), new MagicClone($originalClass, $initializer, $init, $publicProperties), new MagicSleep($originalClass, $initializer, $init, $publicProperties), From 561da4edafe33c5f66ac091d3f042d9c976b25c3 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 01:49:48 +0100 Subject: [PATCH 031/153] Protecting `unset()` access to private/protected properties of ghost objects --- .../MethodGenerator/MagicUnset.php | 66 +++++++++++++++---- .../LazyLoadingGhostGenerator.php | 9 ++- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php index 24973b6ff..fba0f0660 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php @@ -20,6 +20,8 @@ use ProxyManager\Generator\MagicMethodGenerator; use ProxyManager\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,30 +37,68 @@ 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 + * @param ReflectionClass $originalClass + * @param PropertyGenerator $initializerProperty + * @param MethodGenerator $callInitializer + * @param PublicPropertiesMap $publicProperties + * @param ProtectedPropertiesMap $protectedProperties + * @param PrivatePropertiesMap $privateProperties */ public function __construct( ReflectionClass $originalClass, PropertyGenerator $initializerProperty, MethodGenerator $callInitializer, - PublicPropertiesMap $publicProperties + PublicPropertiesMap $publicProperties, + ProtectedPropertiesMap $protectedProperties, + PrivatePropertiesMap $privateProperties ) { parent::__construct($originalClass, '__unset', [new ParameterGenerator('name')]); - $override = $originalClass->hasMethod('__unset'); - $callParent = ''; + $override = $originalClass->hasMethod('__unset'); $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"; - //} + $callParentTemplate = <<<'PHP' +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['class'] : ''; + $expectedType = self::$%s[$name]; + + if ($object instanceof $expectedType) { + return $this->$name; + } + + $class = isset($caller['object']) ? $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'] : ''; + + if (isset(self::$%s[$class][$name])) { + return isset($this->$name); + } +} + +PHP; + + $callParent = sprintf( + $callParentTemplate, + $publicProperties->getName(), + $protectedProperties->getName(), + $protectedProperties->getName(), + $privateProperties->getName() + ); if ($override) { $callParent .= "return parent::__unset(\$name);"; diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php index 546920f67..e161a239c 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php @@ -124,7 +124,14 @@ function (ReflectionMethod $method) use ($initializer, $init) { $protectedProperties, $privateProperties ), - new MagicUnset($originalClass, $initializer, $init, $publicProperties), + new MagicUnset( + $originalClass, + $initializer, + $init, + $publicProperties, + $protectedProperties, + $privateProperties + ), new MagicClone($originalClass, $initializer, $init, $publicProperties), new MagicSleep($originalClass, $initializer, $init, $publicProperties), new SetProxyInitializer($initializer), From 1c2bf2b924110ee62ca55627fd9d89485d867b4c Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 01:51:08 +0100 Subject: [PATCH 032/153] s/isset/unset --- .../LazyLoadingGhost/MethodGenerator/MagicUnset.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php index fba0f0660..4b3d8a28d 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php @@ -71,13 +71,17 @@ public function __construct( $expectedType = self::$%s[$name]; if ($object instanceof $expectedType) { - return $this->$name; + unset($this->$name); + + return; } $class = isset($caller['object']) ? $caller['class'] : ''; if ($class === $expectedType || is_subclass_of($class, $expectedType)) { - return isset($this->$name); + unset($this->$name); + + return; } } else { // check private property access via same class @@ -86,7 +90,9 @@ public function __construct( $class = isset($caller['class']) ? $caller['class'] : ''; if (isset(self::$%s[$class][$name])) { - return isset($this->$name); + unset($this->$name); + + return; } } From e1200951970c1f10f78be72e606dce376b12eb4e Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 01:53:36 +0100 Subject: [PATCH 033/153] s/isset/unset --- .../LazyLoadingGhost/MethodGenerator/MagicUnset.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php index 4b3d8a28d..d25603737 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php @@ -60,7 +60,9 @@ public function __construct( $callParentTemplate = <<<'PHP' if (isset(self::$%s[$name])) { - return isset($this->$name); + unset($this->$name); + + return; } if (isset(self::$%s[$name])) { From 60fa1cd2560e04a08ac7477bbb1fd777e0ab5a61 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 02:05:58 +0100 Subject: [PATCH 034/153] Typo fixes (wrong array keys used) --- .../MethodGenerator/MagicGet.php | 4 +- .../MethodGenerator/MagicIsset.php | 4 +- .../MethodGenerator/MagicSet.php | 63 +++++++++++++++---- .../MethodGenerator/MagicUnset.php | 4 +- 4 files changed, 58 insertions(+), 17 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php index 8814fe0b0..5b9c22b46 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php @@ -68,14 +68,14 @@ public function __construct( // 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['class'] : ''; + $object = isset($caller['object']) ? $caller['object'] : ''; $expectedType = self::$%s[$name]; if ($object instanceof $expectedType) { return $this->$name; } - $class = isset($caller['object']) ? $caller['class'] : ''; + $class = isset($caller['class']) ? $caller['class'] : ''; if ($class === $expectedType || is_subclass_of($class, $expectedType)) { return $this->$name; diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php index 5008aa9fd..7fa53d01f 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php @@ -67,14 +67,14 @@ public function __construct( // 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['class'] : ''; + $object = isset($caller['object']) ? $caller['object'] : ''; $expectedType = self::$%s[$name]; if ($object instanceof $expectedType) { return $this->$name; } - $class = isset($caller['object']) ? $caller['class'] : ''; + $class = isset($caller['class']) ? $caller['class'] : ''; if ($class === $expectedType || is_subclass_of($class, $expectedType)) { return isset($this->$name); diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php index 82a5e100e..1a2818413 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php @@ -20,6 +20,8 @@ use ProxyManager\Generator\MagicMethodGenerator; use ProxyManager\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,16 +37,20 @@ 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 + * @param ReflectionClass $originalClass + * @param PropertyGenerator $initializerProperty + * @param MethodGenerator $callInitializer + * @param PublicPropertiesMap $publicProperties + * @param ProtectedPropertiesMap $protectedProperties + * @param PrivatePropertiesMap $privateProperties */ public function __construct( ReflectionClass $originalClass, PropertyGenerator $initializerProperty, MethodGenerator $callInitializer, - PublicPropertiesMap $publicProperties + PublicPropertiesMap $publicProperties, + ProtectedPropertiesMap $protectedProperties, + PrivatePropertiesMap $privateProperties ) { parent::__construct( $originalClass, @@ -53,15 +59,50 @@ public function __construct( ); $override = $originalClass->hasMethod('__set'); - $callParent = ''; $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"; - //} + $callParentTemplate = <<<'PHP' +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 === $this || $object instanceof $expectedType) { + return ($this->$name = $value); + } + + $class = isset($caller['class']) ? $caller['class'] : ''; + + if ($class === $expectedType || is_subclass_of($class, $expectedType)) { + return ($this->$name = $value); + } +} 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'] : ''; + + if (isset(self::$%s[$class][$name])) { + return ($this->$name = $value); + } +} + +PHP; + + $callParent = sprintf( + $callParentTemplate, + $publicProperties->getName(), + $protectedProperties->getName(), + $protectedProperties->getName(), + $privateProperties->getName() + ); if ($override) { $callParent .= 'return parent::__set($name, $value);'; diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php index d25603737..a72276364 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php @@ -69,7 +69,7 @@ public function __construct( // 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['class'] : ''; + $object = isset($caller['object']) ? $caller['object'] : ''; $expectedType = self::$%s[$name]; if ($object instanceof $expectedType) { @@ -78,7 +78,7 @@ public function __construct( return; } - $class = isset($caller['object']) ? $caller['class'] : ''; + $class = isset($caller['class']) ? $caller['class'] : ''; if ($class === $expectedType || is_subclass_of($class, $expectedType)) { unset($this->$name); From a5b1a1d70eb32c56496904235927f9f855be2ea6 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 02:07:00 +0100 Subject: [PATCH 035/153] The proxy itself must have privileged access to properties of the object, crossing visibility boundaries --- .../LazyLoadingGhost/MethodGenerator/MagicGet.php | 2 +- .../LazyLoadingGhost/MethodGenerator/MagicIsset.php | 2 +- .../LazyLoadingGhost/MethodGenerator/MagicSet.php | 2 +- .../LazyLoadingGhost/MethodGenerator/MagicUnset.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php index 5b9c22b46..b9fd3df51 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php @@ -86,7 +86,7 @@ public function __construct( $caller = isset($callers[1]) ? $callers[1] : []; $class = isset($caller['class']) ? $caller['class'] : ''; - if (isset(self::$%s[$class][$name])) { + if ($class === __CLASS__ || isset(self::$%s[$class][$name])) { return $this->$name; } } diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php index 7fa53d01f..db2411540 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php @@ -85,7 +85,7 @@ public function __construct( $caller = isset($callers[1]) ? $callers[1] : []; $class = isset($caller['class']) ? $caller['class'] : ''; - if (isset(self::$%s[$class][$name])) { + if ($class === __CLASS__ || isset(self::$%s[$class][$name])) { return isset($this->$name); } } diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php index 1a2818413..4fa2e9e6e 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php @@ -89,7 +89,7 @@ public function __construct( $caller = isset($callers[1]) ? $callers[1] : []; $class = isset($caller['class']) ? $caller['class'] : ''; - if (isset(self::$%s[$class][$name])) { + if ($class === __CLASS__ || isset(self::$%s[$class][$name])) { return ($this->$name = $value); } } diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php index a72276364..e150d31bc 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php @@ -91,7 +91,7 @@ public function __construct( $caller = isset($callers[1]) ? $callers[1] : []; $class = isset($caller['class']) ? $caller['class'] : ''; - if (isset(self::$%s[$class][$name])) { + if ($class === __CLASS__ || isset(self::$%s[$class][$name])) { unset($this->$name); return; From 3fd68cd0d9a91fa8aee2feaadda70b467b1781f8 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 02:11:20 +0100 Subject: [PATCH 036/153] Correcting parameters passed to `MagicSet` constructor --- .../ProxyGenerator/LazyLoadingGhostGenerator.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php index e161a239c..bc698bed5 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php @@ -115,7 +115,14 @@ function (ReflectionMethod $method) use ($initializer, $init) { $protectedProperties, $privateProperties ), - new MagicSet($originalClass, $initializer, $init, $publicProperties), + new MagicSet( + $originalClass, + $initializer, + $init, + $publicProperties, + $protectedProperties, + $privateProperties + ), new MagicIsset( $originalClass, $initializer, From 60b854be468dd4ee2cc6d515f1d57c46965a9e78 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 02:48:21 +0100 Subject: [PATCH 037/153] Syntax error fix, using closure to access the private property --- .../LazyLoadingGhost/MethodGenerator/MagicSet.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php index 4fa2e9e6e..7a2684021 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php @@ -83,14 +83,16 @@ public function __construct( if ($class === $expectedType || is_subclass_of($class, $expectedType)) { return ($this->$name = $value); } -} else { +} 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'] : ''; - if ($class === __CLASS__ || isset(self::$%s[$class][$name])) { - return ($this->$name = $value); + if ($class === __CLASS__ || $class === \ReflectionProperty::class || isset(self::$%s[$name][$class])) { + return \Closure::bind(function () use ($name, $value) { + return ($this->$name = $value); + }, $this, %s)->__invoke(); } } @@ -101,7 +103,9 @@ public function __construct( $publicProperties->getName(), $protectedProperties->getName(), $protectedProperties->getName(), - $privateProperties->getName() + $privateProperties->getName(), + $privateProperties->getName(), + var_export($originalClass->getName(), true) ); if ($override) { From 2d6df0fbc1db53564bad1afaf96198b49ca3b435 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 02:48:37 +0100 Subject: [PATCH 038/153] Minor performance optimization --- .../LazyLoadingGhost/MethodGenerator/MagicGet.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php index b9fd3df51..f764f07fa 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php @@ -80,7 +80,7 @@ public function __construct( if ($class === $expectedType || is_subclass_of($class, $expectedType)) { return $this->$name; } -} else { +} 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] : []; @@ -98,6 +98,7 @@ public function __construct( $publicProperties->getName(), $protectedProperties->getName(), $protectedProperties->getName(), + $privateProperties->getName(), $privateProperties->getName() ); From df7a30353bfabb76c747e72b4cf06430ce72521f Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 02:48:58 +0100 Subject: [PATCH 039/153] Private properties are now indexed by property name first --- .../PropertyGenerator/PrivatePropertiesMap.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMap.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMap.php index 685f62346..38669731f 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMap.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMap.php @@ -43,7 +43,7 @@ public function __construct(\ReflectionClass $originalClass) $this->setVisibility(self::VISIBILITY_PRIVATE); $this->setStatic(true); $this->setDocblock( - '@var array[][] visibility and default value of defined properties, indexed by class name and property name' + '@var array[][] visibility and default value of defined properties, indexed by property name and class name' ); $this->setDefaultValue($this->getMap($originalClass)); } @@ -58,9 +58,9 @@ private function getMap(\ReflectionClass $originalClass) $map = []; foreach ($this->getProperties($originalClass) as $property) { - $class = & $map[$property->getDeclaringClass()->getName()]; + $propertyKey = & $map[$property->getName()]; - $class[$property->getName()] = true; + $propertyKey[$property->getDeclaringClass()->getName()] = true; } return $map; From 92864d88567a415f4bfb893689f2eb01b42416eb Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 05:25:29 +0100 Subject: [PATCH 040/153] Re-implemented initializer call so that it fetches all reflection properties by references and passes them to the initializer closure --- .../MethodGenerator/CallInitializer.php | 88 ++++++++++-- .../LazyLoadingGhostGenerator.php | 7 +- .../ProxyGenerator/Util/Properties.php | 127 ++++++++++++++++++ 3 files changed, 213 insertions(+), 9 deletions(-) create mode 100644 src/ProxyManager/ProxyGenerator/Util/Properties.php diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php index 132910a9f..507e138fd 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php @@ -21,6 +21,11 @@ use ProxyManager\Generator\MethodGenerator; use ProxyManager\Generator\ParameterGenerator; use ProxyManager\Generator\Util\UniqueIdentifierGenerator; +use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PrivatePropertiesMap; +use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\ProtectedPropertiesMap; +use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap; +use ProxyManager\ProxyGenerator\Util\Properties; +use ReflectionProperty; use Zend\Code\Generator\PropertyGenerator; /** @@ -36,13 +41,13 @@ class CallInitializer extends MethodGenerator * Constructor * * @param PropertyGenerator $initializerProperty - * @param PropertyGenerator $publicPropsDefaults * @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"); @@ -60,13 +65,80 @@ public function __construct( $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" - // @todo should use closures for private properties here - . " \$this->\$key = \$default;\n" - . "}\n\n" + . $this->propertiesInitializationCode($properties) + . $this->propertiesReferenceArrayCode($properties) . '$this->' . $initializer . '->__invoke' - . '($this, $methodName, $parameters, $this->' . $initializer . ');' . "\n\n" + . '($this, $methodName, $parameters, $this->' . $initializer . ', $properties);' . "\n\n" . "\$this->" . $initialization . " = false;" ); } + + /** + * @param Properties $properties + * + * @return string + */ + private function propertiesInitializationCode(Properties $properties) + { + $assignments = []; + + foreach ($properties->getAccessibleProperties() as $property) { + $assignments[] = '$this->' + . $property->getName() + . ' = ' . $this->getExportedPropertyDefaultValue($property) + . ';'; + } + + foreach ($properties->getPrivateProperties() as $property) { + $name = $property->getName(); + $assignments[] = "\\Closure::bind(function (\$object) {\n" + . ' $object->' . $name . ' = ' . $this->getExportedPropertyDefaultValue($property) . ";\n" + . "}, null, " . var_export($property->getDeclaringClass()->getName(), true) . ")->__invoke(\$this)" + . ';'; + } + + return implode("\n", $assignments) . "\n\n"; + } + + /** + * @param Properties $properties + * + * @return string + */ + private function propertiesReferenceArrayCode(Properties $properties) + { + $assignments = []; + + foreach ($properties->getAccessibleProperties() as $propertyInternalName => $property) { + $assignments[] = ' ' + . var_export($propertyInternalName, true) . ' => & $this->' . $property->getName() + . ','; + } + + foreach ($properties->getPrivateProperties() as $propertyInternalName => $property) { + $name = $property->getName(); + $declaringClass = $property->getDeclaringClass()->getName(); + $assignments[] = ' ' + . var_export($propertyInternalName, true) + . " => \\Closure::bind(function & (\$object) {\n" + . ' return $object->' . $name . ";\n" + . " }, null, " . var_export($declaringClass, true) . ")->__invoke(\$this)" + . ','; + } + + return "\$properties = [\n" . implode("\n", $assignments) . "\n];\n\n"; + } + + /** + * @param ReflectionProperty $property + * + * @return string + */ + private function getExportedPropertyDefaultValue(ReflectionProperty $property) + { + $name = $property->getName(); + $defaults = $property->getDeclaringClass()->getDefaultProperties(); + + return var_export(isset($defaults[$name]) ? $defaults[$name] : null, true); + } } diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php index bc698bed5..f40a225e9 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php @@ -41,6 +41,7 @@ use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\ProtectedPropertiesMap; use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesDefaults; use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap; +use ProxyManager\ProxyGenerator\Util\Properties; use ProxyManager\ProxyGenerator\Util\ProxiedMethodsFilter; use ReflectionClass; use ReflectionMethod; @@ -87,7 +88,11 @@ public function generate(ReflectionClass $originalClass, ClassGenerator $classGe $classGenerator->addPropertyFromGenerator($privateProperties); $classGenerator->addPropertyFromGenerator($protectedProperties); - $init = new CallInitializer($initializer, $publicPropsDefaults, $initializationTracker); + $init = new CallInitializer( + $initializer, + $initializationTracker, + Properties::fromReflectionClass($originalClass) + ); array_map( function (MethodGenerator $generatedMethod) use ($originalClass, $classGenerator) { diff --git a/src/ProxyManager/ProxyGenerator/Util/Properties.php b/src/ProxyManager/ProxyGenerator/Util/Properties.php new file mode 100644 index 000000000..e57b04eea --- /dev/null +++ b/src/ProxyManager/ProxyGenerator/Util/Properties.php @@ -0,0 +1,127 @@ + + * @license MIT + */ +final class Properties +{ + /** + * @var array|\ReflectionProperty[] + */ + private $properties; + + /** + * @param ReflectionProperty[] $properties + */ + private function __construct(array $properties) + { + $this->properties = $properties; + } + + /** + * @param ReflectionClass $reflection + * + * @return self + */ + public static function fromReflectionClass(ReflectionClass $reflection) + { + $class = $reflection; + $properties = []; + + do { + $properties = array_merge( + $properties, + array_values(array_filter( + $class->getProperties(), + function (ReflectionProperty $property) use ($class) { + return $class->getName() === $property->getDeclaringClass()->getName(); + } + )) + ); + } while ($class = $class->getParentClass()); + + return new self($properties); + } + + /** + * @return ReflectionProperty[] indexed by the property internal visibility-aware name + */ + public function getPublicProperties() + { + $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() + { + $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() + { + $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() + { + return array_merge($this->getPublicProperties(), $this->getProtectedProperties()); + } +} From 5d5050fb0da03f12a971b3a0f2c19c4933a66570 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 07:25:33 +0100 Subject: [PATCH 041/153] Reverting PoC change --- .../PropertyGenerator/PublicPropertiesDefaults.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesDefaults.php b/src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesDefaults.php index 915f92099..2aa395ce7 100644 --- a/src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesDefaults.php +++ b/src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesDefaults.php @@ -45,7 +45,7 @@ public function __construct(ReflectionClass $originalClass) $defaults = $originalClass->getDefaultProperties(); - foreach ($originalClass->getProperties() as $publicProperty) { + foreach ($originalClass->getProperties(ReflectionProperty::IS_PUBLIC) as $publicProperty) { $name = $publicProperty->getName(); $this->publicProperties[$name] = $defaults[$name]; } From ede012d3e23606ea46d8728ae2e6a1b7c3e25669 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 07:28:51 +0100 Subject: [PATCH 042/153] Fixed prefix for the `ProtectedPropertiesMap` --- .../PropertyGenerator/ProtectedPropertiesMap.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMap.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMap.php index 67fe3c0fa..d853d7922 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMap.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMap.php @@ -37,7 +37,7 @@ class ProtectedPropertiesMap extends PropertyGenerator public function __construct(\ReflectionClass $originalClass) { parent::__construct( - UniqueIdentifierGenerator::getIdentifier('privateProperties') + UniqueIdentifierGenerator::getIdentifier('protectedProperties') ); $this->setVisibility(self::VISIBILITY_PRIVATE); From ea366ca741e832191b71107113c34620de2b632d Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 07:29:40 +0100 Subject: [PATCH 043/153] Rewrote initialization to allow fetching all properties in a by-ref array when calling the initializer --- .../MethodGenerator/CallInitializer.php | 49 +++++++++++++------ 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php index 507e138fd..3288f618e 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php @@ -62,15 +62,31 @@ public function __construct( $initializer = $initializerProperty->getName(); $initialization = $initTracker->getName(); - $this->setBody( - 'if ($this->' . $initialization . ' || ! $this->' . $initializer . ') {' . "\n return;\n}\n\n" - . "\$this->" . $initialization . " = true;\n\n" - . $this->propertiesInitializationCode($properties) - . $this->propertiesReferenceArrayCode($properties) - . '$this->' . $initializer . '->__invoke' - . '($this, $methodName, $parameters, $this->' . $initializer . ', $properties);' . "\n\n" - . "\$this->" . $initialization . " = false;" - ); + $bodyTemplate = <<<'PHP' +if ($this->%s || ! $this->%s) { + return; +} + +$this->%s = true; + +%s +%s + +$this->%s->__invoke($this, $methodName, $parameters, $this->%s, $properties); +$this->%s = false; +PHP; + + $this->setBody(sprintf( + $bodyTemplate, + $initialization, + $initializer, + $initialization, + $this->propertiesInitializationCode($properties), + $this->propertiesReferenceArrayCode($properties), + $initializer, + $initializer, + $initialization + )); } /** @@ -115,18 +131,19 @@ private function propertiesReferenceArrayCode(Properties $properties) . ','; } + $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->getPrivateProperties() as $propertyInternalName => $property) { $name = $property->getName(); $declaringClass = $property->getDeclaringClass()->getName(); - $assignments[] = ' ' - . var_export($propertyInternalName, true) - . " => \\Closure::bind(function & (\$object) {\n" - . ' return $object->' . $name . ";\n" - . " }, null, " . var_export($declaringClass, true) . ")->__invoke(\$this)" - . ','; + $code .= '$properties[' . var_export($propertyInternalName, true) . ']' + . " = & \\Closure::bind(function & () {\n" + . ' return $this->' . $name . ";\n" + . "}, \$this, " . var_export($declaringClass, true) . ")->__invoke(\$this);\n\n"; } - return "\$properties = [\n" . implode("\n", $assignments) . "\n];\n\n"; + return $code; } /** From cdac5759b7e947e4413c7bd23698932d7334eac7 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 07:30:06 +0100 Subject: [PATCH 044/153] Magic getter should always bind closures to a valid class --- .../MethodGenerator/MagicGet.php | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php index f764f07fa..e7d1e2f51 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php @@ -21,7 +21,6 @@ use ProxyManager\Generator\MagicMethodGenerator; use ProxyManager\Generator\ParameterGenerator; use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PrivatePropertiesMap; -use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PropertiesMap; use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\ProtectedPropertiesMap; use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap; use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator; @@ -77,7 +76,7 @@ public function __construct( $class = isset($caller['class']) ? $caller['class'] : ''; - if ($class === $expectedType || is_subclass_of($class, $expectedType)) { + if ($class === $expectedType || is_subclass_of($class, $expectedType) || $class === 'ReflectionProperty') { return $this->$name; } } elseif (isset(self::$%s[$name])) { @@ -86,8 +85,16 @@ public function __construct( $caller = isset($callers[1]) ? $callers[1] : []; $class = isset($caller['class']) ? $caller['class'] : ''; - if ($class === __CLASS__ || isset(self::$%s[$class][$name])) { - return $this->$name; + if (isset(self::$%s[$name][$class])) { + return \Closure::bind(function & () use ($name) { + return $this->$name; + }, $this, $class)->__invoke($this, $name); + } + + if ($class === __CLASS__ || $class === 'ReflectionProperty') { + return \Closure::bind(function & () use ($name) { + return $this->$name; + }, $this, key(self::$%s[$name]))->__invoke($this, $name); } } @@ -99,6 +106,9 @@ public function __construct( $protectedProperties->getName(), $protectedProperties->getName(), $privateProperties->getName(), + $privateProperties->getName(), + $privateProperties->getName(), + $privateProperties->getName(), $privateProperties->getName() ); From b35abcd2a7710ea56db5773c39dca823f9877912 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 07:36:29 +0100 Subject: [PATCH 045/153] Always bind to correct classes, never to the current proxy class or just the parent class --- .../LazyLoadingGhost/MethodGenerator/MagicGet.php | 2 +- .../MethodGenerator/MagicIsset.php | 14 ++++++++++++-- .../LazyLoadingGhost/MethodGenerator/MagicSet.php | 13 ++++++++++--- .../MethodGenerator/MagicUnset.php | 14 +++++++++++--- 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php index e7d1e2f51..fc324fd6b 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php @@ -91,7 +91,7 @@ public function __construct( }, $this, $class)->__invoke($this, $name); } - if ($class === __CLASS__ || $class === 'ReflectionProperty') { + if ($class === 'ReflectionProperty') { return \Closure::bind(function & () use ($name) { return $this->$name; }, $this, key(self::$%s[$name]))->__invoke($this, $name); diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php index db2411540..6e2d77240 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php @@ -85,8 +85,16 @@ public function __construct( $caller = isset($callers[1]) ? $callers[1] : []; $class = isset($caller['class']) ? $caller['class'] : ''; - if ($class === __CLASS__ || isset(self::$%s[$class][$name])) { - return isset($this->$name); + if (isset(self::$%s[$name][$class])) { + return \Closure::bind(function () use ($name) { + return isset($this->$name); + }, $this, $class)->__invoke($this, $name); + } + + if ($class === 'ReflectionProperty') { + return \Closure::bind(function () use ($name) { + return isset($this->$name); + }, $this, key(self::$%s[$name]))->__invoke($this, $name); } } @@ -97,6 +105,8 @@ public function __construct( $publicProperties->getName(), $protectedProperties->getName(), $protectedProperties->getName(), + $privateProperties->getName(), + $privateProperties->getName(), $privateProperties->getName() ); diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php index 7a2684021..111726b2e 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php @@ -89,10 +89,16 @@ public function __construct( $caller = isset($callers[1]) ? $callers[1] : []; $class = isset($caller['class']) ? $caller['class'] : ''; - if ($class === __CLASS__ || $class === \ReflectionProperty::class || isset(self::$%s[$name][$class])) { + if (isset(self::$%s[$name][$class])) { return \Closure::bind(function () use ($name, $value) { return ($this->$name = $value); - }, $this, %s)->__invoke(); + }, $this, $class)->__invoke($this, $name); + } + + if ($class === 'ReflectionProperty') { + return \Closure::bind(function () use ($name, $value) { + return ($this->$name = $value); + }, $this, key(self::$%s[$name]))->__invoke($this, $name); } } @@ -105,7 +111,8 @@ public function __construct( $protectedProperties->getName(), $privateProperties->getName(), $privateProperties->getName(), - var_export($originalClass->getName(), true) + $privateProperties->getName(), + $privateProperties->getName() ); if ($override) { diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php index e150d31bc..c7085937e 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php @@ -91,10 +91,16 @@ public function __construct( $caller = isset($callers[1]) ? $callers[1] : []; $class = isset($caller['class']) ? $caller['class'] : ''; - if ($class === __CLASS__ || isset(self::$%s[$class][$name])) { - unset($this->$name); + if (isset(self::$%s[$name][$class])) { + return \Closure::bind(function () use ($name) { + unset($this->$name); + }, $this, $class)->__invoke($this, $name); + } - return; + if ($class === 'ReflectionProperty') { + return \Closure::bind(function () use ($name) { + unset($this->$name); + }, $this, key(self::$%s[$name]))->__invoke($this, $name); } } @@ -105,6 +111,8 @@ public function __construct( $publicProperties->getName(), $protectedProperties->getName(), $protectedProperties->getName(), + $privateProperties->getName(), + $privateProperties->getName(), $privateProperties->getName() ); From f85b73fc197863725c3bd7a0d3d04f3f26713194 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 07:50:18 +0100 Subject: [PATCH 046/153] Test asset for classes with colliding inherited properties --- ...ithCollidingPrivateInheritedProperties.php | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 tests/ProxyManagerTestAsset/ClassWithCollidingPrivateInheritedProperties.php diff --git a/tests/ProxyManagerTestAsset/ClassWithCollidingPrivateInheritedProperties.php b/tests/ProxyManagerTestAsset/ClassWithCollidingPrivateInheritedProperties.php new file mode 100644 index 000000000..62a9ed1c8 --- /dev/null +++ b/tests/ProxyManagerTestAsset/ClassWithCollidingPrivateInheritedProperties.php @@ -0,0 +1,33 @@ + + * @license MIT + */ +class ClassWithCollidingPrivateInheritedProperties extends ClassWithPrivateProperties +{ + /** + * @var string + */ + private $property0 = 'childClassProperty0'; +} From 2533a6a1035e24985d80fb0d9b1bec7de24a33db Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 07:57:52 +0100 Subject: [PATCH 047/153] Verifying that inheritance default values are respected --- .../LazyLoadingGhostFunctionalTest.php | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php index b86301ed6..663ab6bdc 100644 --- a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php +++ b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php @@ -20,12 +20,15 @@ use PHPUnit_Framework_MockObject_MockObject as Mock; use PHPUnit_Framework_TestCase; +use ProxyManager\FileLocator\FileLocator; use ProxyManager\Generator\ClassGenerator; use ProxyManager\Generator\Util\UniqueIdentifierGenerator; use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy; +use ProxyManager\GeneratorStrategy\FileWriterGeneratorStrategy; use ProxyManager\Proxy\GhostObjectInterface; use ProxyManager\ProxyGenerator\LazyLoadingGhostGenerator; use ProxyManagerTestAsset\BaseClass; +use ProxyManagerTestAsset\ClassWithCollidingPrivateInheritedProperties; use ProxyManagerTestAsset\ClassWithCounterConstructor; use ProxyManagerTestAsset\ClassWithPrivateProperties; use ProxyManagerTestAsset\ClassWithProtectedProperties; @@ -321,6 +324,24 @@ public function testPrivatePropertyDefaultWillBePreserved() $this->assertSame('property0', $reflectionProperty->getValue($proxy)); } + public function testMultiLevelPrivatePropertiesDefaultsWillBePreserved() + { + $instance = new ClassWithCollidingPrivateInheritedProperties(); + $proxyName = $this->generateProxy(ClassWithCollidingPrivateInheritedProperties::class); + /* @var $proxy ClassWithPrivateProperties */ + $proxy = $proxyName::staticProxyConstructor(function () { + }); + + $childProperty = new ReflectionProperty($instance, 'property0'); + $parentProperty = new ReflectionProperty(get_parent_class($instance), 'property0'); + + $childProperty->setAccessible(true); + $parentProperty->setAccessible(true); + + $this->assertSame('childClassProperty0', $childProperty->getValue($proxy)); + $this->assertSame('property0', $parentProperty->getValue($proxy)); + } + /** * @group 115 * @group 175 From 7d6d980933ce7d5a42876b514b52e8f5fd2385e1 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 08:01:49 +0100 Subject: [PATCH 048/153] Testing new by-ref property initialization feature --- .../LazyLoadingGhostFunctionalTest.php | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php index 663ab6bdc..0725f3f0a 100644 --- a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php +++ b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php @@ -342,6 +342,29 @@ public function testMultiLevelPrivatePropertiesDefaultsWillBePreserved() $this->assertSame('property0', $parentProperty->getValue($proxy)); } + public function testMultiLevelPrivatePropertiesByRefInitialization() + { + $class = ClassWithCollidingPrivateInheritedProperties::class; + $proxyName = $this->generateProxy($class); + /* @var $proxy ClassWithPrivateProperties */ + $proxy = $proxyName::staticProxyConstructor( + function ($proxy, $method, $params, & $initializer, array $properties) use ($class) { + $initializer = null; + $properties["\0" . $class . "\0property0"] = 'foo'; + $properties["\0" . get_parent_class($class) . "\0property0"] = 'bar'; + } + ); + + $childProperty = new ReflectionProperty($class, 'property0'); + $parentProperty = new ReflectionProperty(get_parent_class($class), 'property0'); + + $childProperty->setAccessible(true); + $parentProperty->setAccessible(true); + + $this->assertSame('foo', $childProperty->getValue($proxy)); + $this->assertSame('bar', $parentProperty->getValue($proxy)); + } + /** * @group 115 * @group 175 From ef98b1864ce58f6dd158e806e5185767f89e02e9 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 08:12:54 +0100 Subject: [PATCH 049/153] Verifying by-ref initialization --- .../LazyLoadingGhostFunctionalTest.php | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php index 0725f3f0a..c8284af52 100644 --- a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php +++ b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php @@ -30,6 +30,7 @@ use ProxyManagerTestAsset\BaseClass; use ProxyManagerTestAsset\ClassWithCollidingPrivateInheritedProperties; use ProxyManagerTestAsset\ClassWithCounterConstructor; +use ProxyManagerTestAsset\ClassWithMixedProperties; use ProxyManagerTestAsset\ClassWithPrivateProperties; use ProxyManagerTestAsset\ClassWithProtectedProperties; use ProxyManagerTestAsset\ClassWithPublicArrayProperty; @@ -365,6 +366,32 @@ function ($proxy, $method, $params, & $initializer, array $properties) use ($cla $this->assertSame('bar', $parentProperty->getValue($proxy)); } + public function testByRefInitialization() + { + $proxyName = $this->generateProxy(ClassWithMixedProperties::class); + /* @var $proxy ClassWithPrivateProperties */ + $proxy = $proxyName::staticProxyConstructor( + function ($proxy, $method, $params, & $initializer, array $properties) { + $initializer = null; + $properties["\0" . ClassWithMixedProperties::class . "\0privateProperty0"] = 'private0'; + $properties["\0" . ClassWithMixedProperties::class . "\0privateProperty1"] = 'private1'; + $properties["\0" . ClassWithMixedProperties::class . "\0privateProperty2"] = 'private2'; + $properties["\0*\0protectedProperty0"] = 'protected0'; + $properties["\0*\0protectedProperty1"] = 'protected1'; + $properties["\0*\0protectedProperty2"] = 'protected2'; + $properties["publicProperty0"] = 'public0'; + $properties["publicProperty1"] = 'public1'; + $properties["publicProperty2"] = 'public2'; + } + ); + + foreach ((new ReflectionClass(ClassWithMixedProperties::class))->getProperties() as $property) { + $property->setAccessible(true); + + $this->assertSame(str_replace('Property', '', $property->getName()), $property->getValue($proxy)); + } + } + /** * @group 115 * @group 175 From 2295470792568a696e8212cb8732381fb31aa9f1 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 08:17:52 +0100 Subject: [PATCH 050/153] Simplifying performance test now that reflection is not needed anymore --- .../LazyLoadingGhostPerformanceTest.php | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/tests/ProxyManagerTest/Functional/LazyLoadingGhostPerformanceTest.php b/tests/ProxyManagerTest/Functional/LazyLoadingGhostPerformanceTest.php index ea5c8395a..379480a35 100644 --- a/tests/ProxyManagerTest/Functional/LazyLoadingGhostPerformanceTest.php +++ b/tests/ProxyManagerTest/Functional/LazyLoadingGhostPerformanceTest.php @@ -49,31 +49,27 @@ class LazyLoadingGhostPerformanceTest extends BaseLazyLoadingPerformanceTest * * @return void */ - public function testProxyInstantiationPerformance( - $className, - array $methods, - array $properties, - array $reflectionProperties - ) { + public function testProxyInstantiationPerformance($className, array $methods, array $properties) + { $proxyName = $this->generateProxy($className); $iterations = 20000; $instances = []; /* @var $proxies \ProxyManager\Proxy\GhostObjectInterface[] */ $proxies = []; - $realInstance = new $className(); + $realInstance = (array) new $className(); $initializer = function ( GhostObjectInterface $proxy, $method, $params, - & $initializer + & $initializer, + array $properties ) use ( - $reflectionProperties, $realInstance ) { $initializer = null; - foreach ($reflectionProperties as $reflectionProperty) { - $reflectionProperty->setValue($proxy, $reflectionProperty->getValue($realInstance)); + foreach ($realInstance as $name => $value) { + $properties[$name] = $value; } return true; From 30f69ea6ef1c3848e271b0b968a9ec54a2ceddf0 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 08:26:22 +0100 Subject: [PATCH 051/153] All generator must be able to handle multiple inheritance with same private properties --- .../ProxyManagerTest/Functional/MultipleProxyGenerationTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/ProxyManagerTest/Functional/MultipleProxyGenerationTest.php b/tests/ProxyManagerTest/Functional/MultipleProxyGenerationTest.php index 9fcbbece1..9241f0f2a 100644 --- a/tests/ProxyManagerTest/Functional/MultipleProxyGenerationTest.php +++ b/tests/ProxyManagerTest/Functional/MultipleProxyGenerationTest.php @@ -29,6 +29,7 @@ use ProxyManager\Proxy\VirtualProxyInterface; use ProxyManagerTestAsset\BaseClass; use ProxyManagerTestAsset\ClassWithByRefMagicMethods; +use ProxyManagerTestAsset\ClassWithCollidingPrivateInheritedProperties; use ProxyManagerTestAsset\ClassWithFinalMagicMethods; use ProxyManagerTestAsset\ClassWithFinalMethods; use ProxyManagerTestAsset\ClassWithMagicMethods; @@ -115,6 +116,7 @@ public function getTestedClasses() [EmptyClass::class], [HydratedObject::class], [ClassWithSelfHint::class], + [ClassWithCollidingPrivateInheritedProperties::class], ]; } } From 6a4ef6e648626b89e7222a73f8a202429cd2e49f Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 08:35:04 +0100 Subject: [PATCH 052/153] Verifying multiple inheritance proxy private property access --- ...t-multiple-inheritance-private-access.phpt | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 tests/language-feature-scripts/lazy-loading-ghost-multiple-inheritance-private-access.phpt diff --git a/tests/language-feature-scripts/lazy-loading-ghost-multiple-inheritance-private-access.phpt b/tests/language-feature-scripts/lazy-loading-ghost-multiple-inheritance-private-access.phpt new file mode 100644 index 000000000..51bee3ed2 --- /dev/null +++ b/tests/language-feature-scripts/lazy-loading-ghost-multiple-inheritance-private-access.phpt @@ -0,0 +1,44 @@ +--TEST-- +Verifies that private API of proxies is guaranteed to access private properties in any case +--FILE-- +multiplier * $value; + } +} + +class Bar extends Foo +{ + private $multiplier = 5; + + public function multiply($value) + { + return $value * parent::multiply($this->multiplier); + } +} + +class Baz extends Bar +{ + private $multiplier = 7; + + public function multiply($value) + { + return $value * parent::multiply($this->multiplier); + } +} + +echo (new \ProxyManager\Factory\LazyLoadingGhostFactory($configuration)) + ->createProxy(Baz::class, function () {}) + ->multiply(2); + +?> +--EXPECTF-- +210 \ No newline at end of file From 2a934bf6224b477b1391aeb3ce9b25f90c7afcfe Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 09:07:20 +0100 Subject: [PATCH 053/153] Refactored `StaticProxyConstructor` to group together closure calls and avoid multiple closure calls for a single initialization with a low level of inheritance --- .../StaticProxyConstructor.php | 87 ++++++++++++------- 1 file changed, 57 insertions(+), 30 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php index c96d82a36..08fb3a846 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php @@ -20,6 +20,7 @@ use ProxyManager\Generator\MethodGenerator; use ProxyManager\Generator\ParameterGenerator; +use ProxyManager\ProxyGenerator\Util\Properties; use ReflectionClass; use ReflectionProperty; use Zend\Code\Generator\PropertyGenerator; @@ -33,59 +34,85 @@ class StaticProxyConstructor extends MethodGenerator { /** - * Constructor + * Static constructor * - * @param ReflectionClass $originalClass * @param PropertyGenerator $initializerProperty + * @param Properties $properties */ - public function __construct(ReflectionClass $originalClass, PropertyGenerator $initializerProperty) + public function __construct(PropertyGenerator $initializerProperty, Properties $properties) { parent::__construct('staticProxyConstructor', [], static::FLAG_PUBLIC | static::FLAG_STATIC); $this->setParameter(new ParameterGenerator('initializer')); - /* @var $publicProperties \ReflectionProperty[] */ - $publicProperties = $originalClass->getProperties(ReflectionProperty::IS_PUBLIC); - $unsetProperties = []; - - foreach ($publicProperties as $publicProperty) { - $unsetProperties[] = '$instance->' . $publicProperty->getName(); - } - - /* @var $allProperties \ReflectionProperty[] */ - $allProperties = []; - $class = $originalClass; - - // @todo move this filter to a separate class - do { - foreach ($class->getProperties() as $property) { - $allProperties[$property->getDeclaringClass()->getName() . '#' . $property->getName()] = $property; - } - } while ($class = $class->getParentClass()); - $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" - . implode("\n", array_map([$this, 'getUnsetPropertyCode'], $allProperties)) + . $this->generateUnsetPropertiesCode($properties) . '$instance->' . $initializerProperty->getName() . ' = $initializer;' . "\n\n" . 'return $instance;' ); } /** - * @param ReflectionProperty $property + * @param Properties $properties * * @return string */ - private function getUnsetPropertyCode(ReflectionProperty $property) + private function generateUnsetPropertiesCode(Properties $properties) + { + $code = ''; + + if ($accessibleProperties = $properties->getAccessibleProperties()) { + $code .= $this->getUnsetPropertiesGroupCode($accessibleProperties) . "\n"; + } + + foreach ($this->getGroupedPrivateProperties($properties) as $className => $privateProperties) { + $code .= "\\Closure::bind(function (\$instance) {\n" + . ' ' . $this->getUnsetPropertiesGroupCode($privateProperties) . ";\n" + . '}, null, ' . var_export($className, true) . ")->__invoke(\$instance);\n"; + } + + return $code; + } + + /** + * @param Properties $properties + * + * @return ReflectionProperty[][] indexed by class name and property name + */ + private function getGroupedPrivateProperties(Properties $properties) { - if (! $property->isPrivate()) { - return 'unset($instance->' . $property->getName() . ");\n"; + $propertiesMap = []; + + foreach ($properties->getPrivateProperties() as $property) { + $class = & $propertiesMap[$property->getDeclaringClass()->getName()]; + + $class[$property->getName()] = $property; } - return "\\Closure::bind(function (\$instance) {\n" - . ' unset($instance->' . $property->getName() . ");\n" - . '}, null, ' . var_export($property->getDeclaringClass()->getName(), true) . ")->__invoke(\$instance);\n"; + + return $propertiesMap; + } + + /** + * @param ReflectionProperty[] $properties + * + * @return string + */ + private function getUnsetPropertiesGroupCode(array $properties) + { + return 'unset(' + . implode( + ', ', + array_map( + function (ReflectionProperty $property) { + return '$this->' . $property->getName(); + }, + $properties + ) + ) + . ");\n"; } } From bda3bb1ff3bfbadbedf6f5bd868e560ecacaea37 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 09:07:33 +0100 Subject: [PATCH 054/153] Optimized imports --- .../LazyLoading/MethodGenerator/StaticProxyConstructor.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php index 08fb3a846..f34ead495 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php @@ -21,7 +21,6 @@ use ProxyManager\Generator\MethodGenerator; use ProxyManager\Generator\ParameterGenerator; use ProxyManager\ProxyGenerator\Util\Properties; -use ReflectionClass; use ReflectionProperty; use Zend\Code\Generator\PropertyGenerator; From 6c930bc3ced9b1e1b11a0797726d90326411ba0c Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 09:08:57 +0100 Subject: [PATCH 055/153] Fixing constructor call to `StaticProxyConstructor` --- .../ProxyGenerator/LazyLoadingGhostGenerator.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php index f40a225e9..bf155e3af 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php @@ -111,7 +111,10 @@ function (ReflectionMethod $method) use ($initializer, $init) { ), [ $init, - new StaticProxyConstructor($originalClass, $initializer), + new StaticProxyConstructor( + $initializer, + Properties::fromReflectionClass($originalClass) + ), new MagicGet( $originalClass, $initializer, From 1134d1c72f8612c225aaa95e4e1f108e6e685b7e Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 09:14:37 +0100 Subject: [PATCH 056/153] Fixing constructor call to `StaticProxyConstructor` --- .../ProxyGenerator/LazyLoadingValueHolderGenerator.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolderGenerator.php b/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolderGenerator.php index 23f185643..07dd54fd0 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolderGenerator.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingValueHolderGenerator.php @@ -37,6 +37,7 @@ 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; @@ -93,7 +94,7 @@ function (ReflectionMethod $method) use ($initializer, $valueHolder) { ProxiedMethodsFilter::getProxiedMethods($originalClass) ), [ - new StaticProxyConstructor($originalClass, $initializer), + new StaticProxyConstructor($initializer, Properties::fromReflectionClass($originalClass)), Constructor::generateMethod($originalClass, $valueHolder), new MagicGet($originalClass, $initializer, $valueHolder, $publicProperties), new MagicSet($originalClass, $initializer, $valueHolder, $publicProperties), From fb0965af5211c565f105da7d001bae60e8de4c79 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 09:15:27 +0100 Subject: [PATCH 057/153] s/$this/$instance --- .../LazyLoading/MethodGenerator/StaticProxyConstructor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php index f34ead495..5146cf09d 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php @@ -107,7 +107,7 @@ private function getUnsetPropertiesGroupCode(array $properties) ', ', array_map( function (ReflectionProperty $property) { - return '$this->' . $property->getName(); + return '$instance->' . $property->getName(); }, $properties ) From 4670f9e73299873ebf6427ccc612da3f6c5f1695 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 09:18:49 +0100 Subject: [PATCH 058/153] CS (generated code spacing) --- .../LazyLoading/MethodGenerator/StaticProxyConstructor.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php index 5146cf09d..531e2130f 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php @@ -70,8 +70,8 @@ private function generateUnsetPropertiesCode(Properties $properties) foreach ($this->getGroupedPrivateProperties($properties) as $className => $privateProperties) { $code .= "\\Closure::bind(function (\$instance) {\n" - . ' ' . $this->getUnsetPropertiesGroupCode($privateProperties) . ";\n" - . '}, null, ' . var_export($className, true) . ")->__invoke(\$instance);\n"; + . ' ' . $this->getUnsetPropertiesGroupCode($privateProperties) + . '}, null, ' . var_export($className, true) . ")->__invoke(\$instance);\n\n"; } return $code; From b2f5e5f51caa4ea7c03cdee59892f55b41df8f8a Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 09:21:43 +0100 Subject: [PATCH 059/153] Aligning expected code structure to new generated code --- .../StaticProxyConstructorTest.php | 44 +++++-------------- 1 file changed, 10 insertions(+), 34 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructorTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructorTest.php index 0fc88a80a..493312ad2 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructorTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructorTest.php @@ -20,6 +20,8 @@ use PHPUnit_Framework_TestCase; use ProxyManager\ProxyGenerator\LazyLoading\MethodGenerator\StaticProxyConstructor; +use ProxyManager\ProxyGenerator\Util\Properties; +use ProxyManagerTestAsset\ClassWithMixedProperties; use ProxyManagerTestAsset\EmptyClass; use ProxyManagerTestAsset\ProxyGenerator\LazyLoading\MethodGenerator\ClassWithTwoPublicProperties; use ReflectionClass; @@ -44,51 +46,25 @@ public function testBodyStructure() $initializer->expects($this->any())->method('getName')->will($this->returnValue('foo')); $constructor = new StaticProxyConstructor( - new ReflectionClass( - ClassWithTwoPublicProperties::class - ), - $initializer + $initializer, + Properties::fromReflectionClass(new ReflectionClass(ClassWithMixedProperties::class)) ); $this->assertSame('staticProxyConstructor', $constructor->getName()); $this->assertCount(1, $constructor->getParameters()); + $this->assertSame( 'static $reflection; $reflection = $reflection ?: $reflection = new \ReflectionClass(__CLASS__); $instance = (new \ReflectionClass(get_class()))->newInstanceWithoutConstructor(); -unset($instance->bar, $instance->baz); - -$instance->foo = $initializer; - -return $instance;', - $constructor->getBody() - ); - } - - /** - * @covers \ProxyManager\ProxyGenerator\LazyLoading\MethodGenerator\Constructor::__construct - */ - public function testBodyStructureWithoutPublicProperties() - { - /* @var $initializer PropertyGenerator|\PHPUnit_Framework_MockObject_MockObject */ - $initializer = $this->getMock(PropertyGenerator::class); - - $initializer->expects($this->any())->method('getName')->will($this->returnValue('foo')); - - $constructor = new StaticProxyConstructor( - new ReflectionClass(EmptyClass::class), - $initializer - ); - - $this->assertSame('staticProxyConstructor', $constructor->getName()); - $this->assertCount(1, $constructor->getParameters()); - $this->assertSame( - 'static $reflection; +unset($instance->publicProperty0, $instance->publicProperty1, $instance->publicProperty2, ' + . '$instance->protectedProperty0, $instance->protectedProperty1, $instance->protectedProperty2); -$reflection = $reflection ?: $reflection = new \ReflectionClass(__CLASS__); -$instance = (new \ReflectionClass(get_class()))->newInstanceWithoutConstructor(); +\Closure::bind(function ($instance) { + unset($instance->privateProperty0, $instance->privateProperty1, $instance->privateProperty2); +}, null, \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\')->__invoke($instance); $instance->foo = $initializer; From e89dbe5ddd36260ddff10a0e77e36abdc444cb1a Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 09:35:17 +0100 Subject: [PATCH 060/153] Closures calling `unset()` should be cached --- .../MethodGenerator/StaticProxyConstructorTest.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructorTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructorTest.php index 493312ad2..ccb4efaf3 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructorTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructorTest.php @@ -53,7 +53,7 @@ public function testBodyStructure() $this->assertSame('staticProxyConstructor', $constructor->getName()); $this->assertCount(1, $constructor->getParameters()); - $this->assertSame( + $this->assertStringMatchesFormat( 'static $reflection; $reflection = $reflection ?: $reflection = new \ReflectionClass(__CLASS__); @@ -62,9 +62,13 @@ public function testBodyStructure() unset($instance->publicProperty0, $instance->publicProperty1, $instance->publicProperty2, ' . '$instance->protectedProperty0, $instance->protectedProperty1, $instance->protectedProperty2); -\Closure::bind(function ($instance) { +static $cache%a; + +$cache%s ?: $cache%s = \Closure::bind(function ($instance) { unset($instance->privateProperty0, $instance->privateProperty1, $instance->privateProperty2); -}, null, \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\')->__invoke($instance); +}, null, \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\'); + +$cache%s($instance); $instance->foo = $initializer; From 5af812bb064ed2386da2424b2b00920d3299cf33 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 09:36:34 +0100 Subject: [PATCH 061/153] Closures calling `unset()` are cached --- .../LazyLoading/MethodGenerator/StaticProxyConstructor.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php index 531e2130f..62eee4cc8 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php @@ -69,9 +69,12 @@ private function generateUnsetPropertiesCode(Properties $properties) } foreach ($this->getGroupedPrivateProperties($properties) as $className => $privateProperties) { - $code .= "\\Closure::bind(function (\$instance) {\n" + $cacheKey = 'cache' . str_replace('\\', '_', $className); + $code .= 'static $' . $cacheKey . ";\n\n" + . '$' . $cacheKey . ' ?: $' . $cacheKey . " = \\Closure::bind(function (\$instance) {\n" . ' ' . $this->getUnsetPropertiesGroupCode($privateProperties) - . '}, null, ' . var_export($className, true) . ")->__invoke(\$instance);\n\n"; + . '}, null, ' . var_export($className, true) . ");\n\n" + . '$' . $cacheKey . "(\$instance);\n\n"; } return $code; From 4abc9c81640db536cd6739c1096f2372fa7ae8e2 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 10:03:02 +0100 Subject: [PATCH 062/153] Closures setting private properties defaults should be cached --- .../MethodGenerator/CallInitializerTest.php | 76 ++++++++++++++++--- 1 file changed, 66 insertions(+), 10 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php index faf84bfb6..90d452514 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php @@ -20,6 +20,9 @@ use PHPUnit_Framework_TestCase; use ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\CallInitializer; +use ProxyManager\ProxyGenerator\Util\Properties; +use ProxyManagerTestAsset\ClassWithMixedProperties; +use ReflectionClass; use Zend\Code\Generator\PropertyGenerator; /** @@ -38,22 +41,75 @@ class CallInitializerTest extends PHPUnit_Framework_TestCase public function testBodyStructure() { $initializer = $this->getMock(PropertyGenerator::class); - $propertiesDefaults = $this->getMock(PropertyGenerator::class); $initializationTracker = $this->getMock(PropertyGenerator::class); $initializer->expects($this->any())->method('getName')->will($this->returnValue('init')); - $propertiesDefaults->expects($this->any())->method('getName')->will($this->returnValue('props')); $initializationTracker->expects($this->any())->method('getName')->will($this->returnValue('track')); - $callInitializer = new CallInitializer($initializer, $propertiesDefaults, $initializationTracker); + $callInitializer = new CallInitializer( + $initializer, + $initializationTracker, + Properties::fromReflectionClass(new ReflectionClass(ClassWithMixedProperties::class)) + ); + + $expectedCode = 'if ($this->track || ! $this->init) { + return; +} + +$this->track = true; + +$this->publicProperty0 = \'publicProperty0\'; +$this->publicProperty1 = \'publicProperty1\'; +$this->publicProperty2 = \'publicProperty2\'; +$this->protectedProperty0 = \'protectedProperty0\'; +$this->protectedProperty1 = \'protectedProperty1\'; +$this->protectedProperty2 = \'protectedProperty2\'; +static $cacheProxyManagerTestAsset_ClassWithMixedProperties; + +$cacheProxyManagerTestAsset_ClassWithMixedProperties ?: $cacheProxyManagerTestAsset_ClassWithMixedProperties = ' + . '\Closure::bind(function ($instance) { + $instance->privateProperty0 = \'privateProperty0\'; + $instance->privateProperty1 = \'privateProperty1\'; + $instance->privateProperty2 = \'privateProperty2\'; +}, null, \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\'); + +$cacheProxyManagerTestAsset_ClassWithMixedProperties($this); + + + + +$properties = [ + \'publicProperty0\' => & $this->publicProperty0, + \'publicProperty1\' => & $this->publicProperty1, + \'publicProperty2\' => & $this->publicProperty2, + \'\' . "\0" . \'*\' . "\0" . \'protectedProperty0\' => & $this->protectedProperty0, + \'\' . "\0" . \'*\' . "\0" . \'protectedProperty1\' => & $this->protectedProperty1, + \'\' . "\0" . \'*\' . "\0" . \'protectedProperty2\' => & $this->protectedProperty2, +]; + +$properties[\'\' . "\0" . \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\' . "\0" . \'privateProperty0\'] ' + . '= & \Closure::bind(function & () { + return $this->privateProperty0; +}, $this, \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\')->__invoke($this); + +$properties[\'\' . "\0" . \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\' . "\0" . \'privateProperty1\'] ' + . '= & \Closure::bind(function & () { + return $this->privateProperty1; +}, $this, \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\')->__invoke($this); + +$properties[\'\' . "\0" . \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\' . "\0" . \'privateProperty2\'] ' + . '= & \Closure::bind(function & () { + return $this->privateProperty2; +}, $this, \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\')->__invoke($this); + + + +$this->init->__invoke($this, $methodName, $parameters, $this->init, $properties); +$this->track = false;'; + - $this->assertStringMatchesFormat( - '%Aif ($this->track || ! $this->init) {%areturn;%a}%a' - . '$this->track = true;%a' - . 'foreach (self::$props as $key => $default) {%a' - . '$this->$key = $default;%a' - . '$this->init->__invoke(%a);%a' - . '$this->track = false;', + $this->assertSame( + $expectedCode, $callInitializer->getBody() ); } From e5ad21266d6fb880b529d10be1cea53628f4b261 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 10:03:45 +0100 Subject: [PATCH 063/153] Fixing coverage annotation --- .../LazyLoadingGhost/MethodGenerator/CallInitializerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php index 90d452514..35499b6d7 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php @@ -36,7 +36,7 @@ class CallInitializerTest extends PHPUnit_Framework_TestCase { /** - * @covers \ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\CallInitializer::__construct + * @covers \ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\CallInitializer */ public function testBodyStructure() { From 73206a239da8da0958bb0ace1cdb86954d7e7dbd Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 10:04:17 +0100 Subject: [PATCH 064/153] Caching property default value writer closures --- .../MethodGenerator/CallInitializer.php | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php index 3288f618e..a1fa895c4 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php @@ -105,17 +105,38 @@ private function propertiesInitializationCode(Properties $properties) . ';'; } - foreach ($properties->getPrivateProperties() as $property) { - $name = $property->getName(); - $assignments[] = "\\Closure::bind(function (\$object) {\n" - . ' $object->' . $name . ' = ' . $this->getExportedPropertyDefaultValue($property) . ";\n" - . "}, null, " . var_export($property->getDeclaringClass()->getName(), true) . ")->__invoke(\$this)" - . ';'; + + 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) + { + return implode( + "\n", + array_map( + function (ReflectionProperty $property) { + return ' $instance->' . $property->getName() + . ' = ' . $this->getExportedPropertyDefaultValue($property) . ';'; + }, + $properties + ) + ); + } + /** * @param Properties $properties * From 3481d05db12933361033c74c13242778391cef42 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 10:04:47 +0100 Subject: [PATCH 065/153] `Properties` utility now provides also private properties grouped by declaring class name --- .../ProxyGenerator/Util/Properties.php | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/ProxyManager/ProxyGenerator/Util/Properties.php b/src/ProxyManager/ProxyGenerator/Util/Properties.php index e57b04eea..021202fe2 100644 --- a/src/ProxyManager/ProxyGenerator/Util/Properties.php +++ b/src/ProxyManager/ProxyGenerator/Util/Properties.php @@ -22,7 +22,8 @@ use ReflectionProperty; /** - * DTO containing the list of all non-static proxy properties + * DTO containing the list of all non-static proxy properties and utility methods to access them + * in various formats/collections * * @author Marco Pivetta * @license MIT @@ -124,4 +125,20 @@ public function getAccessibleProperties() { return array_merge($this->getPublicProperties(), $this->getProtectedProperties()); } + + /** + * @return ReflectionProperty[][] indexed by class name and property name + */ + public function getGroupedPrivateProperties() + { + $propertiesMap = []; + + foreach ($this->getPrivateProperties() as $property) { + $class = & $propertiesMap[$property->getDeclaringClass()->getName()]; + + $class[$property->getName()] = $property; + } + + return $propertiesMap; + } } From eeddabf7ca83964b0311ab5ab61412292c113fca Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 10:05:07 +0100 Subject: [PATCH 066/153] De-duplicating code by using `Properties#getGroupedPrivateProperties()` --- .../StaticProxyConstructor.php | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php index 62eee4cc8..dbedcefac 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/StaticProxyConstructor.php @@ -68,7 +68,7 @@ private function generateUnsetPropertiesCode(Properties $properties) $code .= $this->getUnsetPropertiesGroupCode($accessibleProperties) . "\n"; } - foreach ($this->getGroupedPrivateProperties($properties) as $className => $privateProperties) { + foreach ($properties->getGroupedPrivateProperties() as $className => $privateProperties) { $cacheKey = 'cache' . str_replace('\\', '_', $className); $code .= 'static $' . $cacheKey . ";\n\n" . '$' . $cacheKey . ' ?: $' . $cacheKey . " = \\Closure::bind(function (\$instance) {\n" @@ -80,24 +80,6 @@ private function generateUnsetPropertiesCode(Properties $properties) return $code; } - /** - * @param Properties $properties - * - * @return ReflectionProperty[][] indexed by class name and property name - */ - private function getGroupedPrivateProperties(Properties $properties) - { - $propertiesMap = []; - - foreach ($properties->getPrivateProperties() as $property) { - $class = & $propertiesMap[$property->getDeclaringClass()->getName()]; - - $class[$property->getName()] = $property; - } - - return $propertiesMap; - } - /** * @param ReflectionProperty[] $properties * From 7b613f02c7ee60e0b2cbff53b88545b87e0918aa Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 10:19:23 +0100 Subject: [PATCH 067/153] Closures using for getting references to private properties should be cached locally --- .../MethodGenerator/CallInitializerTest.php | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php index 35499b6d7..c4b284096 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php @@ -87,20 +87,38 @@ public function testBodyStructure() \'\' . "\0" . \'*\' . "\0" . \'protectedProperty2\' => & $this->protectedProperty2, ]; -$properties[\'\' . "\0" . \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\' . "\0" . \'privateProperty0\'] ' - . '= & \Closure::bind(function & () { - return $this->privateProperty0; -}, $this, \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\')->__invoke($this); +static $cacheProxyManagerTestAsset_ClassWithMixedProperties_privateProperty0; + +$cacheProxyManagerTestAsset_ClassWithMixedProperties_privateProperty0 ?: ' + . '$cacheProxyManagerTestAsset_ClassWithMixedProperties_privateProperty0 ' + . '= \Closure::bind(function & ($instance) { + return $instance->privateProperty0; +}, $this, \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\'); + +$properties[\'\' . "\0" . \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\' . "\0" . \'privateProperty0\']' + . ' = & $cacheProxyManagerTestAsset_ClassWithMixedProperties_privateProperty0($this); + +static $cacheProxyManagerTestAsset_ClassWithMixedProperties_privateProperty1; + +$cacheProxyManagerTestAsset_ClassWithMixedProperties_privateProperty1 ' + . '?: $cacheProxyManagerTestAsset_ClassWithMixedProperties_privateProperty1 = ' + . '\Closure::bind(function & ($instance) { + return $instance->privateProperty1; +}, $this, \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\'); $properties[\'\' . "\0" . \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\' . "\0" . \'privateProperty1\'] ' - . '= & \Closure::bind(function & () { - return $this->privateProperty1; -}, $this, \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\')->__invoke($this); + . '= & $cacheProxyManagerTestAsset_ClassWithMixedProperties_privateProperty1($this); + +static $cacheProxyManagerTestAsset_ClassWithMixedProperties_privateProperty2; + +$cacheProxyManagerTestAsset_ClassWithMixedProperties_privateProperty2 ?: ' + . '$cacheProxyManagerTestAsset_ClassWithMixedProperties_privateProperty2 = ' + . '\Closure::bind(function & ($instance) { + return $instance->privateProperty2; +}, $this, \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\'); $properties[\'\' . "\0" . \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\' . "\0" . \'privateProperty2\'] ' - . '= & \Closure::bind(function & () { - return $this->privateProperty2; -}, $this, \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\')->__invoke($this); + . '= & $cacheProxyManagerTestAsset_ClassWithMixedProperties_privateProperty2($this); From 2a845c350e82c5c493a42b72be31d52c046e2b85 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 10:19:43 +0100 Subject: [PATCH 068/153] Closures used for getting references to private properties are now cached locally --- .../MethodGenerator/CallInitializer.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php index a1fa895c4..84c222e82 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php @@ -156,12 +156,15 @@ private function propertiesReferenceArrayCode(Properties $properties) // must use assignments, as direct reference during array definition causes a fatal error (not sure why) foreach ($properties->getPrivateProperties() as $propertyInternalName => $property) { - $name = $property->getName(); - $declaringClass = $property->getDeclaringClass()->getName(); - $code .= '$properties[' . var_export($propertyInternalName, true) . ']' - . " = & \\Closure::bind(function & () {\n" - . ' return $this->' . $name . ";\n" - . "}, \$this, " . var_export($declaringClass, true) . ")->__invoke(\$this);\n\n"; + $name = $property->getName(); + $className = $property->getDeclaringClass()->getName(); + $cacheKey = 'cache' . str_replace('\\', '_', $className) . '_' . $name; + + $code .= 'static $' . $cacheKey . ";\n\n" + . '$' . $cacheKey . ' ?: $' . $cacheKey . " = \\Closure::bind(function & (\$instance) {\n" + . ' return $instance->' . $name . ";\n" + . "}, \$this, " . var_export($className, true) . ");\n\n" + . '$properties[' . var_export($propertyInternalName, true) . '] = & $' . $cacheKey . "(\$this);\n\n"; } return $code; From c256ab641cd9a1cb4c2f6a2d45f403709e654d45 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 10:19:59 +0100 Subject: [PATCH 069/153] Optimized imports --- .../LazyLoadingGhost/MethodGenerator/CallInitializer.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php index 84c222e82..388d85932 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php @@ -21,9 +21,6 @@ use ProxyManager\Generator\MethodGenerator; use ProxyManager\Generator\ParameterGenerator; use ProxyManager\Generator\Util\UniqueIdentifierGenerator; -use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PrivatePropertiesMap; -use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\ProtectedPropertiesMap; -use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap; use ProxyManager\ProxyGenerator\Util\Properties; use ReflectionProperty; use Zend\Code\Generator\PropertyGenerator; From bf599a540ce5a35f70f6587fc86259b7bbfc3007 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 10:53:18 +0100 Subject: [PATCH 070/153] IDE hints for the type-checker --- .../LazyLoadingGhost/MethodGenerator/CallInitializerTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php index c4b284096..52fc8ce63 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php @@ -40,7 +40,9 @@ class CallInitializerTest extends PHPUnit_Framework_TestCase */ public function testBodyStructure() { + /* @var $initializer PropertyGenerator|\PHPUnit_Framework_MockObject_MockObject */ $initializer = $this->getMock(PropertyGenerator::class); + /* @var $initializationTracker PropertyGenerator|\PHPUnit_Framework_MockObject_MockObject */ $initializationTracker = $this->getMock(PropertyGenerator::class); $initializer->expects($this->any())->method('getName')->will($this->returnValue('init')); From 0de2f05ff4b3b50d37232f89b0e8e2a93f9687ba Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 11:15:07 +0100 Subject: [PATCH 071/153] Refactoring test logic and fixing constructor args --- .../MethodGenerator/MagicGetTest.php | 53 ++++++++++++++++--- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php index fe34842ea..2a0793fef 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php @@ -20,6 +20,8 @@ use PHPUnit_Framework_TestCase; use ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicGet; +use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PrivatePropertiesMap; +use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\ProtectedPropertiesMap; use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap; use ProxyManagerTestAsset\ClassWithMagicMethods; use ProxyManagerTestAsset\EmptyClass; @@ -53,6 +55,16 @@ class MagicGetTest extends PHPUnit_Framework_TestCase */ protected $publicProperties; + /** + * @var ProtectedPropertiesMap|\PHPUnit_Framework_MockObject_MockObject + */ + protected $protectedProperties; + + /** + * @var PrivatePropertiesMap|\PHPUnit_Framework_MockObject_MockObject + */ + protected $privateProperties; + /** * {@inheritDoc} */ @@ -64,11 +76,21 @@ protected function setUp() ->getMockBuilder(PublicPropertiesMap::class) ->disableOriginalConstructor() ->getMock(); + $this->protectedProperties = $this + ->getMockBuilder(ProtectedPropertiesMap::class) + ->disableOriginalConstructor() + ->getMock(); + $this->privateProperties = $this + ->getMockBuilder(PrivatePropertiesMap::class) + ->disableOriginalConstructor() + ->getMock(); $this->initializer->expects($this->any())->method('getName')->will($this->returnValue('foo')); $this->initMethod->expects($this->any())->method('getName')->will($this->returnValue('baz')); $this->publicProperties->expects($this->any())->method('isEmpty')->will($this->returnValue(false)); $this->publicProperties->expects($this->any())->method('getName')->will($this->returnValue('bar')); + $this->protectedProperties->expects($this->any())->method('getName')->will($this->returnValue('baz')); + $this->privateProperties->expects($this->any())->method('getName')->will($this->returnValue('tab')); } /** @@ -76,8 +98,14 @@ protected function setUp() */ public function testBodyStructure() { - $reflection = new ReflectionClass(EmptyClass::class); - $magicGet = new MagicGet($reflection, $this->initializer, $this->initMethod, $this->publicProperties); + $magicGet = new MagicGet( + new ReflectionClass(EmptyClass::class), + $this->initializer, + $this->initMethod, + $this->publicProperties, + $this->protectedProperties, + $this->privateProperties + ); $this->assertSame('__get', $magicGet->getName()); $this->assertCount(1, $magicGet->getParameters()); @@ -94,12 +122,15 @@ public function testBodyStructure() */ public function testBodyStructureWithPublicProperties() { - $reflection = new ReflectionClass( - ClassWithTwoPublicProperties::class + $magicGet = new MagicGet( + new ReflectionClass(ClassWithTwoPublicProperties::class), + $this->initializer, + $this->initMethod, + $this->publicProperties, + $this->protectedProperties, + $this->privateProperties ); - $magicGet = new MagicGet($reflection, $this->initializer, $this->initMethod, $this->publicProperties); - $this->assertSame('__get', $magicGet->getName()); $this->assertCount(1, $magicGet->getParameters()); $this->assertStringMatchesFormat( @@ -115,8 +146,14 @@ public function testBodyStructureWithPublicProperties() */ public function testBodyStructureWithOverriddenMagicGet() { - $reflection = new ReflectionClass(ClassWithMagicMethods::class); - $magicGet = new MagicGet($reflection, $this->initializer, $this->initMethod, $this->publicProperties); + $magicGet = new MagicGet( + new ReflectionClass(ClassWithMagicMethods::class), + $this->initializer, + $this->initMethod, + $this->publicProperties, + $this->protectedProperties, + $this->privateProperties + ); $this->assertSame('__get', $magicGet->getName()); $this->assertCount(1, $magicGet->getParameters()); From b8403445aa6cdb864e7308f0861af64648649360 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 11:18:15 +0100 Subject: [PATCH 072/153] Aligning test logic to current generated code --- .../MethodGenerator/MagicGetTest.php | 53 ++++++++++++++++--- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php index 2a0793fef..6cadfe6a1 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php @@ -133,6 +133,7 @@ public function testBodyStructureWithPublicProperties() $this->assertSame('__get', $magicGet->getName()); $this->assertCount(1, $magicGet->getParameters()); + $this->assertStringMatchesFormat( "\$this->foo && \$this->baz('__get', array('name' => \$name));\n\n" . "if (isset(self::\$bar[\$name])) {\n return \$this->\$name;\n}\n\n" @@ -157,11 +158,51 @@ public function testBodyStructureWithOverriddenMagicGet() $this->assertSame('__get', $magicGet->getName()); $this->assertCount(1, $magicGet->getParameters()); - $this->assertSame( - "\$this->foo && \$this->baz('__get', array('name' => \$name));\n\n" - . "if (isset(self::\$bar[\$name])) {\n return \$this->\$name;\n}\n\n" - . "return parent::__get(\$name);", - $magicGet->getBody() - ); + + $expectedCode = <<<'PHP' +$this->foo && $this->baz('__get', array('name' => $name)); + +if (isset(self::$bar[$name])) { + return $this->$name; +} + +if (isset(self::$baz[$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::$baz[$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::$tab[$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'] : ''; + + if (isset(self::$tab[$name][$class])) { + return \Closure::bind(function & () use ($name) { + return $this->$name; + }, $this, $class)->__invoke($this, $name); + } + + if ($class === 'ReflectionProperty') { + return \Closure::bind(function & () use ($name) { + return $this->$name; + }, $this, key(self::$tab[$name]))->__invoke($this, $name); + } +} +return parent::__get($name); +PHP; + + $this->assertSame($expectedCode, $magicGet->getBody()); } } From b5cea355afb1d8982c43d85f967cd92adcd9cad0 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 11:27:22 +0100 Subject: [PATCH 073/153] Minor CS fix in generated code (spacing) --- .../ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php | 1 + .../LazyLoadingGhost/MethodGenerator/MagicGetTest.php | 1 + 2 files changed, 2 insertions(+) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php index fc324fd6b..df9cca4eb 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php @@ -98,6 +98,7 @@ public function __construct( } } + PHP; $callParent = sprintf( diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php index 6cadfe6a1..863665784 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php @@ -200,6 +200,7 @@ public function testBodyStructureWithOverriddenMagicGet() }, $this, key(self::$tab[$name]))->__invoke($this, $name); } } + return parent::__get($name); PHP; From f77a1f77f94a9f21d888ece79fa23fca65aa37a8 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 11:31:27 +0100 Subject: [PATCH 074/153] Private properties of the same class should cause a single (cached) closure to be invoked, and not one method call per property --- .../MethodGenerator/CallInitializerTest.php | 42 +++++-------------- 1 file changed, 11 insertions(+), 31 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php index 52fc8ce63..31da2849a 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php @@ -54,6 +54,7 @@ public function testBodyStructure() Properties::fromReflectionClass(new ReflectionClass(ClassWithMixedProperties::class)) ); + // @todo cache also the by-ref stuff by class name. $expectedCode = 'if ($this->track || ! $this->init) { return; } @@ -89,40 +90,19 @@ public function testBodyStructure() \'\' . "\0" . \'*\' . "\0" . \'protectedProperty2\' => & $this->protectedProperty2, ]; -static $cacheProxyManagerTestAsset_ClassWithMixedProperties_privateProperty0; - -$cacheProxyManagerTestAsset_ClassWithMixedProperties_privateProperty0 ?: ' - . '$cacheProxyManagerTestAsset_ClassWithMixedProperties_privateProperty0 ' - . '= \Closure::bind(function & ($instance) { - return $instance->privateProperty0; -}, $this, \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\'); - -$properties[\'\' . "\0" . \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\' . "\0" . \'privateProperty0\']' - . ' = & $cacheProxyManagerTestAsset_ClassWithMixedProperties_privateProperty0($this); - -static $cacheProxyManagerTestAsset_ClassWithMixedProperties_privateProperty1; - -$cacheProxyManagerTestAsset_ClassWithMixedProperties_privateProperty1 ' - . '?: $cacheProxyManagerTestAsset_ClassWithMixedProperties_privateProperty1 = ' - . '\Closure::bind(function & ($instance) { - return $instance->privateProperty1; -}, $this, \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\'); - -$properties[\'\' . "\0" . \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\' . "\0" . \'privateProperty1\'] ' - . '= & $cacheProxyManagerTestAsset_ClassWithMixedProperties_privateProperty1($this); - -static $cacheProxyManagerTestAsset_ClassWithMixedProperties_privateProperty2; +static $cacheProxyManagerTestAsset_ClassWithMixedProperties; -$cacheProxyManagerTestAsset_ClassWithMixedProperties_privateProperty2 ?: ' - . '$cacheProxyManagerTestAsset_ClassWithMixedProperties_privateProperty2 = ' - . '\Closure::bind(function & ($instance) { - return $instance->privateProperty2; +$cacheProxyManagerTestAsset_ClassWithMixedProperties ?: $cacheProxyManagerTestAsset_ClassWithMixedProperties ' + . '= \Closure::bind(function ($instance, & $properties) { + $properties[\'\' . "\0" . \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\' . "\0" . \'privateProperty0\'] = ' + . '& $instance->privateProperty0; + $properties[\'\' . "\0" . \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\' . "\0" . \'privateProperty1\'] = ' + . '& $instance->privateProperty1; + $properties[\'\' . "\0" . \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\' . "\0" . \'privateProperty2\'] = ' + . '& $instance->privateProperty2; }, $this, \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\'); -$properties[\'\' . "\0" . \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\' . "\0" . \'privateProperty2\'] ' - . '= & $cacheProxyManagerTestAsset_ClassWithMixedProperties_privateProperty2($this); - - +cacheProxyManagerTestAsset_ClassWithMixedProperties($this, $properties); $this->init->__invoke($this, $methodName, $parameters, $this->init, $properties); $this->track = false;'; From f864b55da6a1af113598004ec96e9091594e0da1 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 11:43:13 +0100 Subject: [PATCH 075/153] Typo fix --- .../LazyLoadingGhost/MethodGenerator/CallInitializerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php index 31da2849a..89aa77b76 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php @@ -102,7 +102,7 @@ public function testBodyStructure() . '& $instance->privateProperty2; }, $this, \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\'); -cacheProxyManagerTestAsset_ClassWithMixedProperties($this, $properties); +$cacheProxyManagerTestAsset_ClassWithMixedProperties($this, $properties); $this->init->__invoke($this, $methodName, $parameters, $this->init, $properties); $this->track = false;'; From 415c59efd9cc546ecfa7d715c9cfb55a6ffc04aa Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 11:43:29 +0100 Subject: [PATCH 076/153] Caching by-ref private properties accessors by class name --- .../MethodGenerator/CallInitializer.php | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php index 388d85932..7d1b6a673 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php @@ -152,16 +152,33 @@ private function propertiesReferenceArrayCode(Properties $properties) $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->getPrivateProperties() as $propertyInternalName => $property) { - $name = $property->getName(); - $className = $property->getDeclaringClass()->getName(); - $cacheKey = 'cache' . str_replace('\\', '_', $className) . '_' . $name; + foreach ($properties->getGroupedPrivateProperties() as $className => $classPrivateProperties) { + $cacheKey = 'cache' . str_replace('\\', '_', $className); $code .= 'static $' . $cacheKey . ";\n\n" - . '$' . $cacheKey . ' ?: $' . $cacheKey . " = \\Closure::bind(function & (\$instance) {\n" - . ' return $instance->' . $name . ";\n" + . '$' . $cacheKey . ' ?: $' . $cacheKey + . " = \\Closure::bind(function (\$instance, & \$properties) {\n" + . $this->generatePrivatePropertiesAssignmentsCode($classPrivateProperties) . "}, \$this, " . var_export($className, true) . ");\n\n" - . '$properties[' . var_export($propertyInternalName, true) . '] = & $' . $cacheKey . "(\$this);\n\n"; + . '$' . $cacheKey . "(\$this, \$properties);"; + } + + return $code; + } + + /** + * @param ReflectionProperty[] $properties indexed by internal name + * + * @return string + */ + private function generatePrivatePropertiesAssignmentsCode(array $properties) + { + $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; From 0b4293b4f92ac2d9f0618f0073f1127b5b812532 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 11:43:47 +0100 Subject: [PATCH 077/153] Remove resolved todo --- .../LazyLoadingGhost/MethodGenerator/CallInitializerTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php index 89aa77b76..c050288c9 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php @@ -54,7 +54,6 @@ public function testBodyStructure() Properties::fromReflectionClass(new ReflectionClass(ClassWithMixedProperties::class)) ); - // @todo cache also the by-ref stuff by class name. $expectedCode = 'if ($this->track || ! $this->init) { return; } From c07b287ce725512c11f31ba3329a307e6577bb4b Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 11:58:08 +0100 Subject: [PATCH 078/153] Avoiding static cache key collisions --- .../MethodGenerator/CallInitializerTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php index c050288c9..738877546 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php @@ -89,9 +89,9 @@ public function testBodyStructure() \'\' . "\0" . \'*\' . "\0" . \'protectedProperty2\' => & $this->protectedProperty2, ]; -static $cacheProxyManagerTestAsset_ClassWithMixedProperties; +static $cacheFetchProxyManagerTestAsset_ClassWithMixedProperties; -$cacheProxyManagerTestAsset_ClassWithMixedProperties ?: $cacheProxyManagerTestAsset_ClassWithMixedProperties ' +$cacheFetchProxyManagerTestAsset_ClassWithMixedProperties ?: $cacheFetchProxyManagerTestAsset_ClassWithMixedProperties ' . '= \Closure::bind(function ($instance, & $properties) { $properties[\'\' . "\0" . \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\' . "\0" . \'privateProperty0\'] = ' . '& $instance->privateProperty0; @@ -101,7 +101,7 @@ public function testBodyStructure() . '& $instance->privateProperty2; }, $this, \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\'); -$cacheProxyManagerTestAsset_ClassWithMixedProperties($this, $properties); +$cacheFetchProxyManagerTestAsset_ClassWithMixedProperties($this, $properties); $this->init->__invoke($this, $methodName, $parameters, $this->init, $properties); $this->track = false;'; From 76fbc260c6ad2e903c09faccabf158db1e284580 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 11:58:20 +0100 Subject: [PATCH 079/153] Avoiding static cache key collisions in initializer logic --- .../LazyLoadingGhost/MethodGenerator/CallInitializer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php index 7d1b6a673..343b2525c 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php @@ -153,11 +153,11 @@ private function propertiesReferenceArrayCode(Properties $properties) // must use assignments, as direct reference during array definition causes a fatal error (not sure why) foreach ($properties->getGroupedPrivateProperties() as $className => $classPrivateProperties) { - $cacheKey = 'cache' . str_replace('\\', '_', $className); + $cacheKey = 'cacheFetch' . str_replace('\\', '_', $className); $code .= 'static $' . $cacheKey . ";\n\n" . '$' . $cacheKey . ' ?: $' . $cacheKey - . " = \\Closure::bind(function (\$instance, & \$properties) {\n" + . " = \\Closure::bind(function (\$instance, array & \$properties) {\n" . $this->generatePrivatePropertiesAssignmentsCode($classPrivateProperties) . "}, \$this, " . var_export($className, true) . ");\n\n" . '$' . $cacheKey . "(\$this, \$properties);"; From a47906bf43cd6cc766b15ff40ac23f487861a882 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 11:59:52 +0100 Subject: [PATCH 080/153] Missing type-hint in test --- .../LazyLoadingGhost/MethodGenerator/CallInitializerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php index 738877546..d5d8c6ef1 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializerTest.php @@ -92,7 +92,7 @@ public function testBodyStructure() static $cacheFetchProxyManagerTestAsset_ClassWithMixedProperties; $cacheFetchProxyManagerTestAsset_ClassWithMixedProperties ?: $cacheFetchProxyManagerTestAsset_ClassWithMixedProperties ' - . '= \Closure::bind(function ($instance, & $properties) { + . '= \Closure::bind(function ($instance, array & $properties) { $properties[\'\' . "\0" . \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\' . "\0" . \'privateProperty0\'] = ' . '& $instance->privateProperty0; $properties[\'\' . "\0" . \'ProxyManagerTestAsset\\\\ClassWithMixedProperties\' . "\0" . \'privateProperty1\'] = ' From 1e9ad8c839fb1f14df0c4fb0df79b47c926db0e0 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 12:12:48 +0100 Subject: [PATCH 081/153] Adding test for private property `isset()` checks in private scope --- ...roperty-isset-checks-in-private-scope.phpt | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 tests/language-feature-scripts/lazy-loading-ghost-allows-private-property-isset-checks-in-private-scope.phpt diff --git a/tests/language-feature-scripts/lazy-loading-ghost-allows-private-property-isset-checks-in-private-scope.phpt b/tests/language-feature-scripts/lazy-loading-ghost-allows-private-property-isset-checks-in-private-scope.phpt new file mode 100644 index 000000000..208f0ba75 --- /dev/null +++ b/tests/language-feature-scripts/lazy-loading-ghost-allows-private-property-isset-checks-in-private-scope.phpt @@ -0,0 +1,32 @@ +--TEST-- +Verifies that generated lazy loading ghost objects allows checking property `isset()` in private class scope +--FILE-- +sweet); + } + + public function hasSour() + { + return isset($this->sour); + } +} + +$factory = new \ProxyManager\Factory\LazyLoadingGhostFactory($configuration); + +var_dump($factory->createProxy(Kitchen::class, function () {})->hasSweet()); +var_dump($factory->createProxy(Kitchen::class, function () {})->hasSour()); + +?> +--EXPECTF-- +bool(true) +bool(false) \ No newline at end of file From 9edbb9512c51e15467b0ce61f0ad26e18a7d619f Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 12:15:36 +0100 Subject: [PATCH 082/153] Testing also repeated access to the same private properties --- ...rivate-property-isset-checks-in-private-scope.phpt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/language-feature-scripts/lazy-loading-ghost-allows-private-property-isset-checks-in-private-scope.phpt b/tests/language-feature-scripts/lazy-loading-ghost-allows-private-property-isset-checks-in-private-scope.phpt index 208f0ba75..704a1aefc 100644 --- a/tests/language-feature-scripts/lazy-loading-ghost-allows-private-property-isset-checks-in-private-scope.phpt +++ b/tests/language-feature-scripts/lazy-loading-ghost-allows-private-property-isset-checks-in-private-scope.phpt @@ -26,7 +26,18 @@ $factory = new \ProxyManager\Factory\LazyLoadingGhostFactory($configuration); var_dump($factory->createProxy(Kitchen::class, function () {})->hasSweet()); var_dump($factory->createProxy(Kitchen::class, function () {})->hasSour()); +/* @var $kitchen Kitchen */ +$kitchen = $factory->createProxy(Kitchen::class, function () {}); + +var_dump($kitchen->hasSweet()); +var_dump($kitchen->hasSweet()); +var_dump($kitchen->hasSour()); +var_dump($kitchen->hasSour()); ?> --EXPECTF-- bool(true) +bool(false) +bool(true) +bool(true) +bool(false) bool(false) \ No newline at end of file From 2c3ec8defdf5ec5704546428a81a6408eaffbe03 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 12:16:03 +0100 Subject: [PATCH 083/153] `isset()` private property accessors are now cached per class property --- .../MethodGenerator/MagicIsset.php | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php index 6e2d77240..cd67c441d 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php @@ -85,16 +85,29 @@ public function __construct( $caller = isset($callers[1]) ? $callers[1] : []; $class = isset($caller['class']) ? $caller['class'] : ''; + static $accessorCache = []; + if (isset(self::$%s[$name][$class])) { - return \Closure::bind(function () use ($name) { - return isset($this->$name); - }, $this, $class)->__invoke($this, $name); + $cacheKey = $class . '#' . $name; + $accessor = isset($accessorCache[$cacheKey]) + ? $accessorCache[$cacheKey] + : $accessorCache[$cacheKey] = \Closure::bind(function () use ($name) { + return isset($this->$name); + }, $this, $class); + + return $accessor($this, $name); } - if ($class === 'ReflectionProperty') { - return \Closure::bind(function () use ($name) { - return isset($this->$name); - }, $this, key(self::$%s[$name]))->__invoke($this, $name); + if ('ReflectionProperty' === $class) { + $tmpClass = key(self::$%s[$name]); + $cacheKey = $tmpClass . '#' . $name; + $accessor = isset($accessorCache[$cacheKey]) + ? $accessorCache[$cacheKey] + : $accessorCache[$cacheKey] = \Closure::bind(function () use ($name) { + return isset($this->$name); + }, $this, $tmpClass); + + return $accessor($this, $name); } } From a0c64dc7ec874baae9f6034ded7d1cd88462294b Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 12:20:19 +0100 Subject: [PATCH 084/153] s/magicGet/magicIsset --- .../MethodGenerator/MagicIssetTest.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicIssetTest.php b/tests/ProxyManagerTest/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicIssetTest.php index 88a22175a..901e2da35 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicIssetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/AccessInterceptorScopeLocalizer/MethodGenerator/MagicIssetTest.php @@ -47,15 +47,15 @@ public function testBodyStructure() $prefixInterceptors->expects($this->any())->method('getName')->will($this->returnValue('pre')); $suffixInterceptors->expects($this->any())->method('getName')->will($this->returnValue('post')); - $magicGet = new MagicIsset( + $magicIsset = new MagicIsset( $reflection, $prefixInterceptors, $suffixInterceptors ); - $this->assertSame('__isset', $magicGet->getName()); - $this->assertCount(1, $magicGet->getParameters()); - $this->assertStringMatchesFormat('%a$returnValue = $accessor();%a', $magicGet->getBody()); + $this->assertSame('__isset', $magicIsset->getName()); + $this->assertCount(1, $magicIsset->getParameters()); + $this->assertStringMatchesFormat('%a$returnValue = $accessor();%a', $magicIsset->getBody()); } /** @@ -70,14 +70,14 @@ public function testBodyStructureWithInheritedMethod() $prefixInterceptors->expects($this->any())->method('getName')->will($this->returnValue('pre')); $suffixInterceptors->expects($this->any())->method('getName')->will($this->returnValue('post')); - $magicGet = new MagicIsset( + $magicIsset = new MagicIsset( $reflection, $prefixInterceptors, $suffixInterceptors ); - $this->assertSame('__isset', $magicGet->getName()); - $this->assertCount(1, $magicGet->getParameters()); - $this->assertStringMatchesFormat('%a$returnValue = & parent::__isset($name);%a', $magicGet->getBody()); + $this->assertSame('__isset', $magicIsset->getName()); + $this->assertCount(1, $magicIsset->getParameters()); + $this->assertStringMatchesFormat('%a$returnValue = & parent::__isset($name);%a', $magicIsset->getBody()); } } From 78611cb2388192d657fc52a1135caa40e33d7b29 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 12:29:35 +0100 Subject: [PATCH 085/153] Adapting `MagicIssetTest` to new code generation --- .../MethodGenerator/MagicIssetTest.php | 125 ++++++++++++------ 1 file changed, 84 insertions(+), 41 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIssetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIssetTest.php index 54696cc81..825a38631 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIssetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIssetTest.php @@ -20,6 +20,8 @@ use PHPUnit_Framework_TestCase; use ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicIsset; +use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PrivatePropertiesMap; +use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\ProtectedPropertiesMap; use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap; use ProxyManagerTestAsset\ClassWithMagicMethods; use ProxyManagerTestAsset\EmptyClass; @@ -53,6 +55,16 @@ class MagicIssetTest extends PHPUnit_Framework_TestCase */ protected $publicProperties; + /** + * @var ProtectedPropertiesMap|\PHPUnit_Framework_MockObject_MockObject + */ + protected $protectedProperties; + + /** + * @var PrivatePropertiesMap|\PHPUnit_Framework_MockObject_MockObject + */ + protected $privateProperties; + /** * {@inheritDoc} */ @@ -64,66 +76,97 @@ protected function setUp() ->getMockBuilder(PublicPropertiesMap::class) ->disableOriginalConstructor() ->getMock(); + $this->protectedProperties = $this + ->getMockBuilder(ProtectedPropertiesMap::class) + ->disableOriginalConstructor() + ->getMock(); + $this->privateProperties = $this + ->getMockBuilder(PrivatePropertiesMap::class) + ->disableOriginalConstructor() + ->getMock(); $this->initializer->expects($this->any())->method('getName')->will($this->returnValue('foo')); $this->initMethod->expects($this->any())->method('getName')->will($this->returnValue('baz')); $this->publicProperties->expects($this->any())->method('isEmpty')->will($this->returnValue(false)); $this->publicProperties->expects($this->any())->method('getName')->will($this->returnValue('bar')); + $this->protectedProperties->expects($this->any())->method('getName')->will($this->returnValue('baz')); + $this->privateProperties->expects($this->any())->method('getName')->will($this->returnValue('tab')); } /** * @covers \ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicIsset::__construct */ - public function testBodyStructure() + public function testBodyStructureWithPublicProperties() { - $reflection = new ReflectionClass(EmptyClass::class); - $magicIsset = new MagicIsset($reflection, $this->initializer, $this->initMethod, $this->publicProperties); + $magicIsset = new MagicIsset( + new ReflectionClass(ClassWithTwoPublicProperties::class), + $this->initializer, + $this->initMethod, + $this->publicProperties, + $this->protectedProperties, + $this->privateProperties + ); $this->assertSame('__isset', $magicIsset->getName()); $this->assertCount(1, $magicIsset->getParameters()); - $this->assertStringMatchesFormat( - "\$this->foo && \$this->baz('__isset', array('name' => \$name));\n\n" - . "if (isset(self::\$bar[\$name])) {\n return isset(\$this->\$name);\n}\n\n" - . "%areturn %s;", - $magicIsset->getBody() - ); + + $expectedCode = <<<'PHP' +$this->foo && $this->baz('__isset', array('name' => $name)); + +if (isset(self::$bar[$name])) { + return isset($this->$name); +} + +if (isset(self::$baz[$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::$baz[$name]; + + if ($object instanceof $expectedType) { + return $this->$name; } - /** - * @covers \ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicIsset::__construct - */ - public function testBodyStructureWithPublicProperties() - { - $reflection = new ReflectionClass( - ClassWithTwoPublicProperties::class - ); - $magicIsset = new MagicIsset($reflection, $this->initializer, $this->initMethod, $this->publicProperties); + $class = isset($caller['class']) ? $caller['class'] : ''; - $this->assertSame('__isset', $magicIsset->getName()); - $this->assertCount(1, $magicIsset->getParameters()); - $this->assertStringMatchesFormat( - "\$this->foo && \$this->baz('__isset', array('name' => \$name));\n\n" - . "if (isset(self::\$bar[\$name])) {\n return isset(\$this->\$name);\n}\n\n" - . "%areturn %s;", - $magicIsset->getBody() - ); + 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'] : ''; - /** - * @covers \ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicIsset::__construct - */ - public function testBodyStructureWithOverriddenMagicGet() - { - $reflection = new ReflectionClass(ClassWithMagicMethods::class); - $magicIsset = new MagicIsset($reflection, $this->initializer, $this->initMethod, $this->publicProperties); + static $accessorCache = []; - $this->assertSame('__isset', $magicIsset->getName()); - $this->assertCount(1, $magicIsset->getParameters()); - $this->assertSame( - "\$this->foo && \$this->baz('__isset', array('name' => \$name));\n\n" - . "if (isset(self::\$bar[\$name])) {\n return isset(\$this->\$name);\n}\n\n" - . "return parent::__isset(\$name);", - $magicIsset->getBody() - ); + if (isset(self::$tab[$name][$class])) { + $cacheKey = $class . '#' . $name; + $accessor = isset($accessorCache[$cacheKey]) + ? $accessorCache[$cacheKey] + : $accessorCache[$cacheKey] = \Closure::bind(function () use ($name) { + return isset($this->$name); + }, $this, $class); + + return $accessor($this, $name); + } + + if ('ReflectionProperty' === $class) { + $tmpClass = key(self::$tab[$name]); + $cacheKey = $tmpClass . '#' . $name; + $accessor = isset($accessorCache[$cacheKey]) + ? $accessorCache[$cacheKey] + : $accessorCache[$cacheKey] = \Closure::bind(function () use ($name) { + return isset($this->$name); + }, $this, $tmpClass); + + return $accessor($this, $name); + } +} +%A +PHP; + + $this->assertStringMatchesFormat($expectedCode, $magicIsset->getBody()); } } From 602f7cfade25413a9766d4c5f1c512957adfe2b5 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 12:33:48 +0100 Subject: [PATCH 086/153] Testing `MagicIsset` when `__isset` is implemented in original classes --- .../MethodGenerator/MagicIssetTest.php | 118 +++++++++++------- 1 file changed, 71 insertions(+), 47 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIssetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIssetTest.php index 825a38631..39241240c 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIssetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIssetTest.php @@ -65,52 +65,7 @@ class MagicIssetTest extends PHPUnit_Framework_TestCase */ protected $privateProperties; - /** - * {@inheritDoc} - */ - protected function setUp() - { - $this->initializer = $this->getMock(PropertyGenerator::class); - $this->initMethod = $this->getMock(MethodGenerator::class); - $this->publicProperties = $this - ->getMockBuilder(PublicPropertiesMap::class) - ->disableOriginalConstructor() - ->getMock(); - $this->protectedProperties = $this - ->getMockBuilder(ProtectedPropertiesMap::class) - ->disableOriginalConstructor() - ->getMock(); - $this->privateProperties = $this - ->getMockBuilder(PrivatePropertiesMap::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->initializer->expects($this->any())->method('getName')->will($this->returnValue('foo')); - $this->initMethod->expects($this->any())->method('getName')->will($this->returnValue('baz')); - $this->publicProperties->expects($this->any())->method('isEmpty')->will($this->returnValue(false)); - $this->publicProperties->expects($this->any())->method('getName')->will($this->returnValue('bar')); - $this->protectedProperties->expects($this->any())->method('getName')->will($this->returnValue('baz')); - $this->privateProperties->expects($this->any())->method('getName')->will($this->returnValue('tab')); - } - - /** - * @covers \ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicIsset::__construct - */ - public function testBodyStructureWithPublicProperties() - { - $magicIsset = new MagicIsset( - new ReflectionClass(ClassWithTwoPublicProperties::class), - $this->initializer, - $this->initMethod, - $this->publicProperties, - $this->protectedProperties, - $this->privateProperties - ); - - $this->assertSame('__isset', $magicIsset->getName()); - $this->assertCount(1, $magicIsset->getParameters()); - - $expectedCode = <<<'PHP' + private $expectedCode = <<<'PHP' $this->foo && $this->baz('__isset', array('name' => $name)); if (isset(self::$bar[$name])) { @@ -167,6 +122,75 @@ public function testBodyStructureWithPublicProperties() %A PHP; - $this->assertStringMatchesFormat($expectedCode, $magicIsset->getBody()); + + /** + * {@inheritDoc} + */ + protected function setUp() + { + $this->initializer = $this->getMock(PropertyGenerator::class); + $this->initMethod = $this->getMock(MethodGenerator::class); + $this->publicProperties = $this + ->getMockBuilder(PublicPropertiesMap::class) + ->disableOriginalConstructor() + ->getMock(); + $this->protectedProperties = $this + ->getMockBuilder(ProtectedPropertiesMap::class) + ->disableOriginalConstructor() + ->getMock(); + $this->privateProperties = $this + ->getMockBuilder(PrivatePropertiesMap::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->initializer->expects($this->any())->method('getName')->will($this->returnValue('foo')); + $this->initMethod->expects($this->any())->method('getName')->will($this->returnValue('baz')); + $this->publicProperties->expects($this->any())->method('isEmpty')->will($this->returnValue(false)); + $this->publicProperties->expects($this->any())->method('getName')->will($this->returnValue('bar')); + $this->protectedProperties->expects($this->any())->method('getName')->will($this->returnValue('baz')); + $this->privateProperties->expects($this->any())->method('getName')->will($this->returnValue('tab')); + } + + /** + * @covers \ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicIsset + */ + public function testBodyStructureWithPublicProperties() + { + $magicIsset = new MagicIsset( + new ReflectionClass(ClassWithTwoPublicProperties::class), + $this->initializer, + $this->initMethod, + $this->publicProperties, + $this->protectedProperties, + $this->privateProperties + ); + + $this->assertSame('__isset', $magicIsset->getName()); + $this->assertCount(1, $magicIsset->getParameters()); + + $this->assertStringMatchesFormat($this->expectedCode, $magicIsset->getBody()); + } + + /** + * @covers \ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicIsset + */ + public function testBodyStructureWithOverriddenMagicGet() + { + $magicIsset = new MagicIsset( + new ReflectionClass(ClassWithMagicMethods::class), + $this->initializer, + $this->initMethod, + $this->publicProperties, + $this->protectedProperties, + $this->privateProperties + ); + + $this->assertSame('__isset', $magicIsset->getName()); + $this->assertCount(1, $magicIsset->getParameters()); + + $body = $magicIsset->getBody(); + + $this->assertStringMatchesFormat($this->expectedCode, $body); + $this->assertStringMatchesFormat('%Areturn parent::__isset($name);', $body); } } From 04bc62a64349db8d26dea9ade9e600a922424428 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 12:35:08 +0100 Subject: [PATCH 087/153] Adding property docblock --- .../LazyLoadingGhost/MethodGenerator/MagicIssetTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIssetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIssetTest.php index 39241240c..8b2c5a87b 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIssetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIssetTest.php @@ -65,6 +65,9 @@ class MagicIssetTest extends PHPUnit_Framework_TestCase */ protected $privateProperties; + /** + * @var string + */ private $expectedCode = <<<'PHP' $this->foo && $this->baz('__isset', array('name' => $name)); From 37c306521e0f8e6fed5abc9f7baaace651a3f914 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 12:36:47 +0100 Subject: [PATCH 088/153] Refactoring `MagicGet` tests to expect the code in the template in any case --- .../MethodGenerator/MagicGetTest.php | 96 ++++++++++--------- 1 file changed, 50 insertions(+), 46 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php index 863665784..a534e693a 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php @@ -65,6 +65,54 @@ class MagicGetTest extends PHPUnit_Framework_TestCase */ protected $privateProperties; + /** + * @var string + */ + private $expectedCode = <<<'PHP' +$this->foo && $this->baz('__get', array('name' => $name)); + +if (isset(self::$bar[$name])) { + return $this->$name; +} + +if (isset(self::$baz[$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::$baz[$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::$tab[$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'] : ''; + + if (isset(self::$tab[$name][$class])) { + return \Closure::bind(function & () use ($name) { + return $this->$name; + }, $this, $class)->__invoke($this, $name); + } + + if ($class === 'ReflectionProperty') { + return \Closure::bind(function & () use ($name) { + return $this->$name; + }, $this, key(self::$tab[$name]))->__invoke($this, $name); + } +} + +%a +PHP; + /** * {@inheritDoc} */ @@ -159,51 +207,7 @@ public function testBodyStructureWithOverriddenMagicGet() $this->assertSame('__get', $magicGet->getName()); $this->assertCount(1, $magicGet->getParameters()); - $expectedCode = <<<'PHP' -$this->foo && $this->baz('__get', array('name' => $name)); - -if (isset(self::$bar[$name])) { - return $this->$name; -} - -if (isset(self::$baz[$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::$baz[$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::$tab[$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'] : ''; - - if (isset(self::$tab[$name][$class])) { - return \Closure::bind(function & () use ($name) { - return $this->$name; - }, $this, $class)->__invoke($this, $name); - } - - if ($class === 'ReflectionProperty') { - return \Closure::bind(function & () use ($name) { - return $this->$name; - }, $this, key(self::$tab[$name]))->__invoke($this, $name); - } -} - -return parent::__get($name); -PHP; - - $this->assertSame($expectedCode, $magicGet->getBody()); + $this->assertStringMatchesFormat($this->expectedCode, $magicGet->getBody()); + $this->assertStringMatchesFormat('%Areturn parent::__get($name);', $magicGet->getBody()); } } From 5f834c9993b2e9f0a431f9a6257bf6ef275ab38c Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 12:37:56 +0100 Subject: [PATCH 089/153] Refactoring `MagicGet` tests, removing useless test --- .../MethodGenerator/MagicGetTest.php | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php index a534e693a..4241bff67 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php @@ -141,30 +141,6 @@ protected function setUp() $this->privateProperties->expects($this->any())->method('getName')->will($this->returnValue('tab')); } - /** - * @covers \ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicGet::__construct - */ - public function testBodyStructure() - { - $magicGet = new MagicGet( - new ReflectionClass(EmptyClass::class), - $this->initializer, - $this->initMethod, - $this->publicProperties, - $this->protectedProperties, - $this->privateProperties - ); - - $this->assertSame('__get', $magicGet->getName()); - $this->assertCount(1, $magicGet->getParameters()); - $this->assertStringMatchesFormat( - "\$this->foo && \$this->baz('__get', array('name' => \$name));\n\n" - . "if (isset(self::\$bar[\$name])) {\n return \$this->\$name;\n}\n\n" - . "%a", - $magicGet->getBody() - ); - } - /** * @covers \ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicGet::__construct */ From 7ed8bd553b9ee0508c6a29b21367bda9ee9aa9b0 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 12:42:30 +0100 Subject: [PATCH 090/153] Adding missing `@group` annotations --- .../Functional/LazyLoadingGhostFunctionalTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php index c8284af52..07fee57c6 100644 --- a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php +++ b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php @@ -325,6 +325,10 @@ public function testPrivatePropertyDefaultWillBePreserved() $this->assertSame('property0', $reflectionProperty->getValue($proxy)); } + /** + * @group 159 + * @group 192 + */ public function testMultiLevelPrivatePropertiesDefaultsWillBePreserved() { $instance = new ClassWithCollidingPrivateInheritedProperties(); @@ -343,6 +347,10 @@ public function testMultiLevelPrivatePropertiesDefaultsWillBePreserved() $this->assertSame('property0', $parentProperty->getValue($proxy)); } + /** + * @group 159 + * @group 192 + */ public function testMultiLevelPrivatePropertiesByRefInitialization() { $class = ClassWithCollidingPrivateInheritedProperties::class; From 5c4b7c5efa5ddbc747de46c646fecc792ea281df Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 12:46:10 +0100 Subject: [PATCH 091/153] Proxies should handle properties separately for every proxy instance --- .../LazyLoadingGhostFunctionalTest.php | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php index 07fee57c6..361e9adce 100644 --- a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php +++ b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php @@ -374,6 +374,48 @@ function ($proxy, $method, $params, & $initializer, array $properties) use ($cla $this->assertSame('bar', $parentProperty->getValue($proxy)); } + /** + * @group 159 + * @group 192 + * + * Test designed to verify that the cached logic does take into account the fact that + * proxies are different instances + */ + public function testGetPropertyFromDifferentProxyInstances() + { + $class = ClassWithCollidingPrivateInheritedProperties::class; + $proxyName = $this->generateProxy($class); + + /* @var $proxy ClassWithPrivateProperties */ + $proxy1 = $proxyName::staticProxyConstructor( + function ($proxy, $method, $params, & $initializer, array $properties) use ($class) { + $initializer = null; + $properties["\0" . $class . "\0property0"] = 'foo'; + $properties["\0" . get_parent_class($class) . "\0property0"] = 'bar'; + } + ); + /* @var $proxy ClassWithPrivateProperties */ + $proxy2 = $proxyName::staticProxyConstructor( + function ($proxy, $method, $params, & $initializer, array $properties) use ($class) { + $initializer = null; + $properties["\0" . $class . "\0property0"] = 'baz'; + $properties["\0" . get_parent_class($class) . "\0property0"] = 'tab'; + } + ); + + $childProperty = new ReflectionProperty($class, 'property0'); + $parentProperty = new ReflectionProperty(get_parent_class($class), 'property0'); + + $childProperty->setAccessible(true); + $parentProperty->setAccessible(true); + + $this->assertSame('foo', $childProperty->getValue($proxy1)); + $this->assertSame('bar', $parentProperty->getValue($proxy1)); + + $this->assertSame('baz', $childProperty->getValue($proxy1)); + $this->assertSame('tab', $parentProperty->getValue($proxy1)); + } + public function testByRefInitialization() { $proxyName = $this->generateProxy(ClassWithMixedProperties::class); From 6166c277ddca2839b76562a15453a654bef9f25b Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 12:46:42 +0100 Subject: [PATCH 092/153] Proxies should handle properties separately for every proxy instance --- .../Functional/LazyLoadingGhostFunctionalTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php index 361e9adce..4c7ec6a74 100644 --- a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php +++ b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php @@ -412,8 +412,8 @@ function ($proxy, $method, $params, & $initializer, array $properties) use ($cla $this->assertSame('foo', $childProperty->getValue($proxy1)); $this->assertSame('bar', $parentProperty->getValue($proxy1)); - $this->assertSame('baz', $childProperty->getValue($proxy1)); - $this->assertSame('tab', $parentProperty->getValue($proxy1)); + $this->assertSame('baz', $childProperty->getValue($proxy2)); + $this->assertSame('tab', $parentProperty->getValue($proxy2)); } public function testByRefInitialization() From 92801aa2d3f07d736f135d6752d4cd539eba832b Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 12:47:17 +0100 Subject: [PATCH 093/153] Proxies should handle properties separately for every proxy instance --- .../MethodGenerator/MagicGetTest.php | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php index 4241bff67..46c9b2f01 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php @@ -98,15 +98,26 @@ class MagicGetTest extends PHPUnit_Framework_TestCase $class = isset($caller['class']) ? $caller['class'] : ''; if (isset(self::$tab[$name][$class])) { - return \Closure::bind(function & () use ($name) { - return $this->$name; - }, $this, $class)->__invoke($this, $name); + $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, $name); } - if ($class === 'ReflectionProperty') { - return \Closure::bind(function & () use ($name) { - return $this->$name; - }, $this, key(self::$tab[$name]))->__invoke($this, $name); + if ('ReflectionProperty' === $class) { + $tmpClass = key(self::$tab[$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, $name); } } From e8c9eb0a56de2ec8eff94e0eb5a544f2f508d253 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 12:48:43 +0100 Subject: [PATCH 094/153] Accessors should be cached --- .../LazyLoadingGhost/MethodGenerator/MagicGetTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php index 46c9b2f01..635d04572 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php @@ -97,6 +97,8 @@ class MagicGetTest extends PHPUnit_Framework_TestCase $caller = isset($callers[1]) ? $callers[1] : []; $class = isset($caller['class']) ? $caller['class'] : ''; + static $accessorCache = []; + if (isset(self::$tab[$name][$class])) { $cacheKey = $class . '#' . $name; $accessor = isset($accessorCache[$cacheKey]) From 2ce80d1f34c0b5a45b740d584daccb4b42a9073f Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 12:50:12 +0100 Subject: [PATCH 095/153] Caching individual accessors in `__get` logic: accessors are now instance-agnostic as well --- .../MethodGenerator/MagicGet.php | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php index df9cca4eb..557e1f50e 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php @@ -85,16 +85,29 @@ public function __construct( $caller = isset($callers[1]) ? $callers[1] : []; $class = isset($caller['class']) ? $caller['class'] : ''; + static $accessorCache = []; + if (isset(self::$%s[$name][$class])) { - return \Closure::bind(function & () use ($name) { - return $this->$name; - }, $this, $class)->__invoke($this, $name); + $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, $name); } - if ($class === 'ReflectionProperty') { - return \Closure::bind(function & () use ($name) { - return $this->$name; - }, $this, key(self::$%s[$name]))->__invoke($this, $name); + 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 $instance->$name; + }, null, $tmpClass); + + return $accessor($this, $name); } } From 881d9f912e0362f1f8a30f7e7b6bbfb3da1bc72f Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 12:50:47 +0100 Subject: [PATCH 096/153] Cached accessors should be instance-agnostic --- .../MethodGenerator/MagicIssetTest.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIssetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIssetTest.php index 8b2c5a87b..eebd7ba5c 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIssetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIssetTest.php @@ -103,9 +103,9 @@ class MagicIssetTest extends PHPUnit_Framework_TestCase $cacheKey = $class . '#' . $name; $accessor = isset($accessorCache[$cacheKey]) ? $accessorCache[$cacheKey] - : $accessorCache[$cacheKey] = \Closure::bind(function () use ($name) { - return isset($this->$name); - }, $this, $class); + : $accessorCache[$cacheKey] = \Closure::bind(function ($instance) use ($name) { + return isset($instance->$name); + }, null, $class); return $accessor($this, $name); } @@ -115,9 +115,9 @@ class MagicIssetTest extends PHPUnit_Framework_TestCase $cacheKey = $tmpClass . '#' . $name; $accessor = isset($accessorCache[$cacheKey]) ? $accessorCache[$cacheKey] - : $accessorCache[$cacheKey] = \Closure::bind(function () use ($name) { - return isset($this->$name); - }, $this, $tmpClass); + : $accessorCache[$cacheKey] = \Closure::bind(function ($instance) use ($name) { + return isset($instance->$name); + }, null, $tmpClass); return $accessor($this, $name); } From 59c8def9058f81bab061730b6112840dbbd8f60a Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 12:51:52 +0100 Subject: [PATCH 097/153] No need to pass parameter `$name` to cached `__get` accessors --- .../LazyLoadingGhost/MethodGenerator/MagicGetTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php index 635d04572..b2b3cfb63 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php @@ -107,7 +107,7 @@ class MagicGetTest extends PHPUnit_Framework_TestCase return $instance->$name; }, null, $class); - return $accessor($this, $name); + return $accessor($this); } if ('ReflectionProperty' === $class) { @@ -119,7 +119,7 @@ class MagicGetTest extends PHPUnit_Framework_TestCase return $instance->$name; }, null, $tmpClass); - return $accessor($this, $name); + return $accessor($this); } } From 65dfd76b15459285f77e1b5065e0de713a7c4f6d Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 12:51:58 +0100 Subject: [PATCH 098/153] No need to pass parameter `$name` to cached `__get` accessors --- .../LazyLoadingGhost/MethodGenerator/MagicGet.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php index 557e1f50e..ae1aaef0a 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGet.php @@ -95,7 +95,7 @@ public function __construct( return $instance->$name; }, null, $class); - return $accessor($this, $name); + return $accessor($this); } if ('ReflectionProperty' === $class) { @@ -107,7 +107,7 @@ public function __construct( return $instance->$name; }, null, $tmpClass); - return $accessor($this, $name); + return $accessor($this); } } From 5f05fa461599fa2803d555d7072809cb25d58e0e Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 12:52:52 +0100 Subject: [PATCH 099/153] No need to pass parameter `$name` to cached `__isset` accessors --- .../LazyLoadingGhost/MethodGenerator/MagicIssetTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIssetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIssetTest.php index eebd7ba5c..f46017a8b 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIssetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIssetTest.php @@ -107,7 +107,7 @@ class MagicIssetTest extends PHPUnit_Framework_TestCase return isset($instance->$name); }, null, $class); - return $accessor($this, $name); + return $accessor($this); } if ('ReflectionProperty' === $class) { @@ -119,7 +119,7 @@ class MagicIssetTest extends PHPUnit_Framework_TestCase return isset($instance->$name); }, null, $tmpClass); - return $accessor($this, $name); + return $accessor($this); } } %A From 07f412530b05eed0b987ce608b8233c7c8440bc2 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 12:53:09 +0100 Subject: [PATCH 100/153] No need to pass parameter `$name` to cached `__isset` accessors, making accessors instance-agnostic --- .../MethodGenerator/MagicIsset.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php index cd67c441d..b91cdd2fe 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php @@ -91,11 +91,11 @@ public function __construct( $cacheKey = $class . '#' . $name; $accessor = isset($accessorCache[$cacheKey]) ? $accessorCache[$cacheKey] - : $accessorCache[$cacheKey] = \Closure::bind(function () use ($name) { - return isset($this->$name); - }, $this, $class); + : $accessorCache[$cacheKey] = \Closure::bind(function ($instance) use ($name) { + return isset($instance->$name); + }, null, $class); - return $accessor($this, $name); + return $accessor($this); } if ('ReflectionProperty' === $class) { @@ -103,11 +103,11 @@ public function __construct( $cacheKey = $tmpClass . '#' . $name; $accessor = isset($accessorCache[$cacheKey]) ? $accessorCache[$cacheKey] - : $accessorCache[$cacheKey] = \Closure::bind(function () use ($name) { - return isset($this->$name); - }, $this, $tmpClass); + : $accessorCache[$cacheKey] = \Closure::bind(function ($instance) use ($name) { + return isset($instance->$name); + }, null, $tmpClass); - return $accessor($this, $name); + return $accessor($this); } } From 75415889aff96a7cb7df09bbff6b00ca459557ab Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 12:55:37 +0100 Subject: [PATCH 101/153] New test asset: class that provides means to use `isset()` in private scope --- .../ClassWithMixedPropertiesAndIssetCheck.php | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 tests/ProxyManagerTestAsset/ClassWithMixedPropertiesAndIssetCheck.php diff --git a/tests/ProxyManagerTestAsset/ClassWithMixedPropertiesAndIssetCheck.php b/tests/ProxyManagerTestAsset/ClassWithMixedPropertiesAndIssetCheck.php new file mode 100644 index 000000000..6ce85aaf2 --- /dev/null +++ b/tests/ProxyManagerTestAsset/ClassWithMixedPropertiesAndIssetCheck.php @@ -0,0 +1,53 @@ + + * @license MIT + */ +class ClassWithMixedPropertiesAndIssetCheck +{ + /** + * @var mixed + */ + public $publicProperty; + + /** + * @var mixed + */ + protected $protectedProperty; + + /** + * @var mixed + */ + private $privateProperty; + + /** + * @param string $name + * + * @return bool + */ + public function hasProperty($name) + { + return isset($this->$name); + } +} From 761dc2fbce122c56d35be51011354ca2a0d7e5e9 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 12:56:44 +0100 Subject: [PATCH 102/153] Renaming: making the class a multi-purpose property-state check class --- ...Check.php => ClassWithMixedPropertiesAndAccessorMethods.php} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/ProxyManagerTestAsset/{ClassWithMixedPropertiesAndIssetCheck.php => ClassWithMixedPropertiesAndAccessorMethods.php} (96%) diff --git a/tests/ProxyManagerTestAsset/ClassWithMixedPropertiesAndIssetCheck.php b/tests/ProxyManagerTestAsset/ClassWithMixedPropertiesAndAccessorMethods.php similarity index 96% rename from tests/ProxyManagerTestAsset/ClassWithMixedPropertiesAndIssetCheck.php rename to tests/ProxyManagerTestAsset/ClassWithMixedPropertiesAndAccessorMethods.php index 6ce85aaf2..79e20b7c5 100644 --- a/tests/ProxyManagerTestAsset/ClassWithMixedPropertiesAndIssetCheck.php +++ b/tests/ProxyManagerTestAsset/ClassWithMixedPropertiesAndAccessorMethods.php @@ -24,7 +24,7 @@ * @author Marco Pivetta * @license MIT */ -class ClassWithMixedPropertiesAndIssetCheck +class ClassWithMixedPropertiesAndAccessorMethods { /** * @var mixed From 407e6243da121746929f5e24c7bdb999944415be Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 12:58:15 +0100 Subject: [PATCH 103/153] Adding pseudo-magic methods for private access --- ...sWithMixedPropertiesAndAccessorMethods.php | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/tests/ProxyManagerTestAsset/ClassWithMixedPropertiesAndAccessorMethods.php b/tests/ProxyManagerTestAsset/ClassWithMixedPropertiesAndAccessorMethods.php index 79e20b7c5..a581fbb96 100644 --- a/tests/ProxyManagerTestAsset/ClassWithMixedPropertiesAndAccessorMethods.php +++ b/tests/ProxyManagerTestAsset/ClassWithMixedPropertiesAndAccessorMethods.php @@ -46,8 +46,39 @@ class ClassWithMixedPropertiesAndAccessorMethods * * @return bool */ - public function hasProperty($name) + public function has($name) { return isset($this->$name); } + + /** + * @param string $name + * + * @return mixed + */ + public function get($name) + { + return $this->$name; + } + + /** + * @param string $name + * @param mixed $name + * + * @return void + */ + public function set($name, $value) + { + $this->$name = $value; + } + + /** + * @param string $name + * + * @return void + */ + public function remove($name) + { + unset($this->$name); + } } From 8da734ea93ed6b62c11394c099a30e02a09d5bf8 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 13:01:02 +0100 Subject: [PATCH 104/153] Test private property write access on different proxy instances --- .../LazyLoadingGhostFunctionalTest.php | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php index 4c7ec6a74..6f729b3db 100644 --- a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php +++ b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php @@ -31,6 +31,7 @@ use ProxyManagerTestAsset\ClassWithCollidingPrivateInheritedProperties; use ProxyManagerTestAsset\ClassWithCounterConstructor; use ProxyManagerTestAsset\ClassWithMixedProperties; +use ProxyManagerTestAsset\ClassWithMixedPropertiesAndAccessorMethods; use ProxyManagerTestAsset\ClassWithPrivateProperties; use ProxyManagerTestAsset\ClassWithProtectedProperties; use ProxyManagerTestAsset\ClassWithPublicArrayProperty; @@ -386,7 +387,7 @@ public function testGetPropertyFromDifferentProxyInstances() $class = ClassWithCollidingPrivateInheritedProperties::class; $proxyName = $this->generateProxy($class); - /* @var $proxy ClassWithPrivateProperties */ + /* @var $proxy1 ClassWithPrivateProperties */ $proxy1 = $proxyName::staticProxyConstructor( function ($proxy, $method, $params, & $initializer, array $properties) use ($class) { $initializer = null; @@ -394,7 +395,7 @@ function ($proxy, $method, $params, & $initializer, array $properties) use ($cla $properties["\0" . get_parent_class($class) . "\0property0"] = 'bar'; } ); - /* @var $proxy ClassWithPrivateProperties */ + /* @var $proxy2 ClassWithPrivateProperties */ $proxy2 = $proxyName::staticProxyConstructor( function ($proxy, $method, $params, & $initializer, array $properties) use ($class) { $initializer = null; @@ -416,6 +417,41 @@ function ($proxy, $method, $params, & $initializer, array $properties) use ($cla $this->assertSame('tab', $parentProperty->getValue($proxy2)); } + /** + * @group 159 + * @group 192 + * + * Test designed to verify that the cached logic does take into account the fact that + * proxies are different instances + */ + public function testSetPrivatePropertyOnDifferentProxyInstances() + { + $class = ClassWithMixedPropertiesAndAccessorMethods::class; + $proxyName = $this->generateProxy($class); + + /* @var $proxy1 ClassWithMixedPropertiesAndAccessorMethods */ + $proxy1 = $proxyName::staticProxyConstructor( + function ($proxy, $method, $params, & $initializer, array $properties) use ($class) { + $initializer = null; + $properties["\0" . $class . "\0property0"] = 'foo'; + $properties["\0" . get_parent_class($class) . "\0property0"] = 'bar'; + } + ); + /* @var $proxy2 ClassWithMixedPropertiesAndAccessorMethods */ + $proxy2 = $proxyName::staticProxyConstructor( + function ($proxy, $method, $params, & $initializer, array $properties) use ($class) { + $initializer = null; + $properties["\0" . $class . "\0property0"] = 'foo'; + $properties["\0" . get_parent_class($class) . "\0property0"] = 'bar'; + } + ); + + $proxy1->set('privateProperty', 'private1'); + $proxy2->set('privateProperty', 'private2'); + $this->assertSame('private1', $proxy1->get('privateProperty')); + $this->assertSame('private2', $proxy2->get('privateProperty')); + } + public function testByRefInitialization() { $proxyName = $this->generateProxy(ClassWithMixedProperties::class); From cc4f9cef69f736d895543acf39fde2dca16f07ed Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 13:01:43 +0100 Subject: [PATCH 105/153] Default values for properties --- .../ClassWithMixedPropertiesAndAccessorMethods.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ProxyManagerTestAsset/ClassWithMixedPropertiesAndAccessorMethods.php b/tests/ProxyManagerTestAsset/ClassWithMixedPropertiesAndAccessorMethods.php index a581fbb96..4aa7b6252 100644 --- a/tests/ProxyManagerTestAsset/ClassWithMixedPropertiesAndAccessorMethods.php +++ b/tests/ProxyManagerTestAsset/ClassWithMixedPropertiesAndAccessorMethods.php @@ -29,17 +29,17 @@ class ClassWithMixedPropertiesAndAccessorMethods /** * @var mixed */ - public $publicProperty; + public $publicProperty = 'publicProperty'; /** * @var mixed */ - protected $protectedProperty; + protected $protectedProperty = 'protectedProperty'; /** * @var mixed */ - private $privateProperty; + private $privateProperty = 'privateProperty'; /** * @param string $name From e1ca2311c6c8c8451d0927b2b9f66a67d37094e2 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 13:02:19 +0100 Subject: [PATCH 106/153] Removing useless initialization code --- .../LazyLoadingGhostFunctionalTest.php | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php index 6f729b3db..d3757cae7 100644 --- a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php +++ b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php @@ -429,6 +429,37 @@ public function testSetPrivatePropertyOnDifferentProxyInstances() $class = ClassWithMixedPropertiesAndAccessorMethods::class; $proxyName = $this->generateProxy($class); + /* @var $proxy1 ClassWithMixedPropertiesAndAccessorMethods */ + $proxy1 = $proxyName::staticProxyConstructor( + function ($proxy, $method, $params, & $initializer, array $properties) use ($class) { + $initializer = null; + } + ); + /* @var $proxy2 ClassWithMixedPropertiesAndAccessorMethods */ + $proxy2 = $proxyName::staticProxyConstructor( + function ($proxy, $method, $params, & $initializer, array $properties) use ($class) { + $initializer = null; + } + ); + + $proxy1->set('privateProperty', 'private1'); + $proxy2->set('privateProperty', 'private2'); + $this->assertSame('private1', $proxy1->get('privateProperty')); + $this->assertSame('private2', $proxy2->get('privateProperty')); + } + + /** + * @group 159 + * @group 192 + * + * Test designed to verify that the cached logic does take into account the fact that + * proxies are different instances + */ + public function testIssetPrivatePropertyOnDifferentProxyInstances() + { + $class = ClassWithMixedPropertiesAndAccessorMethods::class; + $proxyName = $this->generateProxy($class); + /* @var $proxy1 ClassWithMixedPropertiesAndAccessorMethods */ $proxy1 = $proxyName::staticProxyConstructor( function ($proxy, $method, $params, & $initializer, array $properties) use ($class) { From b98f51394753378bc1faa0f46f0c9258adb2ade7 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 13:04:25 +0100 Subject: [PATCH 107/153] Verifying that private property access does not leak across instances during initialization --- .../LazyLoadingGhostFunctionalTest.php | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php index d3757cae7..dd29d0b42 100644 --- a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php +++ b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php @@ -431,13 +431,13 @@ public function testSetPrivatePropertyOnDifferentProxyInstances() /* @var $proxy1 ClassWithMixedPropertiesAndAccessorMethods */ $proxy1 = $proxyName::staticProxyConstructor( - function ($proxy, $method, $params, & $initializer, array $properties) use ($class) { + function ($proxy, $method, $params, & $initializer, array $properties) { $initializer = null; } ); /* @var $proxy2 ClassWithMixedPropertiesAndAccessorMethods */ $proxy2 = $proxyName::staticProxyConstructor( - function ($proxy, $method, $params, & $initializer, array $properties) use ($class) { + function ($proxy, $method, $params, & $initializer, array $properties) { $initializer = null; } ); @@ -462,25 +462,22 @@ public function testIssetPrivatePropertyOnDifferentProxyInstances() /* @var $proxy1 ClassWithMixedPropertiesAndAccessorMethods */ $proxy1 = $proxyName::staticProxyConstructor( - function ($proxy, $method, $params, & $initializer, array $properties) use ($class) { + function ($proxy, $method, $params, & $initializer, array $properties) { $initializer = null; - $properties["\0" . $class . "\0property0"] = 'foo'; - $properties["\0" . get_parent_class($class) . "\0property0"] = 'bar'; } ); /* @var $proxy2 ClassWithMixedPropertiesAndAccessorMethods */ $proxy2 = $proxyName::staticProxyConstructor( function ($proxy, $method, $params, & $initializer, array $properties) use ($class) { $initializer = null; - $properties["\0" . $class . "\0property0"] = 'foo'; - $properties["\0" . get_parent_class($class) . "\0property0"] = 'bar'; + $properties["\0" . $class . "\0" . "privateProperty"] = null; } ); - $proxy1->set('privateProperty', 'private1'); - $proxy2->set('privateProperty', 'private2'); - $this->assertSame('private1', $proxy1->get('privateProperty')); - $this->assertSame('private2', $proxy2->get('privateProperty')); + $this->assertTrue($proxy1->has('privateProperty')); + $this->assertFalse($proxy2->has('privateProperty')); + $this->assertTrue($proxy1->has('privateProperty')); + $this->assertFalse($proxy2->has('privateProperty')); } public function testByRefInitialization() From 9cad99f19f181e9a503bba2be4b592d758dd2ab8 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 13:06:26 +0100 Subject: [PATCH 108/153] Verifying that private property `unset` does not leak across instances during initialization --- .../LazyLoadingGhostFunctionalTest.php | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php index dd29d0b42..38d10838f 100644 --- a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php +++ b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php @@ -480,6 +480,40 @@ function ($proxy, $method, $params, & $initializer, array $properties) use ($cla $this->assertFalse($proxy2->has('privateProperty')); } + /** + * @group 159 + * @group 192 + * + * Test designed to verify that the cached logic does take into account the fact that + * proxies are different instances + */ + public function testUnsetPrivatePropertyOnDifferentProxyInstances() + { + $class = ClassWithMixedPropertiesAndAccessorMethods::class; + $proxyName = $this->generateProxy($class); + + /* @var $proxy1 ClassWithMixedPropertiesAndAccessorMethods */ + $proxy1 = $proxyName::staticProxyConstructor( + function ($proxy, $method, $params, & $initializer, array $properties) { + $initializer = null; + } + ); + /* @var $proxy2 ClassWithMixedPropertiesAndAccessorMethods */ + $proxy2 = $proxyName::staticProxyConstructor( + function ($proxy, $method, $params, & $initializer, array $properties) { + $initializer = null; + } + ); + + $this->assertTrue($proxy1->has('privateProperty')); + $proxy2->remove('privateProperty'); + $this->assertFalse($proxy2->has('privateProperty')); + $this->assertTrue($proxy1->has('privateProperty')); + $proxy1->remove('privateProperty'); + $this->assertFalse($proxy1->has('privateProperty')); + $this->assertFalse($proxy2->has('privateProperty')); + } + public function testByRefInitialization() { $proxyName = $this->generateProxy(ClassWithMixedProperties::class); From 754dfa86143c33da4432d3659e63de02d5bd30ab Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 13:10:07 +0100 Subject: [PATCH 109/153] Fixing constructor parameters mismatch in tests --- .../MethodGenerator/MagicSetTest.php | 51 +++++++++++++++---- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php index cdae84b88..f41bde6fc 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php @@ -20,6 +20,8 @@ use PHPUnit_Framework_TestCase; use ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicSet; +use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PrivatePropertiesMap; +use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\ProtectedPropertiesMap; use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap; use ProxyManagerTestAsset\ClassWithMagicMethods; use ProxyManagerTestAsset\EmptyClass; @@ -53,6 +55,16 @@ class MagicSetTest extends PHPUnit_Framework_TestCase */ protected $publicProperties; + /** + * @var ProtectedPropertiesMap|\PHPUnit_Framework_MockObject_MockObject + */ + protected $protectedProperties; + + /** + * @var PrivatePropertiesMap|\PHPUnit_Framework_MockObject_MockObject + */ + protected $privateProperties; + /** * {@inheritDoc} */ @@ -64,6 +76,14 @@ protected function setUp() ->getMockBuilder(PublicPropertiesMap::class) ->disableOriginalConstructor() ->getMock(); + $this->protectedProperties = $this + ->getMockBuilder(ProtectedPropertiesMap::class) + ->disableOriginalConstructor() + ->getMock(); + $this->privateProperties = $this + ->getMockBuilder(PrivatePropertiesMap::class) + ->disableOriginalConstructor() + ->getMock(); $this->initializer->expects($this->any())->method('getName')->will($this->returnValue('foo')); $this->initMethod->expects($this->any())->method('getName')->will($this->returnValue('baz')); @@ -76,8 +96,14 @@ protected function setUp() */ public function testBodyStructure() { - $reflection = new ReflectionClass(EmptyClass::class); - $magicSet = new MagicSet($reflection, $this->initializer, $this->initMethod, $this->publicProperties); + $magicSet = new MagicSet( + new ReflectionClass(EmptyClass::class), + $this->initializer, + $this->initMethod, + $this->publicProperties, + $this->protectedProperties, + $this->privateProperties + ); $this->assertSame('__set', $magicSet->getName()); $this->assertCount(2, $magicSet->getParameters()); @@ -94,11 +120,13 @@ public function testBodyStructure() */ public function testBodyStructureWithPublicProperties() { - $reflection = new ReflectionClass( - ClassWithTwoPublicProperties::class - ); - - $magicSet = new MagicSet($reflection, $this->initializer, $this->initMethod, $this->publicProperties); + $magicSet = new MagicSet( + new ReflectionClass(ClassWithTwoPublicProperties::class), + $this->initializer, + $this->initMethod, + $this->publicProperties, + $this->protectedProperties, + $this->privateProperties); $this->assertSame('__set', $magicSet->getName()); $this->assertCount(2, $magicSet->getParameters()); @@ -115,8 +143,13 @@ public function testBodyStructureWithPublicProperties() */ public function testBodyStructureWithOverriddenMagicGet() { - $reflection = new ReflectionClass(ClassWithMagicMethods::class); - $magicSet = new MagicSet($reflection, $this->initializer, $this->initMethod, $this->publicProperties); + $magicSet = new MagicSet( + new ReflectionClass(ClassWithMagicMethods::class), + $this->initializer, + $this->initMethod, + $this->publicProperties, + $this->protectedProperties, + $this->privateProperties); $this->assertSame('__set', $magicSet->getName()); $this->assertCount(2, $magicSet->getParameters()); From dd50f5be115e92363927d16b1e60301b4f70839c Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 13:12:34 +0100 Subject: [PATCH 110/153] Test should expect the magic `__set` to interact with private properties and cache accessors correctly --- .../MethodGenerator/MagicSetTest.php | 68 +++++++++++++++++-- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php index f41bde6fc..27573879d 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php @@ -65,6 +65,67 @@ class MagicSetTest extends PHPUnit_Framework_TestCase */ protected $privateProperties; + /** + * @var string + */ + private $expectedCode = <<<'PHP' +$this->foo && $this->baz('__get', array('name' => $name)); + +if (isset(self::$bar[$name])) { + return $this->$name; +} + +if (isset(self::$baz[$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::$baz[$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::$tab[$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::$tab[$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::$tab[$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); + } +} + +%a +PHP; + /** * {@inheritDoc} */ @@ -107,12 +168,7 @@ public function testBodyStructure() $this->assertSame('__set', $magicSet->getName()); $this->assertCount(2, $magicSet->getParameters()); - $this->assertStringMatchesFormat( - "\$this->foo && \$this->baz('__set', array('name' => \$name, 'value' => \$value));\n\n" - . "if (isset(self::\$bar[\$name])) {\n return (\$this->\$name = \$value);\n}\n\n" - . "%areturn %s;", - $magicSet->getBody() - ); + $this->assertStringMatchesFormat($this->expectedCode, $magicSet->getBody()); } /** From 9ac39c7d5c57bd654fa30041afba819ba4b4763f Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 13:13:05 +0100 Subject: [PATCH 111/153] Corrected expected parameters passed to the initializer method --- .../LazyLoadingGhost/MethodGenerator/MagicSetTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php index 27573879d..e76414200 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php @@ -69,7 +69,7 @@ class MagicSetTest extends PHPUnit_Framework_TestCase * @var string */ private $expectedCode = <<<'PHP' -$this->foo && $this->baz('__get', array('name' => $name)); +$this->foo && $this->baz('__get', array('name' => $name, 'value' => $value)); if (isset(self::$bar[$name])) { return $this->$name; From 39d2d10812e207013d05e80ea37322576feffbf4 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 13:15:23 +0100 Subject: [PATCH 112/153] Fixing test inconsistencies caused by copying the boilerplate code from the `__get` test --- .../LazyLoadingGhost/MethodGenerator/MagicSetTest.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php index e76414200..589eb0016 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php @@ -69,10 +69,10 @@ class MagicSetTest extends PHPUnit_Framework_TestCase * @var string */ private $expectedCode = <<<'PHP' -$this->foo && $this->baz('__get', array('name' => $name, 'value' => $value)); +$this->foo && $this->baz('__set', array('name' => $name, 'value' => $value)); if (isset(self::$bar[$name])) { - return $this->$name; + return ($this->$name = $value); } if (isset(self::$baz[$name])) { @@ -83,13 +83,13 @@ class MagicSetTest extends PHPUnit_Framework_TestCase $expectedType = self::$baz[$name]; if ($object instanceof $expectedType) { - return $this->$name; + return ($this->$name = $value); } $class = isset($caller['class']) ? $caller['class'] : ''; if ($class === $expectedType || is_subclass_of($class, $expectedType) || $class === 'ReflectionProperty') { - return $this->$name; + return ($this->$name = $value); } } elseif (isset(self::$tab[$name])) { // check private property access via same class @@ -150,6 +150,8 @@ protected function setUp() $this->initMethod->expects($this->any())->method('getName')->will($this->returnValue('baz')); $this->publicProperties->expects($this->any())->method('isEmpty')->will($this->returnValue(false)); $this->publicProperties->expects($this->any())->method('getName')->will($this->returnValue('bar')); + $this->protectedProperties->expects($this->any())->method('getName')->will($this->returnValue('baz')); + $this->privateProperties->expects($this->any())->method('getName')->will($this->returnValue('tab')); } /** From bbfd1df5e467e0fab59674928118e676c8c25fd6 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 13:30:36 +0100 Subject: [PATCH 113/153] Removing useless test, asserting structure also in cases where a magic setter is provided in the base class --- .../MethodGenerator/MagicGetTest.php | 25 +------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php index b2b3cfb63..a5a9981fe 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php @@ -154,30 +154,6 @@ protected function setUp() $this->privateProperties->expects($this->any())->method('getName')->will($this->returnValue('tab')); } - /** - * @covers \ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicGet::__construct - */ - public function testBodyStructureWithPublicProperties() - { - $magicGet = new MagicGet( - new ReflectionClass(ClassWithTwoPublicProperties::class), - $this->initializer, - $this->initMethod, - $this->publicProperties, - $this->protectedProperties, - $this->privateProperties - ); - - $this->assertSame('__get', $magicGet->getName()); - $this->assertCount(1, $magicGet->getParameters()); - - $this->assertStringMatchesFormat( - "\$this->foo && \$this->baz('__get', array('name' => \$name));\n\n" - . "if (isset(self::\$bar[\$name])) {\n return \$this->\$name;\n}\n\n" - . "%a", - $magicGet->getBody() - ); - } /** * @covers \ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicGet::__construct @@ -196,6 +172,7 @@ public function testBodyStructureWithOverriddenMagicGet() $this->assertSame('__get', $magicGet->getName()); $this->assertCount(1, $magicGet->getParameters()); + $this->assertStringMatchesFormat($this->expectedCode, $magicGet->getBody()); $this->assertStringMatchesFormat($this->expectedCode, $magicGet->getBody()); $this->assertStringMatchesFormat('%Areturn parent::__get($name);', $magicGet->getBody()); } From a0f18cf98c3f712903944059db667e17ddbdf310 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 13:37:32 +0100 Subject: [PATCH 114/153] Fixed coverage annotations --- .../LazyLoadingGhost/MethodGenerator/MagicGetTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php index a5a9981fe..d6389b653 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php @@ -156,7 +156,7 @@ protected function setUp() /** - * @covers \ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicGet::__construct + * @covers \ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicGet */ public function testBodyStructureWithOverriddenMagicGet() { From 2682110f22e2bfb8e54ab645e6e5aca0fc5cb8f9 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 13:41:34 +0100 Subject: [PATCH 115/153] Syntax fix (consistency with implementation) --- .../LazyLoadingGhost/MethodGenerator/MagicSetTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php index 589eb0016..c7fb8ae83 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php @@ -116,7 +116,7 @@ class MagicSetTest extends PHPUnit_Framework_TestCase $accessor = isset($accessorCache[$cacheKey]) ? $accessorCache[$cacheKey] : $accessorCache[$cacheKey] = \Closure::bind(function ($instance, $value) use ($name) { - return $instance->$name = $value; + return ($instance->$name = $value); }, null, $tmpClass); return $accessor($this, $value); From 8dd4fd9a1ee6eac52f422ef10c0025b3956579ee Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 13:42:08 +0100 Subject: [PATCH 116/153] Removing useless test --- .../MethodGenerator/MagicSetTest.php | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php index c7fb8ae83..ef5be236e 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php @@ -173,29 +173,6 @@ public function testBodyStructure() $this->assertStringMatchesFormat($this->expectedCode, $magicSet->getBody()); } - /** - * @covers \ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicSet::__construct - */ - public function testBodyStructureWithPublicProperties() - { - $magicSet = new MagicSet( - new ReflectionClass(ClassWithTwoPublicProperties::class), - $this->initializer, - $this->initMethod, - $this->publicProperties, - $this->protectedProperties, - $this->privateProperties); - - $this->assertSame('__set', $magicSet->getName()); - $this->assertCount(2, $magicSet->getParameters()); - $this->assertStringMatchesFormat( - "\$this->foo && \$this->baz('__set', array('name' => \$name, 'value' => \$value));\n\n" - . "if (isset(self::\$bar[\$name])) {\n return (\$this->\$name = \$value);\n}\n\n" - . "%areturn %s;", - $magicSet->getBody() - ); - } - /** * @covers \ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicSet::__construct */ From 9dd35c68717cf0012b2fef611f69792e26e865af Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 13:42:29 +0100 Subject: [PATCH 117/153] PSR-2 compliance (closing brace on newline) --- .../LazyLoadingGhost/MethodGenerator/MagicSetTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php index ef5be236e..cb528e598 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php @@ -184,7 +184,8 @@ public function testBodyStructureWithOverriddenMagicGet() $this->initMethod, $this->publicProperties, $this->protectedProperties, - $this->privateProperties); + $this->privateProperties + ); $this->assertSame('__set', $magicSet->getName()); $this->assertCount(2, $magicSet->getParameters()); From 2855bd72a213e6b214867e7988875256e45c5226 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 13:43:47 +0100 Subject: [PATCH 118/153] Simplified test for cases where a parent `__set()` method is provided --- .../LazyLoadingGhost/MethodGenerator/MagicSetTest.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php index cb528e598..ddbae4a0e 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSetTest.php @@ -189,11 +189,10 @@ public function testBodyStructureWithOverriddenMagicGet() $this->assertSame('__set', $magicSet->getName()); $this->assertCount(2, $magicSet->getParameters()); - $this->assertSame( - "\$this->foo && \$this->baz('__set', array('name' => \$name, 'value' => \$value));\n\n" - . "if (isset(self::\$bar[\$name])) {\n return (\$this->\$name = \$value);\n}\n\n" - . "return parent::__set(\$name, \$value);", - $magicSet->getBody() - ); + + $body = $magicSet->getBody(); + + $this->assertStringMatchesFormat($this->expectedCode, $body); + $this->assertStringMatchesFormat('%Areturn parent::__set($name, $value);', $body); } } From dd1f36952bc2ba8a0a2f56e4e69ce3df7505eee1 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 13:44:02 +0100 Subject: [PATCH 119/153] Implementation as per test specification --- .../MethodGenerator/MagicSet.php | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php index 111726b2e..518a5db34 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicSet.php @@ -74,13 +74,13 @@ public function __construct( $object = isset($caller['object']) ? $caller['object'] : ''; $expectedType = self::$%s[$name]; - if ($object === $this || $object instanceof $expectedType) { + if ($object instanceof $expectedType) { return ($this->$name = $value); } $class = isset($caller['class']) ? $caller['class'] : ''; - if ($class === $expectedType || is_subclass_of($class, $expectedType)) { + if ($class === $expectedType || is_subclass_of($class, $expectedType) || $class === 'ReflectionProperty') { return ($this->$name = $value); } } elseif (isset(self::$%s[$name])) { @@ -89,19 +89,33 @@ public function __construct( $caller = isset($callers[1]) ? $callers[1] : []; $class = isset($caller['class']) ? $caller['class'] : ''; + static $accessorCache = []; + if (isset(self::$%s[$name][$class])) { - return \Closure::bind(function () use ($name, $value) { - return ($this->$name = $value); - }, $this, $class)->__invoke($this, $name); + $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 ($class === 'ReflectionProperty') { - return \Closure::bind(function () use ($name, $value) { - return ($this->$name = $value); - }, $this, key(self::$%s[$name]))->__invoke($this, $name); + 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); } } + PHP; $callParent = sprintf( From 10b89be4f0b19ca041f37358373c61afeca6c418 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 13:50:06 +0100 Subject: [PATCH 120/153] Aligning test implementation to other magic method generator tests --- .../MethodGenerator/MagicUnsetTest.php | 127 ++++++++++++++---- 1 file changed, 103 insertions(+), 24 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnsetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnsetTest.php index e7fb73d85..b165664a7 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnsetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnsetTest.php @@ -20,6 +20,8 @@ use PHPUnit_Framework_TestCase; use ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicUnset; +use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PrivatePropertiesMap; +use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\ProtectedPropertiesMap; use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap; use ProxyManagerTestAsset\ClassWithMagicMethods; use ProxyManagerTestAsset\EmptyClass; @@ -53,6 +55,82 @@ class MagicUnsetTest extends PHPUnit_Framework_TestCase */ protected $publicProperties; + /** + * @var ProtectedPropertiesMap|\PHPUnit_Framework_MockObject_MockObject + */ + protected $protectedProperties; + + /** + * @var PrivatePropertiesMap|\PHPUnit_Framework_MockObject_MockObject + */ + protected $privateProperties; + + /** + * @var string + */ + private $expectedCode = <<<'PHP' +$this->foo && $this->baz('__isset', array('name' => $name)); + +if (isset(self::$bar[$name])) { + unset($this->$name); + + return; +} + +if (isset(self::$baz[$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::$baz[$name]; + + if ($object instanceof $expectedType) { + unset($this->$name); + + return; + } + + $class = isset($caller['class']) ? $caller['class'] : ''; + + if ($class === $expectedType || is_subclass_of($class, $expectedType)) { + unset($this->$name); + + return; + } +} 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::$tab[$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::$tab[$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); + } +} +%A +PHP; + /** * {@inheritDoc} */ @@ -64,11 +142,21 @@ protected function setUp() ->getMockBuilder(PublicPropertiesMap::class) ->disableOriginalConstructor() ->getMock(); + $this->protectedProperties = $this + ->getMockBuilder(ProtectedPropertiesMap::class) + ->disableOriginalConstructor() + ->getMock(); + $this->privateProperties = $this + ->getMockBuilder(PrivatePropertiesMap::class) + ->disableOriginalConstructor() + ->getMock(); $this->initializer->expects($this->any())->method('getName')->will($this->returnValue('foo')); $this->initMethod->expects($this->any())->method('getName')->will($this->returnValue('baz')); $this->publicProperties->expects($this->any())->method('isEmpty')->will($this->returnValue(false)); $this->publicProperties->expects($this->any())->method('getName')->will($this->returnValue('bar')); + $this->protectedProperties->expects($this->any())->method('getName')->will($this->returnValue('baz')); + $this->privateProperties->expects($this->any())->method('getName')->will($this->returnValue('tab')); } /** @@ -76,30 +164,15 @@ protected function setUp() */ public function testBodyStructure() { - $reflection = new ReflectionClass(EmptyClass::class); - $magicIsset = new MagicUnset($reflection, $this->initializer, $this->initMethod, $this->publicProperties); - - $this->assertSame('__unset', $magicIsset->getName()); - $this->assertCount(1, $magicIsset->getParameters()); - $this->assertStringMatchesFormat( - "\$this->foo && \$this->baz('__unset', array('name' => \$name));\n\n" - . "if (isset(self::\$bar[\$name])) {\n unset(\$this->\$name);\n\n return;\n}" - . "%areturn %s;", - $magicIsset->getBody() - ); - } - - /** - * @covers \ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicUnset::__construct - */ - public function testBodyStructureWithPublicProperties() - { - $reflection = new ReflectionClass( - ClassWithTwoPublicProperties::class + $magicIsset = new MagicUnset( + new ReflectionClass(ClassWithTwoPublicProperties::class), + $this->initializer, + $this->initMethod, + $this->publicProperties, + $this->protectedProperties, + $this->privateProperties ); - $magicIsset = new MagicUnset($reflection, $this->initializer, $this->initMethod, $this->publicProperties); - $this->assertSame('__unset', $magicIsset->getName()); $this->assertCount(1, $magicIsset->getParameters()); $this->assertStringMatchesFormat( @@ -115,8 +188,14 @@ public function testBodyStructureWithPublicProperties() */ public function testBodyStructureWithOverriddenMagicGet() { - $reflection = new ReflectionClass(ClassWithMagicMethods::class); - $magicIsset = new MagicUnset($reflection, $this->initializer, $this->initMethod, $this->publicProperties); + $magicIsset = new MagicUnset( + new ReflectionClass(ClassWithMagicMethods::class), + $this->initializer, + $this->initMethod, + $this->publicProperties, + $this->protectedProperties, + $this->privateProperties + ); $this->assertSame('__unset', $magicIsset->getName()); $this->assertCount(1, $magicIsset->getParameters()); From 4c14dacd6d63ca5cece780e508cb070aac3f713f Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 13:50:36 +0100 Subject: [PATCH 121/153] Fixing wrong assertion: should `isset()` instead of directly returning the value --- .../LazyLoadingGhost/MethodGenerator/MagicIssetTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIssetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIssetTest.php index f46017a8b..d59d4bd8e 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIssetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIssetTest.php @@ -83,7 +83,7 @@ class MagicIssetTest extends PHPUnit_Framework_TestCase $expectedType = self::$baz[$name]; if ($object instanceof $expectedType) { - return $this->$name; + return isset($this->$name); } $class = isset($caller['class']) ? $caller['class'] : ''; From 1040ee655d13687a17a30795d976ce6836b0adee Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 13:56:33 +0100 Subject: [PATCH 122/153] Verifying that protected properties `null` and `false` values are correctly differentiated in `__isset()` --- .../LazyLoadingGhostFunctionalTest.php | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php index 38d10838f..aa1d33703 100644 --- a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php +++ b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php @@ -514,6 +514,46 @@ function ($proxy, $method, $params, & $initializer, array $properties) { $this->assertFalse($proxy2->has('privateProperty')); } + /** + * @group 159 + * @group 192 + * + * Test designed to verify that the cached logic does take into account the fact that + * proxies are different instances + */ + public function testIssetPrivateAndProtectedPropertiesDoesCheckAgainstBooleanFalse() + { + $class = ClassWithMixedPropertiesAndAccessorMethods::class; + $proxyName = $this->generateProxy($class); + + /* @var $proxy1 ClassWithMixedPropertiesAndAccessorMethods */ + $proxy1 = $proxyName::staticProxyConstructor( + function ($proxy, $method, $params, & $initializer, array $properties) use ($class) { + $initializer = null; + $properties["publicProperty"] = false; + $properties["\0*\0" . "protectedProperty"] = false; + $properties["\0" . $class . "\0" . "privateProperty"] = false; + } + ); + /* @var $proxy2 ClassWithMixedPropertiesAndAccessorMethods */ + $proxy2 = $proxyName::staticProxyConstructor( + function ($proxy, $method, $params, & $initializer, array $properties) use ($class) { + $initializer = null; + $properties["publicProperty"] = null; + $properties["\0*\0" . "protectedProperty"] = null; + $properties["\0" . $class . "\0" . "privateProperty"] = null; + } + ); + + $this->assertTrue($proxy1->has('protectedProperty')); + $this->assertTrue($proxy1->has('publicProperty')); + $this->assertTrue($proxy1->has('privateProperty')); + + $this->assertFalse($proxy2->has('protectedProperty')); + $this->assertFalse($proxy2->has('publicProperty')); + $this->assertFalse($proxy2->has('privateProperty')); + } + public function testByRefInitialization() { $proxyName = $this->generateProxy(ClassWithMixedProperties::class); From 62e6dfa23f294ac49042c0b32231e387460be8b6 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 13:59:56 +0100 Subject: [PATCH 123/153] s/__isset/__unset --- .../LazyLoadingGhost/MethodGenerator/MagicUnsetTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnsetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnsetTest.php index b165664a7..916689be8 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnsetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnsetTest.php @@ -69,7 +69,7 @@ class MagicUnsetTest extends PHPUnit_Framework_TestCase * @var string */ private $expectedCode = <<<'PHP' -$this->foo && $this->baz('__isset', array('name' => $name)); +$this->foo && $this->baz('__unset', array('name' => $name)); if (isset(self::$bar[$name])) { unset($this->$name); From bfe7d68f325dfeac93304b26b784cb1ff7346c97 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 14:03:34 +0100 Subject: [PATCH 124/153] Adjusting test to match generated code pattern, fixing minor inconsistencies in the pattern --- .../MethodGenerator/MagicUnsetTest.php | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnsetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnsetTest.php index 916689be8..056a1febf 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnsetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnsetTest.php @@ -92,12 +92,12 @@ class MagicUnsetTest extends PHPUnit_Framework_TestCase $class = isset($caller['class']) ? $caller['class'] : ''; - if ($class === $expectedType || is_subclass_of($class, $expectedType)) { + if ($class === $expectedType || is_subclass_of($class, $expectedType) || $class === 'ReflectionProperty') { unset($this->$name); return; } -} else { +} elseif (isset(self::$tab[$name])) { // check private property access via same class $callers = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 2); $caller = isset($callers[1]) ? $callers[1] : []; @@ -175,12 +175,7 @@ public function testBodyStructure() $this->assertSame('__unset', $magicIsset->getName()); $this->assertCount(1, $magicIsset->getParameters()); - $this->assertStringMatchesFormat( - "\$this->foo && \$this->baz('__unset', array('name' => \$name));\n\n" - . "if (isset(self::\$bar[\$name])) {\n unset(\$this->\$name);\n\n return;\n}" - . "%areturn %s;", - $magicIsset->getBody() - ); + $this->assertStringMatchesFormat($this->expectedCode, $magicIsset->getBody()); } /** @@ -199,11 +194,10 @@ public function testBodyStructureWithOverriddenMagicGet() $this->assertSame('__unset', $magicIsset->getName()); $this->assertCount(1, $magicIsset->getParameters()); - $this->assertSame( - "\$this->foo && \$this->baz('__unset', array('name' => \$name));\n\n" - . "if (isset(self::\$bar[\$name])) {\n unset(\$this->\$name);\n\n return;\n}\n\n" - . "return parent::__unset(\$name);", - $magicIsset->getBody() - ); + + $body = $magicIsset->getBody(); + + $this->assertStringMatchesFormat($this->expectedCode, $body); + $this->assertStringMatchesFormat('%Areturn parent::__unset($name);', $body); } } From a9b69942ccaf4d448bce91c5a676e027de4e03ea Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 14:04:25 +0100 Subject: [PATCH 125/153] Caching accessors used to `unset()` properties --- .../MethodGenerator/MagicUnset.php | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php index c7085937e..8a715bd08 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicUnset.php @@ -80,30 +80,44 @@ public function __construct( $class = isset($caller['class']) ? $caller['class'] : ''; - if ($class === $expectedType || is_subclass_of($class, $expectedType)) { + if ($class === $expectedType || is_subclass_of($class, $expectedType) || $class === 'ReflectionProperty') { unset($this->$name); return; } -} else { +} 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])) { - return \Closure::bind(function () use ($name) { - unset($this->$name); - }, $this, $class)->__invoke($this, $name); + $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 ($class === 'ReflectionProperty') { - return \Closure::bind(function () use ($name) { - unset($this->$name); - }, $this, key(self::$%s[$name]))->__invoke($this, $name); + 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); } } + PHP; $callParent = sprintf( From 6283ac0d1845d3a7cd2b7e7dd21d4f4d721bd628 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 14:06:42 +0100 Subject: [PATCH 126/153] Fixing getter: should be an issetter instead --- .../LazyLoadingGhost/MethodGenerator/MagicIsset.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php index b91cdd2fe..67abd2350 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicIsset.php @@ -71,7 +71,7 @@ public function __construct( $expectedType = self::$%s[$name]; if ($object instanceof $expectedType) { - return $this->$name; + return isset($this->$name); } $class = isset($caller['class']) ? $caller['class'] : ''; From 2f04e22827fdfd0a9e8965cef2769bb02fafb478 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 14:09:34 +0100 Subject: [PATCH 127/153] Fixed docblock parameter --- .../ClassWithMixedPropertiesAndAccessorMethods.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ProxyManagerTestAsset/ClassWithMixedPropertiesAndAccessorMethods.php b/tests/ProxyManagerTestAsset/ClassWithMixedPropertiesAndAccessorMethods.php index 4aa7b6252..e78f90f11 100644 --- a/tests/ProxyManagerTestAsset/ClassWithMixedPropertiesAndAccessorMethods.php +++ b/tests/ProxyManagerTestAsset/ClassWithMixedPropertiesAndAccessorMethods.php @@ -63,7 +63,7 @@ public function get($name) /** * @param string $name - * @param mixed $name + * @param mixed $value * * @return void */ From 15fd51f54cf0bb30ce5699c676d1dcd44c0a28d7 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 15:01:26 +0100 Subject: [PATCH 128/153] Removing tests for unused class --- .../MethodGenerator/ConstructorTest.php | 72 ------------------- 1 file changed, 72 deletions(-) delete mode 100644 tests/ProxyManagerTest/ProxyGenerator/LazyLoading/MethodGenerator/ConstructorTest.php diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoading/MethodGenerator/ConstructorTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoading/MethodGenerator/ConstructorTest.php deleted file mode 100644 index 34b546192..000000000 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoading/MethodGenerator/ConstructorTest.php +++ /dev/null @@ -1,72 +0,0 @@ - - * @license MIT - * - * @group Coverage - */ -class ConstructorTest extends PHPUnit_Framework_TestCase -{ - /** - * @covers \ProxyManager\ProxyGenerator\LazyLoading\MethodGenerator\Constructor::__construct - */ - public function testBodyStructure() - { - $initializer = $this->getMock(PropertyGenerator::class); - $reflection = new ReflectionClass( - ClassWithTwoPublicProperties::class - ); - - $initializer->expects($this->any())->method('getName')->will($this->returnValue('foo')); - - $constructor = new Constructor($reflection, $initializer); - - $this->assertSame('__construct', $constructor->getName()); - $this->assertCount(1, $constructor->getParameters()); - $this->assertSame("unset(\$this->bar, \$this->baz);\n\n\$this->foo = \$initializer;", $constructor->getBody()); - } - - /** - * @covers \ProxyManager\ProxyGenerator\LazyLoading\MethodGenerator\Constructor::__construct - */ - public function testBodyStructureWithoutPublicProperties() - { - $initializer = $this->getMock(PropertyGenerator::class); - - $initializer->expects($this->any())->method('getName')->will($this->returnValue('foo')); - - $constructor = new Constructor(new ReflectionClass(EmptyClass::class), $initializer); - - $this->assertSame('__construct', $constructor->getName()); - $this->assertCount(1, $constructor->getParameters()); - $this->assertSame("\$this->foo = \$initializer;", $constructor->getBody()); - } -} From f47898b227a33ae2c7b60c971b900c1e36028662 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 15:01:42 +0100 Subject: [PATCH 129/153] Removing unused class --- .../MethodGenerator/Constructor.php | 61 ------------------- 1 file changed, 61 deletions(-) delete mode 100644 src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/Constructor.php diff --git a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/Constructor.php b/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/Constructor.php deleted file mode 100644 index c5145ed32..000000000 --- a/src/ProxyManager/ProxyGenerator/LazyLoading/MethodGenerator/Constructor.php +++ /dev/null @@ -1,61 +0,0 @@ - - * @license MIT - */ -class Constructor extends MethodGenerator -{ - /** - * Constructor - * - * @param ReflectionClass $originalClass - * @param PropertyGenerator $initializerProperty - */ - public function __construct(ReflectionClass $originalClass, PropertyGenerator $initializerProperty) - { - parent::__construct('__construct'); - - $this->setParameter(new ParameterGenerator('initializer')); - - /* @var $publicProperties \ReflectionProperty[] */ - $publicProperties = $originalClass->getProperties(ReflectionProperty::IS_PUBLIC); - $unsetProperties = []; - - foreach ($publicProperties as $publicProperty) { - $unsetProperties[] = '$this->' . $publicProperty->getName(); - } - - $this->setDocblock("@override constructor for lazy initialization\n\n@param \\Closure|null \$initializer"); - $this->setBody( - ($unsetProperties ? 'unset(' . implode(', ', $unsetProperties) . ");\n\n" : '') - . '$this->' . $initializerProperty->getName() . ' = $initializer;' - ); - } -} From 3be82a1a17f49a7f05bd69e6765bc46570d3f65c Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 15:03:15 +0100 Subject: [PATCH 130/153] `$allProperties` is not needed --- src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php index bf155e3af..7ed5b447e 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php @@ -68,7 +68,6 @@ public function generate(ReflectionClass $originalClass, ClassGenerator $classGe $interfaces = [GhostObjectInterface::class]; $publicProperties = new PublicPropertiesMap($originalClass); - $allProperties = new PropertiesMap($originalClass); $privateProperties = new PrivatePropertiesMap($originalClass); $protectedProperties = new ProtectedPropertiesMap($originalClass); $publicPropsDefaults = new PublicPropertiesDefaults($originalClass); @@ -84,7 +83,6 @@ public function generate(ReflectionClass $originalClass, ClassGenerator $classGe $classGenerator->addPropertyFromGenerator($initializationTracker = new InitializationTracker()); $classGenerator->addPropertyFromGenerator($publicProperties); $classGenerator->addPropertyFromGenerator($publicPropsDefaults); - $classGenerator->addPropertyFromGenerator($allProperties); $classGenerator->addPropertyFromGenerator($privateProperties); $classGenerator->addPropertyFromGenerator($protectedProperties); From c173fd1d9d0c0d02c6fadd403be9c631910ee7fb Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 15:03:29 +0100 Subject: [PATCH 131/153] Removing unused class --- .../PropertyGenerator/PropertiesMap.php | 132 ------------------ 1 file changed, 132 deletions(-) delete mode 100644 src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PropertiesMap.php diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PropertiesMap.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PropertiesMap.php deleted file mode 100644 index beb18956f..000000000 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PropertiesMap.php +++ /dev/null @@ -1,132 +0,0 @@ - - * @license MIT - */ -class PropertiesMap extends PropertyGenerator -{ - const KEY_VISIBILITY = 'visibility'; - const KEY_DEFAULT_VALUE = 'defaultValue'; - /** - * Constructor - */ - public function __construct(\ReflectionClass $originalClass) - { - parent::__construct( - UniqueIdentifierGenerator::getIdentifier('propertiesMap') - ); - - $this->setVisibility(self::VISIBILITY_PRIVATE); - $this->setStatic(true); - $this->setDocblock( - '@var array[][] visibility and default value of defined properties, indexed by class name and property name' - ); - $this->setDefaultValue($this->getMap($originalClass)); - } - - /** - * @param \ReflectionClass $originalClass - * - * @return int[][]|mixed[][] - */ - private function getMap(\ReflectionClass $originalClass) - { - $map = []; - - foreach ($this->getProperties($originalClass) as $property) { - $class = & $map[$property->getDeclaringClass()->getName()]; - - $class[$property->getName()] = [ - 'visibility' => $this->getPropertyVisibility($property), - 'defaultValue' => $this->getPropertyDefaultValue($property), - ]; - } - - return $map; - } - - /** - * @param \ReflectionProperty $property - * - * @return int - */ - private function getPropertyVisibility(\ReflectionProperty $property) - { - if ($property->isPrivate()) { - return \ReflectionProperty::IS_PRIVATE; - } - - if ($property->isProtected()) { - return \ReflectionProperty::IS_PROTECTED; - } - - return \ReflectionProperty::IS_PUBLIC; - } - - /** - * @param \ReflectionProperty $property - * - * @return mixed - */ - private function getPropertyDefaultValue(\ReflectionProperty $property) - { - $propertyName = $property->getName(); - $defaultValues = $property->getDeclaringClass()->getDefaultProperties(); - - if (! isset($defaultValues[$propertyName])) { - return null; - } - - return $defaultValues[$propertyName]; - } - - /** - * @param \ReflectionClass $originalClass - * - * @return \ReflectionProperty[] - */ - private function getProperties(\ReflectionClass $originalClass) - { - $class = $originalClass; - $properties = []; - - do { - $properties = array_merge( - $properties, - array_values(array_filter( - $class->getProperties(), - function (\ReflectionProperty $property) use ($class) { - return $property->getDeclaringClass()->getName() === $class->getName() - && ! $property->isStatic(); - } - )) - ); - } while ($class = $class->getParentClass()); - - return $properties; - } -} From 2c9cd43c3ea6c19bb9c4e34ada09225eca1f4825 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 15:05:36 +0100 Subject: [PATCH 132/153] Skip static properties in the `Properties` utility --- src/ProxyManager/ProxyGenerator/Util/Properties.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ProxyManager/ProxyGenerator/Util/Properties.php b/src/ProxyManager/ProxyGenerator/Util/Properties.php index 021202fe2..95496b542 100644 --- a/src/ProxyManager/ProxyGenerator/Util/Properties.php +++ b/src/ProxyManager/ProxyGenerator/Util/Properties.php @@ -59,7 +59,8 @@ public static function fromReflectionClass(ReflectionClass $reflection) array_values(array_filter( $class->getProperties(), function (ReflectionProperty $property) use ($class) { - return $class->getName() === $property->getDeclaringClass()->getName(); + return $class->getName() === $property->getDeclaringClass()->getName() + && ! $property->isStatic(); } )) ); From 6292bf0d6b6d2fff3a8fad61e2ca48082f2d17f8 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 15:06:28 +0100 Subject: [PATCH 133/153] Avoiding code duplication by using an utility class --- .../PrivatePropertiesMap.php | 30 ++----------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMap.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMap.php index 38669731f..ab01d1e79 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMap.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMap.php @@ -19,6 +19,7 @@ namespace ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator; use ProxyManager\Generator\Util\UniqueIdentifierGenerator; +use ProxyManager\ProxyGenerator\Util\Properties; use Zend\Code\Generator\PropertyGenerator; /** @@ -57,7 +58,7 @@ private function getMap(\ReflectionClass $originalClass) { $map = []; - foreach ($this->getProperties($originalClass) as $property) { + foreach (Properties::fromReflectionClass($originalClass)->getPrivateProperties() as $property) { $propertyKey = & $map[$property->getName()]; $propertyKey[$property->getDeclaringClass()->getName()] = true; @@ -65,31 +66,4 @@ private function getMap(\ReflectionClass $originalClass) return $map; } - - /** - * @param \ReflectionClass $originalClass - * - * @return \ReflectionProperty[] - */ - private function getProperties(\ReflectionClass $originalClass) - { - $class = $originalClass; - $properties = []; - - do { - $properties = array_merge( - $properties, - array_values(array_filter( - $class->getProperties(), - function (\ReflectionProperty $property) use ($class) { - return $property->getDeclaringClass()->getName() === $class->getName() - && ! $property->isStatic() - && $property->isPrivate(); - } - )) - ); - } while ($class = $class->getParentClass()); - - return $properties; - } } From 0cc3861fcdfc01b547c0b6d7fa0036054bc6db58 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 15:07:11 +0100 Subject: [PATCH 134/153] Importing used class --- .../PropertyGenerator/PrivatePropertiesMap.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMap.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMap.php index ab01d1e79..4b9780ab4 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMap.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMap.php @@ -20,6 +20,7 @@ use ProxyManager\Generator\Util\UniqueIdentifierGenerator; use ProxyManager\ProxyGenerator\Util\Properties; +use ReflectionClass; use Zend\Code\Generator\PropertyGenerator; /** @@ -35,7 +36,7 @@ class PrivatePropertiesMap extends PropertyGenerator /** * Constructor */ - public function __construct(\ReflectionClass $originalClass) + public function __construct(ReflectionClass $originalClass) { parent::__construct( UniqueIdentifierGenerator::getIdentifier('privateProperties') @@ -50,11 +51,11 @@ public function __construct(\ReflectionClass $originalClass) } /** - * @param \ReflectionClass $originalClass + * @param ReflectionClass $originalClass * * @return int[][]|mixed[][] */ - private function getMap(\ReflectionClass $originalClass) + private function getMap(ReflectionClass $originalClass) { $map = []; From d36425fa35fdb54efcadfc1d60b20508d71951f7 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 15:08:11 +0100 Subject: [PATCH 135/153] Avoiding code duplication by using an utility class --- .../ProtectedPropertiesMap.php | 30 ++----------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMap.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMap.php index d853d7922..b369f9121 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMap.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMap.php @@ -19,6 +19,7 @@ namespace ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator; use ProxyManager\Generator\Util\UniqueIdentifierGenerator; +use ProxyManager\ProxyGenerator\Util\Properties; use Zend\Code\Generator\PropertyGenerator; /** @@ -57,37 +58,10 @@ private function getMap(\ReflectionClass $originalClass) { $map = []; - foreach ($this->getProperties($originalClass) as $property) { + foreach (Properties::fromReflectionClass($originalClass)->getProtectedProperties() as $property) { $map[$property->getName()] = $property->getDeclaringClass()->getName(); } return $map; } - - /** - * @param \ReflectionClass $originalClass - * - * @return \ReflectionProperty[] - */ - private function getProperties(\ReflectionClass $originalClass) - { - $class = $originalClass; - $properties = []; - - do { - $properties = array_merge( - $properties, - array_values(array_filter( - $class->getProperties(), - function (\ReflectionProperty $property) use ($class) { - return $property->getDeclaringClass()->getName() === $class->getName() - && ! $property->isStatic() - && $property->isProtected(); - } - )) - ); - } while ($class = $class->getParentClass()); - - return $properties; - } } From 66cb4ae08efc7aa1413e14f993a66423b319a877 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 15:14:03 +0100 Subject: [PATCH 136/153] Public properties default values map is not needed at all --- src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php index 7ed5b447e..b0a023e8d 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhostGenerator.php @@ -70,7 +70,6 @@ public function generate(ReflectionClass $originalClass, ClassGenerator $classGe $publicProperties = new PublicPropertiesMap($originalClass); $privateProperties = new PrivatePropertiesMap($originalClass); $protectedProperties = new ProtectedPropertiesMap($originalClass); - $publicPropsDefaults = new PublicPropertiesDefaults($originalClass); if ($originalClass->isInterface()) { $interfaces[] = $originalClass->getName(); @@ -82,7 +81,6 @@ public function generate(ReflectionClass $originalClass, ClassGenerator $classGe $classGenerator->addPropertyFromGenerator($initializer = new InitializerProperty()); $classGenerator->addPropertyFromGenerator($initializationTracker = new InitializationTracker()); $classGenerator->addPropertyFromGenerator($publicProperties); - $classGenerator->addPropertyFromGenerator($publicPropsDefaults); $classGenerator->addPropertyFromGenerator($privateProperties); $classGenerator->addPropertyFromGenerator($protectedProperties); From 9e1b75b86d7b67af9dd4a7c3fb7c5be3698f2407 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 15:15:07 +0100 Subject: [PATCH 137/153] Removing tests for unused class --- .../PublicPropertiesDefaultsTest.php | 75 ------------------- 1 file changed, 75 deletions(-) delete mode 100644 tests/ProxyManagerTest/ProxyGenerator/PropertyGenerator/PublicPropertiesDefaultsTest.php diff --git a/tests/ProxyManagerTest/ProxyGenerator/PropertyGenerator/PublicPropertiesDefaultsTest.php b/tests/ProxyManagerTest/ProxyGenerator/PropertyGenerator/PublicPropertiesDefaultsTest.php deleted file mode 100644 index 761b1898d..000000000 --- a/tests/ProxyManagerTest/ProxyGenerator/PropertyGenerator/PublicPropertiesDefaultsTest.php +++ /dev/null @@ -1,75 +0,0 @@ - - * @license MIT - * - * @covers \ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesDefaults - * @group Coverage - */ -class PublicPropertiesDefaultsTest extends PHPUnit_Framework_TestCase -{ - public function testEmptyClass() - { - $publicProperties = new PublicPropertiesDefaults(new ReflectionClass(EmptyClass::class)); - - $this->assertInternalType('array', $publicProperties->getDefaultValue()->getValue()); - $this->assertEmpty($publicProperties->getDefaultValue()->getValue()); - $this->assertTrue($publicProperties->isStatic()); - $this->assertSame(PublicPropertiesDefaults::VISIBILITY_PRIVATE, $publicProperties->getVisibility()); - } - - public function testClassWithPublicProperties() - { - $publicProperties = new PublicPropertiesDefaults( - new ReflectionClass(ClassWithPublicProperties::class) - ); - - $this->assertInternalType('array', $publicProperties->getDefaultValue()->getValue()); - $this->assertCount(10, $publicProperties->getDefaultValue()->getValue()); - $this->assertTrue($publicProperties->isStatic()); - $this->assertSame(PublicPropertiesDefaults::VISIBILITY_PRIVATE, $publicProperties->getVisibility()); - } - - public function testBaseClass() - { - $publicProperties = new PublicPropertiesDefaults( - new ReflectionClass(BaseClass::class) - ); - - $this->assertInternalType('array', $publicProperties->getDefaultValue()->getValue()); - $this->assertSame( - ['publicProperty' => 'publicPropertyDefault'], - $publicProperties->getDefaultValue()->getValue() - ); - $this->assertTrue($publicProperties->isStatic()); - $this->assertSame(PublicPropertiesDefaults::VISIBILITY_PRIVATE, $publicProperties->getVisibility()); - } -} From ad277dd6049393a70720eb8ad4d134245c5bf6bd Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 15:15:28 +0100 Subject: [PATCH 138/153] Removing unused class --- .../PublicPropertiesDefaults.php | 58 ------------------- 1 file changed, 58 deletions(-) delete mode 100644 src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesDefaults.php diff --git a/src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesDefaults.php b/src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesDefaults.php deleted file mode 100644 index 2aa395ce7..000000000 --- a/src/ProxyManager/ProxyGenerator/PropertyGenerator/PublicPropertiesDefaults.php +++ /dev/null @@ -1,58 +0,0 @@ - - * @license MIT - */ -class PublicPropertiesDefaults extends PropertyGenerator -{ - /** - * @var bool[] - */ - private $publicProperties = []; - - /** - * @param \ReflectionClass $originalClass - */ - public function __construct(ReflectionClass $originalClass) - { - parent::__construct(UniqueIdentifierGenerator::getIdentifier('publicPropertiesDefaults')); - - $defaults = $originalClass->getDefaultProperties(); - - foreach ($originalClass->getProperties(ReflectionProperty::IS_PUBLIC) as $publicProperty) { - $name = $publicProperty->getName(); - $this->publicProperties[$name] = $defaults[$name]; - } - - $this->setDefaultValue($this->publicProperties); - $this->setVisibility(self::VISIBILITY_PRIVATE); - $this->setStatic(true); - $this->setDocblock('@var mixed[] map of default property values of the parent class'); - } -} From 8e8da0a33b1df59d7999b5bff2e50e9fc330ba89 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 15:37:02 +0100 Subject: [PATCH 139/153] Basic tests for `ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\ProtectedPropertiesMap` --- .../ProtectedPropertiesMapTest.php | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMapTest.php diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMapTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMapTest.php new file mode 100644 index 000000000..5b4e46d84 --- /dev/null +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMapTest.php @@ -0,0 +1,59 @@ + + * @license MIT + * + * @covers \ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\ProtectedPropertiesMap + * @group Coverage + */ +class ProtectedPropertiesMapTest extends AbstractUniquePropertyNameTest +{ + /** + * {@inheritDoc} + */ + protected function createProperty() + { + return new ProtectedPropertiesMap(new ReflectionClass(ClassWithMixedProperties::class)); + } + + public function testExtractsProtectedProperties() + { + $map = new ProtectedPropertiesMap(new ReflectionClass(ClassWithMixedProperties::class)); + + $this->assertSame( + [ + 'protectedProperty0' => ClassWithMixedProperties::class, + 'protectedProperty1' => ClassWithMixedProperties::class, + 'protectedProperty2' => ClassWithMixedProperties::class, + ], + $map->getDefaultValue()->getValue() + ); + } +} From 930ccf509f885eca6a9fe6a6dcbdf7ff4e562369 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 15:37:56 +0100 Subject: [PATCH 140/153] `ProtectedPropertiesMap` should ignore abstract methods --- .../PropertyGenerator/ProtectedPropertiesMapTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMapTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMapTest.php index 5b4e46d84..46f1d3d53 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMapTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMapTest.php @@ -21,6 +21,7 @@ use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\InitializerProperty; use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\ProtectedPropertiesMap; use ProxyManagerTest\ProxyGenerator\PropertyGenerator\AbstractUniquePropertyNameTest; +use ProxyManagerTestAsset\ClassWithAbstractProtectedMethod; use ProxyManagerTestAsset\ClassWithMixedProperties; use ReflectionClass; @@ -56,4 +57,11 @@ public function testExtractsProtectedProperties() $map->getDefaultValue()->getValue() ); } + + public function testSkipsAbstractProtectedMethods() + { + $map = new ProtectedPropertiesMap(new ReflectionClass(ClassWithAbstractProtectedMethod::class)); + + $this->assertSame([], $map->getDefaultValue()->getValue()); + } } From 3d8150f26888f632c014120b128a78558b79ac14 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 15:39:25 +0100 Subject: [PATCH 141/153] Assertions on `ProtectedPropertiesMap` visibility --- .../PropertyGenerator/ProtectedPropertiesMapTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMapTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMapTest.php index 46f1d3d53..4ffd66519 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMapTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMapTest.php @@ -64,4 +64,12 @@ public function testSkipsAbstractProtectedMethods() $this->assertSame([], $map->getDefaultValue()->getValue()); } + + public function testIsStaticPrivate() + { + $map = $this->createProperty(); + + $this->assertTrue($map->isStatic()); + $this->assertSame(ProtectedPropertiesMap::VISIBILITY_PRIVATE, $map->getVisibility()); + } } From 992cb71a9c5857d0aff045e7a15ee7e205b3a0db Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 15:40:01 +0100 Subject: [PATCH 142/153] Fixed class docblock description --- .../PropertyGenerator/ProtectedPropertiesMap.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMap.php b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMap.php index b369f9121..09b7387f7 100644 --- a/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMap.php +++ b/src/ProxyManager/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/ProtectedPropertiesMap.php @@ -23,7 +23,7 @@ use Zend\Code\Generator\PropertyGenerator; /** - * Property that contains the initializer for a lazy object + * Property that contains the protected instance lazy-loadable properties of an object * * @author Marco Pivetta * @license MIT From 70f98f39f1e89ec88a793d9399d73df1c3471ace Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 15:43:45 +0100 Subject: [PATCH 143/153] Basic tests for `ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PrivatePropertiesMap` --- .../PrivatePropertiesMapTest.php | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMapTest.php diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMapTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMapTest.php new file mode 100644 index 000000000..d4d48898d --- /dev/null +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMapTest.php @@ -0,0 +1,82 @@ + + * @license MIT + * + * @covers \ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PrivatePropertiesMapTest + * @group Coverage + */ +class PrivatePropertiesMapTest extends AbstractUniquePropertyNameTest +{ + /** + * {@inheritDoc} + */ + protected function createProperty() + { + return new PrivatePropertiesMap(new ReflectionClass(ClassWithMixedProperties::class)); + } + + public function testExtractsProtectedProperties() + { + $map = new PrivatePropertiesMap(new ReflectionClass(ClassWithMixedProperties::class)); + + $this->assertSame( + [ + 'privateProperty0' => [ + ClassWithMixedProperties::class => true, + ], + 'privateProperty1' => [ + ClassWithMixedProperties::class => true, + ], + 'privateProperty2' => [ + ClassWithMixedProperties::class => true, + ], + ], + $map->getDefaultValue()->getValue() + ); + } + + public function testSkipsAbstractProtectedMethods() + { + $map = new PrivatePropertiesMap(new ReflectionClass(ClassWithAbstractProtectedMethod::class)); + + $this->assertSame([], $map->getDefaultValue()->getValue()); + } + + public function testIsStaticPrivate() + { + $map = $this->createProperty(); + + $this->assertTrue($map->isStatic()); + $this->assertSame(ProtectedPropertiesMap::VISIBILITY_PRIVATE, $map->getVisibility()); + } +} From 8e7aa785d98d3aad0be9fa5e2a4efd80df790ea6 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 15:45:59 +0100 Subject: [PATCH 144/153] Wrong coverage annotation fix --- .../PropertyGenerator/PrivatePropertiesMapTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMapTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMapTest.php index d4d48898d..cf3d1efb5 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMapTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/PropertyGenerator/PrivatePropertiesMapTest.php @@ -27,12 +27,12 @@ use ReflectionClass; /** - * Tests for {@see \ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PrivatePropertiesMapTest} + * Tests for {@see \ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PrivatePropertiesMap} * * @author Marco Pivetta * @license MIT * - * @covers \ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PrivatePropertiesMapTest + * @covers \ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PrivatePropertiesMap * @group Coverage */ class PrivatePropertiesMapTest extends AbstractUniquePropertyNameTest From b8e1a5616c5eec645350fdcea18e0165162109d9 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 15:51:11 +0100 Subject: [PATCH 145/153] Basic tests for `ProxyManager\ProxyGenerator\Util\Properties` --- .../ProxyGenerator/Util/PropertiesTest.php | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php diff --git a/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php b/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php new file mode 100644 index 000000000..004a8c6be --- /dev/null +++ b/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php @@ -0,0 +1,65 @@ + + * @license MIT + * + * @covers \ProxyManager\ProxyGenerator\Util\Properties + * @group Coverage + */ +class PropertiesTest extends PHPUnit_Framework_TestCase +{ + public function testGetPublicProperties() + { + $properties = Properties::fromReflectionClass(new ReflectionClass(ClassWithMixedProperties::class)); + $publicProperties = $properties->getPublicProperties(); + + $this->assertCount(3, $publicProperties); + $this->assertInstanceOf(ReflectionProperty::class, $publicProperties['publicProperty0']); + $this->assertInstanceOf(ReflectionProperty::class, $publicProperties['publicProperty1']); + $this->assertInstanceOf(ReflectionProperty::class, $publicProperties['publicProperty2']); + } + + public function testGetPublicPropertiesSkipsAbstractMethods() + { + $properties = Properties::fromReflectionClass(new ReflectionClass(ClassWithAbstractPublicMethod::class)); + + $this->assertEmpty($properties->getPublicProperties()); + } +} From 3d7100ca1ca1fb6e39c367594b251287fe4f4834 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 15:52:58 +0100 Subject: [PATCH 146/153] Basic tests for `ProxyManager\ProxyGenerator\Util\Properties#getProtectedProperties()` --- .../ProxyGenerator/Util/PropertiesTest.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php b/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php index 004a8c6be..fdcd1c0de 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php @@ -62,4 +62,17 @@ public function testGetPublicPropertiesSkipsAbstractMethods() $this->assertEmpty($properties->getPublicProperties()); } + + public function testGetProtectedProperties() + { + $properties = Properties::fromReflectionClass(new ReflectionClass(ClassWithMixedProperties::class)); + + $protectedProperties = $properties->getProtectedProperties(); + + $this->assertCount(3, $protectedProperties); + + $this->assertInstanceOf(ReflectionProperty::class, $protectedProperties["\0*\0protectedProperty0"]); + $this->assertInstanceOf(ReflectionProperty::class, $protectedProperties["\0*\0protectedProperty1"]); + $this->assertInstanceOf(ReflectionProperty::class, $protectedProperties["\0*\0protectedProperty2"]); + } } From a0bcea36c8c69d37110c8e3ed43d419ce14501d3 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 15:53:39 +0100 Subject: [PATCH 147/153] `ProxyManager\ProxyGenerator\Util\Properties#getProtectedProperties()` skips protected methods --- .../ProxyGenerator/Util/PropertiesTest.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php b/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php index fdcd1c0de..398f7d28f 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php @@ -75,4 +75,11 @@ public function testGetProtectedProperties() $this->assertInstanceOf(ReflectionProperty::class, $protectedProperties["\0*\0protectedProperty1"]); $this->assertInstanceOf(ReflectionProperty::class, $protectedProperties["\0*\0protectedProperty2"]); } + + public function testGetProtectedPropertiesSkipsAbstractMethods() + { + $properties = Properties::fromReflectionClass(new ReflectionClass(ClassWithAbstractProtectedMethod::class)); + + $this->assertEmpty($properties->getProtectedProperties()); + } } From ba17e210a4599f5e1c158aeb2359ba33c6644fc6 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 15:55:41 +0100 Subject: [PATCH 148/153] Basic tests for `ProxyManager\ProxyGenerator\Util\Properties#getPrivateProperties()` --- .../ProxyGenerator/Util/PropertiesTest.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php b/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php index 398f7d28f..7b4107996 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php @@ -82,4 +82,19 @@ public function testGetProtectedPropertiesSkipsAbstractMethods() $this->assertEmpty($properties->getProtectedProperties()); } + + public function testGetPrivateProperties() + { + $properties = Properties::fromReflectionClass(new ReflectionClass(ClassWithMixedProperties::class)); + + $privateProperties = $properties->getPrivateProperties(); + + $this->assertCount(3, $privateProperties); + + $prefix = "\0" . ClassWithMixedProperties::class . "\0"; + + $this->assertInstanceOf(ReflectionProperty::class, $privateProperties[$prefix . 'privateProperty0']); + $this->assertInstanceOf(ReflectionProperty::class, $privateProperties[$prefix . 'privateProperty1']); + $this->assertInstanceOf(ReflectionProperty::class, $privateProperties[$prefix . 'privateProperty2']); + } } From e1fc98df9d64fb8317f3c0e610f7013afcd8e95e Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 15:58:27 +0100 Subject: [PATCH 149/153] `ProxyManager\ProxyGenerator\Util\Properties#getPrivateProperties()` retrieves also properties with the same and from different classes --- .../ProxyGenerator/Util/PropertiesTest.php | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php b/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php index 7b4107996..1909be91f 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php @@ -25,8 +25,10 @@ use ProxyManagerTestAsset\ClassWithAbstractMagicMethods; use ProxyManagerTestAsset\ClassWithAbstractProtectedMethod; use ProxyManagerTestAsset\ClassWithAbstractPublicMethod; +use ProxyManagerTestAsset\ClassWithCollidingPrivateInheritedProperties; use ProxyManagerTestAsset\ClassWithMagicMethods; use ProxyManagerTestAsset\ClassWithMixedProperties; +use ProxyManagerTestAsset\ClassWithPrivateProperties; use ProxyManagerTestAsset\EmptyClass; use ProxyManagerTestAsset\HydratedObject; use ProxyManagerTestAsset\LazyLoadingMock; @@ -97,4 +99,32 @@ public function testGetPrivateProperties() $this->assertInstanceOf(ReflectionProperty::class, $privateProperties[$prefix . 'privateProperty1']); $this->assertInstanceOf(ReflectionProperty::class, $privateProperties[$prefix . 'privateProperty2']); } + + public function testGetPrivatePropertiesFromInheritance() + { + $properties = Properties::fromReflectionClass( + new ReflectionClass(ClassWithCollidingPrivateInheritedProperties::class) + ); + + $privateProperties = $properties->getPrivateProperties(); + + $this->assertCount(11, $privateProperties); + + $prefix = "\0" . ClassWithCollidingPrivateInheritedProperties::class . "\0"; + + $this->assertInstanceOf(ReflectionProperty::class, $privateProperties[$prefix . 'property0']); + + $prefix = "\0" . ClassWithPrivateProperties::class . "\0"; + + $this->assertInstanceOf(ReflectionProperty::class, $privateProperties[$prefix . 'property0']); + $this->assertInstanceOf(ReflectionProperty::class, $privateProperties[$prefix . 'property1']); + $this->assertInstanceOf(ReflectionProperty::class, $privateProperties[$prefix . 'property2']); + $this->assertInstanceOf(ReflectionProperty::class, $privateProperties[$prefix . 'property3']); + $this->assertInstanceOf(ReflectionProperty::class, $privateProperties[$prefix . 'property4']); + $this->assertInstanceOf(ReflectionProperty::class, $privateProperties[$prefix . 'property5']); + $this->assertInstanceOf(ReflectionProperty::class, $privateProperties[$prefix . 'property6']); + $this->assertInstanceOf(ReflectionProperty::class, $privateProperties[$prefix . 'property7']); + $this->assertInstanceOf(ReflectionProperty::class, $privateProperties[$prefix . 'property8']); + $this->assertInstanceOf(ReflectionProperty::class, $privateProperties[$prefix . 'property9']); + } } From 3717ea2a856d201d2ba21201c650785ba94ca97c Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 15:59:59 +0100 Subject: [PATCH 150/153] `ProxyManager\ProxyGenerator\Util\Properties#getAccessibleProperties()` basic test --- .../ProxyGenerator/Util/PropertiesTest.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php b/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php index 1909be91f..70bc538fb 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php @@ -127,4 +127,18 @@ public function testGetPrivatePropertiesFromInheritance() $this->assertInstanceOf(ReflectionProperty::class, $privateProperties[$prefix . 'property8']); $this->assertInstanceOf(ReflectionProperty::class, $privateProperties[$prefix . 'property9']); } + + public function testGetAccessibleMethods() + { + $properties = Properties::fromReflectionClass(new ReflectionClass(ClassWithMixedProperties::class)); + $accessibleProperties = $properties->getAccessibleProperties(); + + $this->assertCount(6, $accessibleProperties); + $this->assertInstanceOf(ReflectionProperty::class, $accessibleProperties['publicProperty0']); + $this->assertInstanceOf(ReflectionProperty::class, $accessibleProperties['publicProperty1']); + $this->assertInstanceOf(ReflectionProperty::class, $accessibleProperties['publicProperty2']); + $this->assertInstanceOf(ReflectionProperty::class, $accessibleProperties["\0*\0protectedProperty0"]); + $this->assertInstanceOf(ReflectionProperty::class, $accessibleProperties["\0*\0protectedProperty1"]); + $this->assertInstanceOf(ReflectionProperty::class, $accessibleProperties["\0*\0protectedProperty2"]); + } } From d14bdfb0685eca5e0cda83b3ef15535f6f343923 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 16:06:06 +0100 Subject: [PATCH 151/153] `ProxyManager\ProxyGenerator\Util\Properties#getGroupedPrivateProperties()` basic test --- .../ProxyGenerator/Util/PropertiesTest.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php b/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php index 70bc538fb..620d99d55 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php @@ -141,4 +141,21 @@ public function testGetAccessibleMethods() $this->assertInstanceOf(ReflectionProperty::class, $accessibleProperties["\0*\0protectedProperty1"]); $this->assertInstanceOf(ReflectionProperty::class, $accessibleProperties["\0*\0protectedProperty2"]); } + + public function testGetGroupedPrivateProperties() + { + $properties = Properties::fromReflectionClass(new ReflectionClass(ClassWithMixedProperties::class)); + $groupedPrivate = $properties->getGroupedPrivateProperties(); + + $this->assertCount(1, $groupedPrivate); + + $group = $groupedPrivate[ClassWithMixedProperties::class]; + + $this->assertCount(3, $group); + + $this->assertInstanceOf(ReflectionProperty::class, $group['privateProperty0']); + $this->assertInstanceOf(ReflectionProperty::class, $group['privateProperty1']); + $this->assertInstanceOf(ReflectionProperty::class, $group['privateProperty2']); + + } } From 3f5b99a4124938c4ed7968cead5cac5feadd8741 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 16:08:23 +0100 Subject: [PATCH 152/153] `ProxyManager\ProxyGenerator\Util\Properties#getGroupedPrivateProperties()` with inheritance and property collisions --- .../ProxyGenerator/Util/PropertiesTest.php | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php b/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php index 620d99d55..dfe1a46fa 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/Util/PropertiesTest.php @@ -156,6 +156,34 @@ public function testGetGroupedPrivateProperties() $this->assertInstanceOf(ReflectionProperty::class, $group['privateProperty0']); $this->assertInstanceOf(ReflectionProperty::class, $group['privateProperty1']); $this->assertInstanceOf(ReflectionProperty::class, $group['privateProperty2']); + } + + public function testGetGroupedPrivatePropertiesWithInheritedProperties() + { + $properties = Properties::fromReflectionClass( + new ReflectionClass(ClassWithCollidingPrivateInheritedProperties::class) + ); + + $groupedPrivate = $properties->getGroupedPrivateProperties(); + $this->assertCount(2, $groupedPrivate); + + $group1 = $groupedPrivate[ClassWithCollidingPrivateInheritedProperties::class]; + $group2 = $groupedPrivate[ClassWithPrivateProperties::class]; + + $this->assertCount(1, $group1); + $this->assertCount(10, $group2); + + $this->assertInstanceOf(ReflectionProperty::class, $group1['property0']); + $this->assertInstanceOf(ReflectionProperty::class, $group2['property0']); + $this->assertInstanceOf(ReflectionProperty::class, $group2['property1']); + $this->assertInstanceOf(ReflectionProperty::class, $group2['property2']); + $this->assertInstanceOf(ReflectionProperty::class, $group2['property3']); + $this->assertInstanceOf(ReflectionProperty::class, $group2['property4']); + $this->assertInstanceOf(ReflectionProperty::class, $group2['property5']); + $this->assertInstanceOf(ReflectionProperty::class, $group2['property6']); + $this->assertInstanceOf(ReflectionProperty::class, $group2['property7']); + $this->assertInstanceOf(ReflectionProperty::class, $group2['property8']); + $this->assertInstanceOf(ReflectionProperty::class, $group2['property9']); } } From a418c4a679595752de5da4d4f8d23c707d1aff46 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 15 Dec 2014 16:12:36 +0100 Subject: [PATCH 153/153] Verifying `MagicGet` generated code even without parent `__set()` method being defined --- .../MethodGenerator/MagicGetTest.php | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php index d6389b653..1e0b904e2 100644 --- a/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php +++ b/tests/ProxyManagerTest/ProxyGenerator/LazyLoadingGhost/MethodGenerator/MagicGetTest.php @@ -23,6 +23,7 @@ use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PrivatePropertiesMap; use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\ProtectedPropertiesMap; use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap; +use ProxyManagerTestAsset\BaseClass; use ProxyManagerTestAsset\ClassWithMagicMethods; use ProxyManagerTestAsset\EmptyClass; use ProxyManagerTestAsset\ProxyGenerator\LazyLoading\MethodGenerator\ClassWithTwoPublicProperties; @@ -154,6 +155,25 @@ protected function setUp() $this->privateProperties->expects($this->any())->method('getName')->will($this->returnValue('tab')); } + /** + * @covers \ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicGet + */ + public function testBodyStructure() + { + $magicGet = new MagicGet( + new ReflectionClass(BaseClass::class), + $this->initializer, + $this->initMethod, + $this->publicProperties, + $this->protectedProperties, + $this->privateProperties + ); + + $this->assertSame('__get', $magicGet->getName()); + $this->assertCount(1, $magicGet->getParameters()); + + $this->assertStringMatchesFormat($this->expectedCode, $magicGet->getBody()); + } /** * @covers \ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator\MagicGet @@ -172,7 +192,6 @@ public function testBodyStructureWithOverriddenMagicGet() $this->assertSame('__get', $magicGet->getName()); $this->assertCount(1, $magicGet->getParameters()); - $this->assertStringMatchesFormat($this->expectedCode, $magicGet->getBody()); $this->assertStringMatchesFormat($this->expectedCode, $magicGet->getBody()); $this->assertStringMatchesFormat('%Areturn parent::__get($name);', $magicGet->getBody()); }