From 9322fa112d14c05ae27596a852d19c206af3d19e Mon Sep 17 00:00:00 2001 From: Pascal von Rickenbach Date: Tue, 11 Jun 2019 09:52:16 +0200 Subject: [PATCH 1/3] avoid multiple reads/writes for inherited properties in hydrator class --- .../CodeGenerator/Visitor/HydratorMethodsVisitor.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GeneratedHydrator/CodeGenerator/Visitor/HydratorMethodsVisitor.php b/src/GeneratedHydrator/CodeGenerator/Visitor/HydratorMethodsVisitor.php index cce50860..163e5350 100644 --- a/src/GeneratedHydrator/CodeGenerator/Visitor/HydratorMethodsVisitor.php +++ b/src/GeneratedHydrator/CodeGenerator/Visitor/HydratorMethodsVisitor.php @@ -83,8 +83,8 @@ private function findAllInstanceProperties(?ReflectionClass $class = null) : arr $this->findAllInstanceProperties($class->getParentClass() ?: null), // of course PHP is shit. array_values(array_filter( $class->getProperties(), - static function (ReflectionProperty $property) : bool { - return ! $property->isStatic(); + static function (ReflectionProperty $property) use ($class) : bool { + return ! ($property->isStatic() || $property->class !== $class->name); } )) )); From d9e382910abe84dc4c86aad260d21407a467d537 Mon Sep 17 00:00:00 2001 From: Pascal von Rickenbach Date: Tue, 11 Jun 2019 13:56:54 +0200 Subject: [PATCH 2/3] avoid directaccess to public properies of ext-reflection --- .../CodeGenerator/Visitor/HydratorMethodsVisitor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GeneratedHydrator/CodeGenerator/Visitor/HydratorMethodsVisitor.php b/src/GeneratedHydrator/CodeGenerator/Visitor/HydratorMethodsVisitor.php index 163e5350..24ed38ae 100644 --- a/src/GeneratedHydrator/CodeGenerator/Visitor/HydratorMethodsVisitor.php +++ b/src/GeneratedHydrator/CodeGenerator/Visitor/HydratorMethodsVisitor.php @@ -84,7 +84,7 @@ private function findAllInstanceProperties(?ReflectionClass $class = null) : arr array_values(array_filter( $class->getProperties(), static function (ReflectionProperty $property) use ($class) : bool { - return ! ($property->isStatic() || $property->class !== $class->name); + return ! ($property->isStatic() || $property->getDeclaringClass()->getName() !== $class->getName()); } )) )); From 0fb739988a8dd0dcc93c73de4c6d3571f79999f7 Mon Sep 17 00:00:00 2001 From: Pascal von Rickenbach Date: Wed, 12 Jun 2019 12:02:59 +0200 Subject: [PATCH 3/3] add unit test --- .../Visitor/HydratorMethodsVisitorTest.php | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/tests/GeneratedHydratorTest/CodeGenerator/Visitor/HydratorMethodsVisitorTest.php b/tests/GeneratedHydratorTest/CodeGenerator/Visitor/HydratorMethodsVisitorTest.php index f4e5a9d0..0bf40c75 100644 --- a/tests/GeneratedHydratorTest/CodeGenerator/Visitor/HydratorMethodsVisitorTest.php +++ b/tests/GeneratedHydratorTest/CodeGenerator/Visitor/HydratorMethodsVisitorTest.php @@ -7,8 +7,10 @@ use CodeGenerationUtils\Inflector\Util\UniqueIdentifierGenerator; use GeneratedHydrator\CodeGenerator\Visitor\HydratorMethodsVisitor; use PhpParser\Node; +use PhpParser\Node\Expr\Assign; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\NodeFinder; use PhpParser\ParserFactory; use PHPUnit\Framework\TestCase; use ReflectionClass; @@ -81,4 +83,106 @@ public function classAstProvider() : array [$staticClassName, $parser->parse('leaveNode($classNode); + + $constructors = $this->findConstructor($modifiedNode); + self::assertCount(1, $constructors); + $constructor = reset($constructors); + + $hydratePropertyNames = $this->findAssignedPropertyNames( + $constructor, + 'hydrateCallbacks', + 'object', + function (Assign $assign) { + return $assign->var->name->name; + } + ); + $extractPropertyNames = $this->findAssignedPropertyNames( + $constructor, + 'extractCallbacks', + 'values', + function (Assign $assign) { + return $assign->var->dim->value; + } + ); + + self::assertSameSize($properties, $hydratePropertyNames); + self::assertSameSize($properties, $extractPropertyNames); + self::assertCount(0, array_diff($properties, $hydratePropertyNames)); + self::assertCount(0, array_diff($properties, $extractPropertyNames)); + } + + private function findConstructor(Class_ $class) : array { + return array_filter( + $class->stmts, + static function (Node $node): bool { + return $node instanceof ClassMethod && '__construct' === $node->name->name; + } + ); + } + + private function findAssignedPropertyNames(Node $node, string $callbackName, string $variableName, callable $mapper) + { + $finder = new NodeFinder(); + $callbacks = $finder->find($node, function(Node $node) use ($callbackName) { + return $node instanceof Assign + && $node->var->var->name instanceof Node\Identifier + && $node->var->var->name->name === $callbackName; + }); + + $found = []; + foreach($callbacks as $callback) { + /** @noinspection SlowArrayOperationsInLoopInspection */ + $found = array_merge($found, $finder->find($callback, function(Node $node) use ($variableName) { + return $node instanceof Assign + && is_string($node->var->var->name) + && $node->var->var->name === $variableName; + })); + } + + return array_map($mapper, $found); + } + + /** + * @return Node[] + */ + public function propertyAstProvider(): array + { + $parser = (new ParserFactory())->create(ParserFactory::ONLY_PHP7); + + $className = UniqueIdentifierGenerator::getIdentifier('Foo'); + $classCode = 'class '.$className.' { private $bar; private $baz; protected $tab; ' + .'protected $tar; }'; + + eval($classCode); + + $subClassName = UniqueIdentifierGenerator::getIdentifier('Foo'); + $subClassCode = 'class '.$subClassName.' extends '.$className.' { private $fuz; protected $buz; }'; + + eval($subClassCode); + + $sub2ClassName = UniqueIdentifierGenerator::getIdentifier('Foo'); + $sub2ClassCode = 'class '.$sub2ClassName.' extends '.$subClassName.' { protected $bis; }'; + + eval($sub2ClassCode); + + return [ + [$className, $parser->parse('parse('parse('