From 9bad536c8c042bdeab8b4ccde9fa0ee9a6088a5e Mon Sep 17 00:00:00 2001 From: Krzysztof Gardziejewski Date: Wed, 18 Jul 2018 22:39:49 +0200 Subject: [PATCH 01/32] Draft of object builder based on callable blueprint --- .gitignore | 3 +- composer.json | 1 + src/Builder/Blueprint.php | 22 +++ src/Builder/Blueprint/Factory.php | 8 + .../Blueprint/Factory/CodeGenerator.php | 47 +++++ src/Builder/Blueprint/Factory/Store.php | 32 ++++ src/Builder/Blueprint/Store.php | 9 + src/Builder/Blueprint/Store/Memory.php | 25 +++ .../Blueprint/Factory/CodeGeneratorTest.php | 21 +++ .../unit/Builder/Blueprint/Factory/Simple.php | 20 ++ test/unit/Builder/BuilderTest.php | 176 ++++++++++++++++++ test/unit/Builder/ReflectionTest.php | 168 +---------------- 12 files changed, 364 insertions(+), 168 deletions(-) create mode 100644 src/Builder/Blueprint.php create mode 100644 src/Builder/Blueprint/Factory.php create mode 100644 src/Builder/Blueprint/Factory/CodeGenerator.php create mode 100644 src/Builder/Blueprint/Factory/Store.php create mode 100644 src/Builder/Blueprint/Store.php create mode 100644 src/Builder/Blueprint/Store/Memory.php create mode 100644 test/unit/Builder/Blueprint/Factory/CodeGeneratorTest.php create mode 100644 test/unit/Builder/Blueprint/Factory/Simple.php create mode 100644 test/unit/Builder/BuilderTest.php diff --git a/.gitignore b/.gitignore index 19982ea..bf12543 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ composer.lock -vendor \ No newline at end of file +vendor +.idea \ No newline at end of file diff --git a/composer.json b/composer.json index 7a2c093..40129eb 100644 --- a/composer.json +++ b/composer.json @@ -5,6 +5,7 @@ "keywords": [], "require": { "php": "^7.2", + "nette/php-generator": "^3.1", "phpstan/phpdoc-parser": "^0.3.0", "roave/better-reflection": "^3.0" }, diff --git a/src/Builder/Blueprint.php b/src/Builder/Blueprint.php new file mode 100644 index 0000000..92876ff --- /dev/null +++ b/src/Builder/Blueprint.php @@ -0,0 +1,22 @@ +blueprintFactory = $factory; + } + + public function build(string $class, array $data): object + { + $blueprint = $this->blueprintFactory->create($class); + + return $blueprint($data); + } +} diff --git a/src/Builder/Blueprint/Factory.php b/src/Builder/Blueprint/Factory.php new file mode 100644 index 0000000..1eb90fe --- /dev/null +++ b/src/Builder/Blueprint/Factory.php @@ -0,0 +1,8 @@ +closureGenerator = new Closure(); + } + + public function create(string $class): callable + { + $reflection = new ReflectionClass($class); + $constructor = $reflection->getConstructor(); + + $this->closureGenerator->addUse('class'); + $this->closureGenerator->addParameter('data'); + + if (null === $constructor) { + $this->closureGenerator->setBody('return new $class();'); + + return eval('return ' . (string) $this->closureGenerator . ';'); + } + + return function(array $data) use ($class) { }; + } + + private function getConstructorParameter(ReflectionMethod $method): array + { + $parameters = $method->getParameters(); + + if (0 === count($parameters)) { + return []; + } + + return []; + } +} diff --git a/src/Builder/Blueprint/Factory/Store.php b/src/Builder/Blueprint/Factory/Store.php new file mode 100644 index 0000000..1f54450 --- /dev/null +++ b/src/Builder/Blueprint/Factory/Store.php @@ -0,0 +1,32 @@ +store = $store; + $this->factory = $factory; + } + + public function create(string $class): callable + { + $blueprint = $this->store->get($class); + + if (null === $blueprint) { + $blueprint = $this->factory->create($class); + $this->store->save($class, $blueprint); + } + + return $blueprint; + } +} diff --git a/src/Builder/Blueprint/Store.php b/src/Builder/Blueprint/Store.php new file mode 100644 index 0000000..0b9acbd --- /dev/null +++ b/src/Builder/Blueprint/Store.php @@ -0,0 +1,9 @@ +store[$class]; + } catch (Throwable $exception) { + return null; + } + } + + public function save(string $class, callable $blueprint): void + { + $this->store[$class] = $blueprint; + } +} diff --git a/test/unit/Builder/Blueprint/Factory/CodeGeneratorTest.php b/test/unit/Builder/Blueprint/Factory/CodeGeneratorTest.php new file mode 100644 index 0000000..f55f219 --- /dev/null +++ b/test/unit/Builder/Blueprint/Factory/CodeGeneratorTest.php @@ -0,0 +1,21 @@ +create($class); + + $this->assertInstanceOf($class, $closure([])); + } +} diff --git a/test/unit/Builder/Blueprint/Factory/Simple.php b/test/unit/Builder/Blueprint/Factory/Simple.php new file mode 100644 index 0000000..bc705df --- /dev/null +++ b/test/unit/Builder/Blueprint/Factory/Simple.php @@ -0,0 +1,20 @@ +blueprint = $blueprint; + } + + public function create(string $class): callable + { + return $this->blueprint; + } +} diff --git a/test/unit/Builder/BuilderTest.php b/test/unit/Builder/BuilderTest.php new file mode 100644 index 0000000..b89a60a --- /dev/null +++ b/test/unit/Builder/BuilderTest.php @@ -0,0 +1,176 @@ + 'some string', + 'someInt' => 999, + ]; + $class = SimpleScalarConstructor::class; + + /** @var SimpleScalarConstructor $object */ + $object = static::$builder->build($class, $data); + + $this->assertInstanceOf(SimpleScalarConstructor::class, $object); + $this->assertSame('some string', $object->someString); + $this->assertSame(999, $object->someInt); + } + + /** @test */ + public function iCanBuildSimpleObjectWithScalarAndObjectValuesInConstructor() + { + $data = [ + 'someString' => 'some string', + 'someInt' => 999, + 'someObject' => [], + ]; + $class = SimpleMixedConstructor::class; + + /** @var SimpleMixedConstructor $object */ + $object = static::$builder->build($class, $data); + + $this->assertInstanceOf(SimpleMixedConstructor::class, $object); + $this->assertSame('some string', $object->someString); + $this->assertSame(999, $object->someInt); + $this->assertInstanceOf(SomeObjectWithEmptyConstructor::class, $object->someObject); + } + + /** @test */ + public function iCanBuildSimpleObjectWithDefaultValuesInConstructor() + { + $data = [ + 'someObject' => [], + ]; + $class = SimpleMixedConstructorWithDefaultValue::class; + + /** @var SimpleMixedConstructorWithDefaultValue $object */ + $object = static::$builder->build($class, $data); + + $this->assertInstanceOf(SimpleMixedConstructorWithDefaultValue::class, $object); + $this->assertSame('some string', $object->someString); + $this->assertSame(999, $object->someInt); + $this->assertInstanceOf(SomeObjectWithEmptyConstructor::class, $object->someObject); + } + + /** @test */ + public function iCanBuildObjectWithObjectCollectionWithoutUseInConstructor() + { + $data = [ + 'list' => [ + [ + 'someString' => 'some string1', + 'someInt' => 1, + ], + [ + 'someString' => 'some string2', + 'someInt' => 2, + ], + ], + ]; + $class = ListOfObjectsWithoutUseStmtConstructor::class; + + /** @var ListOfObjectsWithoutUseStmtConstructor $object */ + $object = static::$builder->build($class, $data); + + $this->assertInstanceOf(ListOfObjectsWithoutUseStmtConstructor::class, $object); + $this->assertCount(2, $object->list); + foreach($object->list as $element) { + $this->assertInstanceOf(SimpleScalarConstructor::class, $element); + } + } + + /** @test */ + public function iCanBuildObjectWithObjectCollectionWithUseInConstructor() + { + $data = [ + 'list' => [ + [], + [], + ], + ]; + $class = ListOfObjectsWithUseStmtConstructor::class; + + /** @var ListOfObjectsWithUseStmtConstructor $object */ + $object = static::$builder->build($class, $data); + + $this->assertInstanceOf(ListOfObjectsWithUseStmtConstructor::class, $object); + $this->assertCount(2, $object->list); + foreach($object->list as $element) { + $this->assertInstanceOf(SomeSecondObject::class, $element); + } + } + + /** @test */ + public function iCanBuildObjectWithObjectCollectionWithoutUseButWithFQNTypedArrayInConstructor() + { + $data = [ + 'list' => [ + [], + [], + ], + ]; + $class = ListOfObjectsWithoutUseButWithFQNTypedArrayConstructor::class; + + /** @var ListOfObjectsWithoutUseButWithFQNTypedArrayConstructor $object */ + $object = static::$builder->build($class, $data); + + $this->assertInstanceOf(ListOfObjectsWithoutUseButWithFQNTypedArrayConstructor::class, $object); + $this->assertCount(2, $object->list); + foreach($object->list as $element) { + $this->assertInstanceOf(SomeObject::class, $element); + } + } + + /** @test */ + public function iCanBuildAdvancedObjectHierarchy() + { + $data = [ + 'someString' => 'some string', + 'simpleObject1' => [ + 'someString' => 'some string', + 'someInt' => 1, + ], + 'simpleObject2' => [ + 'someString' => 'some string', + 'someInt' => 2, + 'someObject' => [], + ], + 'someInt' => 3, + ]; + $class = SomeAggregateRoot::class; + + /** @var SomeAggregateRoot $object */ + $object = static::$builder->build($class, $data); + + $this->assertInstanceOf(SomeAggregateRoot::class, $object); + $this->assertSame('some string', $object->someString); + $this->assertSame(3, $object->someInt); + $this->assertInstanceOf(SimpleScalarConstructor::class, $object->simpleObject1); + $this->assertInstanceOf(SimpleMixedConstructor::class, $object->simpleObject2); + $this->assertSame(1, $object->simpleObject1->someInt); + $this->assertSame(2, $object->simpleObject2->someInt); + } +} diff --git a/test/unit/Builder/ReflectionTest.php b/test/unit/Builder/ReflectionTest.php index e203426..f9ef8c1 100644 --- a/test/unit/Builder/ReflectionTest.php +++ b/test/unit/Builder/ReflectionTest.php @@ -2,179 +2,13 @@ namespace RstGroup\ObjectBuilder\Test\unit\Builder; -use PHPUnit\Framework\TestCase; use RstGroup\ObjectBuilder\Builder\ParameterNameStrategy\Simple; use RstGroup\ObjectBuilder\Builder\Reflection; -use RstGroup\ObjectBuilder\Test\ListOfObjectsWithoutUseButWithFQNTypedArrayConstructor; -use RstGroup\ObjectBuilder\Test\ListOfObjectsWithoutUseStmtConstructor; -use RstGroup\ObjectBuilder\Test\ListOfObjectsWithUseStmtConstructor; -use RstGroup\ObjectBuilder\Test\Object\SomeObject; -use RstGroup\ObjectBuilder\Test\Object\SomeSecondObject; -use RstGroup\ObjectBuilder\Test\SimpleMixedConstructor; -use RstGroup\ObjectBuilder\Test\SimpleMixedConstructorWithDefaultValue; -use RstGroup\ObjectBuilder\Test\SimpleScalarConstructor; -use RstGroup\ObjectBuilder\Test\SomeAggregateRoot; -use RstGroup\ObjectBuilder\Test\SomeObjectWithEmptyConstructor; -class ReflectionTest extends TestCase +class ReflectionTest extends BuilderTest { - /** @var Reflection */ - private static $builder; - public static function setUpBeforeClass() { static::$builder = new Reflection(new Simple()); } - - /** @test */ - public function iCanBuildSimpleObjectWithScalarValuesInConstructor() - { - $data = [ - 'someString' => 'some string', - 'someInt' => 999, - ]; - $class = SimpleScalarConstructor::class; - - /** @var SimpleScalarConstructor $object */ - $object = static::$builder->build($class, $data); - - $this->assertInstanceOf(SimpleScalarConstructor::class, $object); - $this->assertSame('some string', $object->someString); - $this->assertSame(999, $object->someInt); - } - - /** @test */ - public function iCanBuildSimpleObjectWithScalarAndObjectValuesInConstructor() - { - $data = [ - 'someString' => 'some string', - 'someInt' => 999, - 'someObject' => [], - ]; - $class = SimpleMixedConstructor::class; - - /** @var SimpleMixedConstructor $object */ - $object = static::$builder->build($class, $data); - - $this->assertInstanceOf(SimpleMixedConstructor::class, $object); - $this->assertSame('some string', $object->someString); - $this->assertSame(999, $object->someInt); - $this->assertInstanceOf(SomeObjectWithEmptyConstructor::class, $object->someObject); - } - - /** @test */ - public function iCanBuildSimpleObjectWithDefaultValuesInConstructor() - { - $data = [ - 'someObject' => [], - ]; - $class = SimpleMixedConstructorWithDefaultValue::class; - - /** @var SimpleMixedConstructorWithDefaultValue $object */ - $object = static::$builder->build($class, $data); - - $this->assertInstanceOf(SimpleMixedConstructorWithDefaultValue::class, $object); - $this->assertSame('some string', $object->someString); - $this->assertSame(999, $object->someInt); - $this->assertInstanceOf(SomeObjectWithEmptyConstructor::class, $object->someObject); - } - - /** @test */ - public function iCanBuildObjectWithObjectCollectionWithoutUseInConstructor() - { - $data = [ - 'list' => [ - [ - 'someString' => 'some string1', - 'someInt' => 1, - ], - [ - 'someString' => 'some string2', - 'someInt' => 2, - ], - ], - ]; - $class = ListOfObjectsWithoutUseStmtConstructor::class; - - /** @var ListOfObjectsWithoutUseStmtConstructor $object */ - $object = static::$builder->build($class, $data); - - $this->assertInstanceOf(ListOfObjectsWithoutUseStmtConstructor::class, $object); - $this->assertCount(2, $object->list); - foreach($object->list as $element) { - $this->assertInstanceOf(SimpleScalarConstructor::class, $element); - } - } - - /** @test */ - public function iCanBuildObjectWithObjectCollectionWithUseInConstructor() - { - $data = [ - 'list' => [ - [], - [], - ], - ]; - $class = ListOfObjectsWithUseStmtConstructor::class; - - /** @var ListOfObjectsWithUseStmtConstructor $object */ - $object = static::$builder->build($class, $data); - - $this->assertInstanceOf(ListOfObjectsWithUseStmtConstructor::class, $object); - $this->assertCount(2, $object->list); - foreach($object->list as $element) { - $this->assertInstanceOf(SomeSecondObject::class, $element); - } - } - - /** @test */ - public function iCanBuildObjectWithObjectCollectionWithoutUseButWithFQNTypedArrayInConstructor() - { - $data = [ - 'list' => [ - [], - [], - ], - ]; - $class = ListOfObjectsWithoutUseButWithFQNTypedArrayConstructor::class; - - /** @var ListOfObjectsWithoutUseButWithFQNTypedArrayConstructor $object */ - $object = static::$builder->build($class, $data); - - $this->assertInstanceOf(ListOfObjectsWithoutUseButWithFQNTypedArrayConstructor::class, $object); - $this->assertCount(2, $object->list); - foreach($object->list as $element) { - $this->assertInstanceOf(SomeObject::class, $element); - } - } - - /** @test */ - public function iCanBuildAdvancedObjectHierarchy() - { - $data = [ - 'someString' => 'some string', - 'simpleObject1' => [ - 'someString' => 'some string', - 'someInt' => 1, - ], - 'simpleObject2' => [ - 'someString' => 'some string', - 'someInt' => 2, - 'someObject' => [], - ], - 'someInt' => 3, - ]; - $class = SomeAggregateRoot::class; - - /** @var SomeAggregateRoot $object */ - $object = static::$builder->build($class, $data); - - $this->assertInstanceOf(SomeAggregateRoot::class, $object); - $this->assertSame('some string', $object->someString); - $this->assertSame(3, $object->someInt); - $this->assertInstanceOf(SimpleScalarConstructor::class, $object->simpleObject1); - $this->assertInstanceOf(SimpleMixedConstructor::class, $object->simpleObject2); - $this->assertSame(1, $object->simpleObject1->someInt); - $this->assertSame(2, $object->simpleObject2->someInt); - } } From 1c0f4b21d4181b776cf3cdb6f44970e8e45ebf21 Mon Sep 17 00:00:00 2001 From: Krzysztof Gardziejewski Date: Mon, 23 Jul 2018 20:00:53 +0200 Subject: [PATCH 02/32] Building complex object without default values --- .../Factory/CodeGenerator/Anonymous.php | 66 ++++++++++++++++ .../Blueprint/Factory/CodeGenerator/Node.php | 27 +++++++ .../Factory/CodeGenerator/Node/Complex.php | 25 ++++++ .../Factory/CodeGenerator/Node/Scalar.php | 13 ++++ .../Blueprint/Factory/CodeGenerator/Store.php | 9 +++ .../CodeGenerator}/Store/Memory.php | 8 +- src/Builder/Blueprint/Factory/Store.php | 32 -------- src/Builder/Blueprint/Store.php | 9 --- .../Factory/CodeGenerator/AnonymousTest.php | 78 +++++++++++++++++++ .../Blueprint/Factory/CodeGeneratorTest.php | 21 ----- 10 files changed, 222 insertions(+), 66 deletions(-) create mode 100644 src/Builder/Blueprint/Factory/CodeGenerator/Anonymous.php create mode 100644 src/Builder/Blueprint/Factory/CodeGenerator/Node.php create mode 100644 src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php create mode 100644 src/Builder/Blueprint/Factory/CodeGenerator/Node/Scalar.php create mode 100644 src/Builder/Blueprint/Factory/CodeGenerator/Store.php rename src/Builder/Blueprint/{ => Factory/CodeGenerator}/Store/Memory.php (54%) delete mode 100644 src/Builder/Blueprint/Factory/Store.php delete mode 100644 src/Builder/Blueprint/Store.php create mode 100644 test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php delete mode 100644 test/unit/Builder/Blueprint/Factory/CodeGeneratorTest.php diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Anonymous.php b/src/Builder/Blueprint/Factory/CodeGenerator/Anonymous.php new file mode 100644 index 0000000..eae8283 --- /dev/null +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Anonymous.php @@ -0,0 +1,66 @@ +closureGenerator = new Closure(); + } + + public function create(string $class): string + { + $reflection = new ReflectionClass($class); + + return sprintf( + self::FILE_BEGIN_WITH, + sprintf( + self::FUNCTION_PATTERN, + $this->getNode($reflection) + ) + ); + } + + private function getNode(ReflectionClass $class): Node + { + $constructor = $class->getConstructor(); + + if (null === $constructor) { + return new Complex($class->getName()); + } + + return $this->getNodes($constructor); + } + + private function getNodes(ReflectionMethod $method): Node + { + $node = new Complex($method->getDeclaringClass()->getName()); + + foreach ($method->getParameters() as $parameter) { + $class = $parameter->getClass(); + + if (null === $class) { + $node->add(new Scalar($parameter->getName())); + continue; + } + + $node->add($this->getNode($class)); + } + + return $node; + } +} diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node.php new file mode 100644 index 0000000..07891c6 --- /dev/null +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node.php @@ -0,0 +1,27 @@ +name = $name; + $this->defaultValue = $defaultValue; + } + + public function withDefaultValue(): bool + { + return null !== $this->defaultValue; + } + + public function defaultValue() + { + return $this->defaultValue; + } + + abstract public function __toString(): string; +} diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php new file mode 100644 index 0000000..3381221 --- /dev/null +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php @@ -0,0 +1,25 @@ +nodes[] = $node; + } + + public function __toString(): string + { + return sprintf( + 'new %s(%s)', + $this->name, + implode(', ', $this->nodes) + ); + } +} diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Scalar.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Scalar.php new file mode 100644 index 0000000..e434657 --- /dev/null +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Scalar.php @@ -0,0 +1,13 @@ +name); + } +} diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Store.php b/src/Builder/Blueprint/Factory/CodeGenerator/Store.php new file mode 100644 index 0000000..aaaddbf --- /dev/null +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Store.php @@ -0,0 +1,9 @@ +store[$class]; + return eval($this->store[$class]); } catch (Throwable $exception) { return null; } } - public function save(string $class, callable $blueprint): void + public function save(string $class, string $blueprint): void { $this->store[$class] = $blueprint; } diff --git a/src/Builder/Blueprint/Factory/Store.php b/src/Builder/Blueprint/Factory/Store.php deleted file mode 100644 index 1f54450..0000000 --- a/src/Builder/Blueprint/Factory/Store.php +++ /dev/null @@ -1,32 +0,0 @@ -store = $store; - $this->factory = $factory; - } - - public function create(string $class): callable - { - $blueprint = $this->store->get($class); - - if (null === $blueprint) { - $blueprint = $this->factory->create($class); - $this->store->save($class, $blueprint); - } - - return $blueprint; - } -} diff --git a/src/Builder/Blueprint/Store.php b/src/Builder/Blueprint/Store.php deleted file mode 100644 index 0b9acbd..0000000 --- a/src/Builder/Blueprint/Store.php +++ /dev/null @@ -1,9 +0,0 @@ -create($class); + + $this->assertSame( +'create($class); + + $this->assertSame( + 'create($class); +// +// $this->assertSame( +// 'create($class); + + $this->assertSame( + 'create($class); - - $this->assertInstanceOf($class, $closure([])); - } -} From 985eb59bb176bde3a41a57d5769d775b6c853aae Mon Sep 17 00:00:00 2001 From: Krzysztof Gardziejewski Date: Mon, 23 Jul 2018 22:31:48 +0200 Subject: [PATCH 03/32] Building complex object with default values --- .../Factory/CodeGenerator/Anonymous.php | 84 ++++++++++++++----- .../Blueprint/Factory/CodeGenerator/Node.php | 5 ++ .../Factory/CodeGenerator/Node/Complex.php | 20 ++++- .../Factory/CodeGenerator/AnonymousTest.php | 60 ++++++++----- 4 files changed, 130 insertions(+), 39 deletions(-) diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Anonymous.php b/src/Builder/Blueprint/Factory/CodeGenerator/Anonymous.php index eae8283..94759cf 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Anonymous.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Anonymous.php @@ -3,7 +3,6 @@ namespace RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator; -use Nette\PhpGenerator\Closure; use ReflectionClass; use ReflectionMethod; use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Node\Complex; @@ -11,56 +10,103 @@ final class Anonymous { - private const FILE_BEGIN_WITH = 'closureGenerator = new Closure(); - } - public function create(string $class): string { $reflection = new ReflectionClass($class); - return sprintf( - self::FILE_BEGIN_WITH, - sprintf( + $node = $this->getNode($reflection); + + return self::FILE_BEGIN_WITH + . "\n" + . 'return ' + . sprintf( self::FUNCTION_PATTERN, - $this->getNode($reflection) + $this->getDefaultSection($node), + $node ) - ); + ; } - private function getNode(ReflectionClass $class): Node + private function getNode(ReflectionClass $class, string $name = ''): Node { $constructor = $class->getConstructor(); if (null === $constructor) { - return new Complex($class->getName()); + return new Complex($class->getName(), $name); } - return $this->getNodes($constructor); + return $this->getNodes($constructor, $name); } - private function getNodes(ReflectionMethod $method): Node + private function getNodes(ReflectionMethod $method, string $name = ''): Node { - $node = new Complex($method->getDeclaringClass()->getName()); + $node = new Complex($method->getDeclaringClass()->getName(), $name); foreach ($method->getParameters() as $parameter) { $class = $parameter->getClass(); if (null === $class) { - $node->add(new Scalar($parameter->getName())); + $node->add( + new Scalar( + $parameter->getName(), + $parameter->isDefaultValueAvailable() + ? $parameter->getDefaultValue() + : null + ) + ); continue; } - $node->add($this->getNode($class)); + $node->add($this->getNode($class, $parameter->getName())); } return $node; } + + private function getDefaultSection(Node $node): string + { + $defaultSection = ''; + $defaultValues = $this->getDefaultValues($node); + if (! empty($defaultValues)) { + $defaultSection = sprintf( + self::DEFAULT_VALUES_PATTERN, + var_export($defaultValues, true) + ); + } + + return $defaultSection; + } + + private function getDefaultValues(Node $node): array + { + $values = []; + + if ($node instanceof Complex) { + foreach ($node->innerNodes() as $innerNode) { + $innerNodeDefaultValues = $this->getDefaultValues($innerNode); + if(! empty($innerNodeDefaultValues)) { + $values[$innerNode->name()] = $innerNodeDefaultValues; + } + } + } + + if ($node->withDefaultValue()) { + $values[$node->name()] = $node->defaultValue(); + } + + return $values; + } } diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node.php index 07891c6..4a9bdb8 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Node.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node.php @@ -13,6 +13,11 @@ public function __construct(string $name, $defaultValue = null) $this->defaultValue = $defaultValue; } + public function name(): string + { + return $this->name; + } + public function withDefaultValue(): bool { return null !== $this->defaultValue; diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php index 3381221..d0d4773 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php @@ -8,17 +8,35 @@ final class Complex extends Node { /** @var Node */ private $nodes = []; + private $class; + + public function __construct(string $class, string $name = '', $defaultValue = null) + { + parent::__construct($name, $defaultValue); + $this->class = $class; + } + + public function getClass(): string + { + return $this->class; + } public function add(Node $node): void { $this->nodes[] = $node; } + /** @return Node[] */ + public function innerNodes(): array + { + return $this->nodes; + } + public function __toString(): string { return sprintf( 'new %s(%s)', - $this->name, + $this->getClass(), implode(', ', $this->nodes) ); } diff --git a/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php b/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php index 7a741e9..69a4942 100644 --- a/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php +++ b/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php @@ -20,7 +20,10 @@ public function iCanGenerateSimpleObjectClosure(): void $blueprint = $factory->create($class); $this->assertSame( -'create($class); $this->assertSame( - 'create($class); -// -// $this->assertSame( -// 'create($class); + + $this->assertSame( + ' + array ( + \'someString\' => \'some string\', + ), + \'someInt\' => + array ( + \'someInt\' => 999, + ), +); + $data = array_merge($default, $data); + + return new RstGroup\ObjectBuilder\Test\SimpleMixedConstructorWithDefaultValue(new RstGroup\ObjectBuilder\Test\SomeObjectWithEmptyConstructor(), $data[\'someString\'], $data[\'someInt\']); +}', + $blueprint + ); + } /** @test */ public function iCanBuildAdvancedObjectHierarchy() @@ -69,7 +88,10 @@ public function iCanBuildAdvancedObjectHierarchy() $blueprint = $factory->create($class); $this->assertSame( - ' Date: Tue, 24 Jul 2018 22:15:19 +0200 Subject: [PATCH 04/32] Building object with default values fix --- .../Blueprint/Factory/CodeGenerator.php | 35 ++---- .../Factory/CodeGenerator/Anonymous.php | 47 ++++++-- .../Blueprint/Factory/CodeGenerator/Node.php | 9 +- .../Factory/CodeGenerator/Node/Complex.php | 14 +-- .../Factory/CodeGenerator/Node/ObjectList.php | 28 +++++ .../Factory/CodeGenerator/PhpDocParser.php | 104 ++++++++++++++++++ .../Factory/CodeGenerator/AnonymousTest.php | 42 +++++-- test/unit/Builder/BlueprintTest.php | 15 +++ test/unit/Builder/BuilderTest.php | 10 +- 9 files changed, 239 insertions(+), 65 deletions(-) create mode 100644 src/Builder/Blueprint/Factory/CodeGenerator/Node/ObjectList.php create mode 100644 src/Builder/Blueprint/Factory/CodeGenerator/PhpDocParser.php create mode 100644 test/unit/Builder/BlueprintTest.php diff --git a/src/Builder/Blueprint/Factory/CodeGenerator.php b/src/Builder/Blueprint/Factory/CodeGenerator.php index 0507f2b..f29ad23 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator.php @@ -2,46 +2,27 @@ namespace RstGroup\ObjectBuilder\Builder\Blueprint\Factory; -use Nette\PhpGenerator\Closure; -use ReflectionClass; -use ReflectionMethod; use RstGroup\ObjectBuilder\Builder\Blueprint\Factory; final class CodeGenerator implements Factory { - /** @var Closure */ - private $closureGenerator; + /** @var Factory\CodeGenerator\Anonymous */ + private $generator; public function __construct() { - $this->closureGenerator = new Closure(); + $this->generator = new Factory\CodeGenerator\Anonymous(); } public function create(string $class): callable { - $reflection = new ReflectionClass($class); - $constructor = $reflection->getConstructor(); + $blueprint = $this->generator->create($class); + $prefix = 'closureGenerator->addUse('class'); - $this->closureGenerator->addParameter('data'); - - if (null === $constructor) { - $this->closureGenerator->setBody('return new $class();'); - - return eval('return ' . (string) $this->closureGenerator . ';'); - } - - return function(array $data) use ($class) { }; - } - - private function getConstructorParameter(ReflectionMethod $method): array - { - $parameters = $method->getParameters(); - - if (0 === count($parameters)) { - return []; + if (substr($blueprint, 0, strlen($prefix)) == $prefix) { + $blueprint = substr($blueprint, strlen($prefix)); } - return []; + return eval($blueprint . ';'); } } diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Anonymous.php b/src/Builder/Blueprint/Factory/CodeGenerator/Anonymous.php index 94759cf..8086a65 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Anonymous.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Anonymous.php @@ -6,6 +6,7 @@ use ReflectionClass; use ReflectionMethod; use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Node\Complex; +use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Node\ObjectList; use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Node\Scalar; final class Anonymous @@ -18,11 +19,19 @@ final class Anonymous . ' $data = array_merge($default, $data);' . "\n"; private const FUNCTION_PATTERN = -'function(array $data) use ($class): string { +'function(array $data) use ($class): object { %s return %s; }'; + /** @var PhpDocParser */ + private $phpDocParser; + + public function __construct() + { + $this->phpDocParser = new PhpDocParser(); + } + public function create(string $class): string { $reflection = new ReflectionClass($class); @@ -60,11 +69,9 @@ private function getNodes(ReflectionMethod $method, string $name = ''): Node if (null === $class) { $node->add( - new Scalar( - $parameter->getName(), - $parameter->isDefaultValueAvailable() - ? $parameter->getDefaultValue() - : null + $this->createNode( + $parameter, + $method->getDocComment() ? $method->getDocComment() : '' ) ); continue; @@ -76,6 +83,29 @@ private function getNodes(ReflectionMethod $method, string $name = ''): Node return $node; } + private function createNode(\ReflectionParameter $parameter, string $phpDoc): Node + { + $type = $parameter->getType()->getName(); + + if ($type === 'array' + && $this->phpDocParser->isListOfObject($phpDoc, $parameter)) { + $class = $this->phpDocParser->getListType($phpDoc, $parameter); + + return new ObjectList( + $parameter->getName(), + $this->getNode(new ReflectionClass($class)) + ); + } + + return new Scalar( + $type, + $parameter->getName(), + $parameter->isDefaultValueAvailable() + ? $parameter->getDefaultValue() + : null + ); + } + private function getDefaultSection(Node $node): string { $defaultSection = ''; @@ -90,7 +120,8 @@ private function getDefaultSection(Node $node): string return $defaultSection; } - private function getDefaultValues(Node $node): array + /** @return mixed */ + private function getDefaultValues(Node $node) { $values = []; @@ -104,7 +135,7 @@ private function getDefaultValues(Node $node): array } if ($node->withDefaultValue()) { - $values[$node->name()] = $node->defaultValue(); + $values = $node->defaultValue(); } return $values; diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node.php index 4a9bdb8..63a5f96 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Node.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node.php @@ -6,11 +6,18 @@ abstract class Node { protected $name; private $defaultValue; + private $type; - public function __construct(string $name, $defaultValue = null) + public function __construct(string $type, string $name, $defaultValue = null) { $this->name = $name; $this->defaultValue = $defaultValue; + $this->type = $type; + } + + public function type(): string + { + return $this->type; } public function name(): string diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php index d0d4773..2b697c5 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php @@ -8,18 +8,6 @@ final class Complex extends Node { /** @var Node */ private $nodes = []; - private $class; - - public function __construct(string $class, string $name = '', $defaultValue = null) - { - parent::__construct($name, $defaultValue); - $this->class = $class; - } - - public function getClass(): string - { - return $this->class; - } public function add(Node $node): void { @@ -36,7 +24,7 @@ public function __toString(): string { return sprintf( 'new %s(%s)', - $this->getClass(), + $this->type(), implode(', ', $this->nodes) ); } diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node/ObjectList.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node/ObjectList.php new file mode 100644 index 0000000..7e832a7 --- /dev/null +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node/ObjectList.php @@ -0,0 +1,28 @@ +type(), $name); + $this->objectNode = $objectNode; + } + + public function __toString(): string + { + return sprintf('(function (array $list) { + $arr = []; + foreach ($list as $data) { + $arr[] = %s; + } + + return $arr; + })($data)', $this->objectNode); + } +} diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/PhpDocParser.php b/src/Builder/Blueprint/Factory/CodeGenerator/PhpDocParser.php new file mode 100644 index 0000000..d2228f0 --- /dev/null +++ b/src/Builder/Blueprint/Factory/CodeGenerator/PhpDocParser.php @@ -0,0 +1,104 @@ +parse(new TokenIterator((new Lexer())->tokenize($comment))); + + foreach ($node->getParamTagValues() as $node) { + if ($node->parameterName === '$' . $parameter->getName()) { + return true; + } + } + + return false; + } + + public function getListType(string $comment, ReflectionParameter $parameter): string + { + $parser = new PhpStanPhpDocParser(new TypeParser(), new ConstExprParser()); + $node = $parser->parse(new TokenIterator((new Lexer())->tokenize($comment))); + + foreach ($node->getParamTagValues() as $node) { + if ($node->parameterName === '$' . $parameter->getName()) { + $type = $node->type->type; + $parser = (new BetterReflection())->phpParser(); + + $parsedFile = $parser->parse(file_get_contents($parameter->getDeclaringClass()->getFileName())); + $namespace = $this->getNamespaceStmt($parsedFile); + $uses = $this->getUseStmts($namespace); + $namespaces = $this->getUsesNamespaces($uses); + + return $this->getFullClassName($type->name, $namespaces, $parameter->getDeclaringClass()); + } + } + } + + /** + * @param Stmt[] $nodes + */ + private function getNamespaceStmt(array $nodes): Stmt\Namespace_ + { + foreach ($nodes as $node) { + if ($node instanceof Stmt\Namespace_) { + return $node; + } + } + + return new Stmt\Namespace_(); + } + + /** @return Stmt\Use_[] */ + private function getUseStmts(Stmt\Namespace_ $node): array + { + $uses = []; + foreach ($node->stmts as $node) { + if ($node instanceof Stmt\Use_) { + $uses[]= $node; + } + } + + return $uses; + } + + /** + * @param Stmt\Use_[] $uses + * @return string[] + */ + private function getUsesNamespaces(array $uses): array + { + $names = []; + foreach ($uses as $use) { + $names[] = $use->uses[0]->name->toString(); + } + + return $names; + } + + private function getFullClassName(string $name, array $namespaces, ReflectionClass $class): string + { + if ($name[0] === '\\') { + return $name; + } + + if (0 === count($namespaces)) { + return $class->getNamespaceName() . '\\' . $name; + } + + return $this->getNamespaceForClass($name, $namespaces); + } +} diff --git a/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php b/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php index 69a4942..fd72cc9 100644 --- a/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php +++ b/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php @@ -4,6 +4,7 @@ use PHPUnit\Framework\TestCase; use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Anonymous; +use RstGroup\ObjectBuilder\Test\ListOfObjectsWithoutUseStmtConstructor; use RstGroup\ObjectBuilder\Test\SimpleMixedConstructorWithDefaultValue; use RstGroup\ObjectBuilder\Test\SimpleScalarConstructor; use RstGroup\ObjectBuilder\Test\SomeAggregateRoot; @@ -22,7 +23,7 @@ public function iCanGenerateSimpleObjectClosure(): void $this->assertSame( 'assertSame( 'assertSame( ' - array ( - \'someString\' => \'some string\', - ), - \'someInt\' => - array ( - \'someInt\' => 999, - ), + \'someString\' => \'some string\', + \'someInt\' => 999, ); $data = array_merge($default, $data); @@ -90,11 +85,34 @@ public function iCanBuildAdvancedObjectHierarchy() $this->assertSame( 'create($class); + + $this->assertSame(' 'some string', + 'someString' => 'some string3', 'simpleObject1' => [ - 'someString' => 'some string', + 'someString' => 'some string1', 'someInt' => 1, ], 'simpleObject2' => [ - 'someString' => 'some string', + 'someString' => 'some string2', 'someInt' => 2, 'someObject' => [], ], @@ -166,11 +166,13 @@ public function iCanBuildAdvancedObjectHierarchy() $object = static::$builder->build($class, $data); $this->assertInstanceOf(SomeAggregateRoot::class, $object); - $this->assertSame('some string', $object->someString); + $this->assertSame('some string3', $object->someString); $this->assertSame(3, $object->someInt); $this->assertInstanceOf(SimpleScalarConstructor::class, $object->simpleObject1); $this->assertInstanceOf(SimpleMixedConstructor::class, $object->simpleObject2); $this->assertSame(1, $object->simpleObject1->someInt); + $this->assertSame('some string1', $object->simpleObject1->someString); $this->assertSame(2, $object->simpleObject2->someInt); + $this->assertSame('some string2', $object->simpleObject2->someString); } } From d1957bc46b350db2201d86a82383a5bf99d11920 Mon Sep 17 00:00:00 2001 From: Krzysztof Gardziejewski Date: Tue, 24 Jul 2018 22:32:12 +0200 Subject: [PATCH 05/32] Building advanced object hierarchy with nested objects --- .../Factory/CodeGenerator/Node/Complex.php | 15 ++++++++++++++- .../Factory/CodeGenerator/Node/Scalar.php | 2 +- .../Factory/CodeGenerator/AnonymousTest.php | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php index 2b697c5..c3aae86 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php @@ -22,10 +22,23 @@ public function innerNodes(): array public function __toString(): string { + $nodes = []; + foreach ($this->nodes as $node) { + if ($node instanceof Scalar) { + $nodeSerialized = (string) $node; + if (! empty($this->name())) { + $nodeSerialized = '[\'' . $this->name() . '\']' . $nodeSerialized; + } + $nodes[] = '$data' . $nodeSerialized; + continue; + } + + $nodes[] = (string) $node; + } return sprintf( 'new %s(%s)', $this->type(), - implode(', ', $this->nodes) + implode(', ', $nodes) ); } } diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Scalar.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Scalar.php index e434657..929afce 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Scalar.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Scalar.php @@ -8,6 +8,6 @@ final class Scalar extends Node { public function __toString(): string { - return sprintf('$data[\'%s\']', $this->name); + return sprintf('[\'%s\']', $this->name); } } diff --git a/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php b/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php index fd72cc9..ae98cce 100644 --- a/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php +++ b/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php @@ -87,7 +87,7 @@ public function iCanBuildAdvancedObjectHierarchy() return function(array $data) use ($class): object { - return new RstGroup\ObjectBuilder\Test\SomeAggregateRoot($data[\'someString\'], new RstGroup\ObjectBuilder\Test\SimpleScalarConstructor($data[\'someString\'], $data[\'someInt\']), new RstGroup\ObjectBuilder\Test\SimpleMixedConstructor($data[\'someString\'], $data[\'someInt\'], new RstGroup\ObjectBuilder\Test\SomeObjectWithEmptyConstructor()), $data[\'someInt\']); + return new RstGroup\ObjectBuilder\Test\SomeAggregateRoot($data[\'someString\'], new RstGroup\ObjectBuilder\Test\SimpleScalarConstructor($data[\'simpleObject1\'][\'someString\'], $data[\'simpleObject1\'][\'someInt\']), new RstGroup\ObjectBuilder\Test\SimpleMixedConstructor($data[\'simpleObject2\'][\'someString\'], $data[\'simpleObject2\'][\'someInt\'], new RstGroup\ObjectBuilder\Test\SomeObjectWithEmptyConstructor()), $data[\'someInt\']); }', $blueprint ); From 8326c724010cca2a2ea9f497854186dda97b8b55 Mon Sep 17 00:00:00 2001 From: Krzysztof Gardziejewski Date: Tue, 24 Jul 2018 22:40:06 +0200 Subject: [PATCH 06/32] Handle all ReflectionBuilder cases with BlueprintBuilder --- .../Factory/CodeGenerator/Anonymous.php | 1 - .../Factory/CodeGenerator/Node/ObjectList.php | 2 +- .../Factory/CodeGenerator/PhpDocParser.php | 23 +++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Anonymous.php b/src/Builder/Blueprint/Factory/CodeGenerator/Anonymous.php index 8086a65..2eca24d 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Anonymous.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Anonymous.php @@ -2,7 +2,6 @@ namespace RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator; - use ReflectionClass; use ReflectionMethod; use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Node\Complex; diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node/ObjectList.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node/ObjectList.php index 7e832a7..a73fa4c 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Node/ObjectList.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node/ObjectList.php @@ -23,6 +23,6 @@ public function __toString(): string } return $arr; - })($data)', $this->objectNode); + })($data[\'' . $this->name() . '\'])', $this->objectNode); } } diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/PhpDocParser.php b/src/Builder/Blueprint/Factory/CodeGenerator/PhpDocParser.php index d2228f0..7072f6f 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/PhpDocParser.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/PhpDocParser.php @@ -11,6 +11,7 @@ use ReflectionClass; use ReflectionParameter; use Roave\BetterReflection\BetterReflection; +use RstGroup\ObjectBuilder\BuilderException; class PhpDocParser { @@ -101,4 +102,26 @@ private function getFullClassName(string $name, array $namespaces, ReflectionCla return $this->getNamespaceForClass($name, $namespaces); } + + /** + * @param string[] $namespaces + * @throws BuilderException + */ + private function getNamespaceForClass(string $className, array $namespaces): string + { + foreach ($namespaces as $namespace) { + if ($this->endsWith($namespace, $className)) { + return $namespace; + } + } + + throw new BuilderException('Can not resolve namespace for class ' . $className); + } + + private function endsWith(string $haystack, string $needle): bool + { + $length = strlen($needle); + + return $length === 0 || (substr($haystack, -$length) === $needle); + } } From e6941b0ed728729a3d75b8cffd1e6a892c3d1e4a Mon Sep 17 00:00:00 2001 From: Krzysztof Gardziejewski Date: Tue, 24 Jul 2018 22:44:21 +0200 Subject: [PATCH 07/32] Removed unused dependency --- composer.json | 1 - .../Blueprint/Factory/CodeGenerator/Node/ObjectList.php | 5 ++++- .../Blueprint/Factory/CodeGenerator/AnonymousTest.php | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 40129eb..7a2c093 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,6 @@ "keywords": [], "require": { "php": "^7.2", - "nette/php-generator": "^3.1", "phpstan/phpdoc-parser": "^0.3.0", "roave/better-reflection": "^3.0" }, diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node/ObjectList.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node/ObjectList.php index a73fa4c..9b6b069 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Node/ObjectList.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node/ObjectList.php @@ -23,6 +23,9 @@ public function __toString(): string } return $arr; - })($data[\'' . $this->name() . '\'])', $this->objectNode); + })($data[\'%s\'])', + $this->objectNode, + $this->name() + ); } } diff --git a/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php b/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php index ae98cce..0f434e1 100644 --- a/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php +++ b/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php @@ -112,7 +112,7 @@ public function iCanBuildObjectWithObjectCollectionWithoutUseInConstructor() } return $arr; - })($data)); + })($data[\'list\'])); }', $blueprint); } } From aa5c23eecd7bc386dd27076c0b23a47da1924bb2 Mon Sep 17 00:00:00 2001 From: gardziejewskik Date: Tue, 24 Jul 2018 23:08:59 +0200 Subject: [PATCH 08/32] Add code standard analyse --- .gitignore | 4 +- .travis.yml | 4 +- composer.json | 36 +++++++++++++-- phpstan-test.neon | 6 +++ phpstan.neon | 4 ++ ruleset.xml | 45 +++++++++++++++++++ src/Builder.php | 2 +- src/Builder/Blueprint.php | 2 +- src/Builder/Blueprint/Factory.php | 2 +- .../Blueprint/Factory/CodeGenerator.php | 4 +- .../Factory/CodeGenerator/Anonymous.php | 22 ++++----- .../Blueprint/Factory/CodeGenerator/Node.php | 2 +- .../Factory/CodeGenerator/Node/Complex.php | 2 +- .../Factory/CodeGenerator/Node/ObjectList.php | 5 ++- .../Factory/CodeGenerator/Node/Scalar.php | 2 +- .../Factory/CodeGenerator/PhpDocParser.php | 12 ++--- .../Blueprint/Factory/CodeGenerator/Store.php | 2 +- .../Factory/CodeGenerator/Store/Memory.php | 2 +- src/Builder/ParameterNameStrategy.php | 2 +- src/Builder/ParameterNameStrategy/Simple.php | 4 +- .../ParameterNameStrategy/SnakeCase.php | 4 +- src/Builder/Reflection.php | 17 ++++--- src/BuilderException.php | 3 +- test/ListOfObjectsWithUseStmtConstructor.php | 2 +- ...houtUseButWithFQNTypedArrayConstructor.php | 2 +- ...ListOfObjectsWithoutUseStmtConstructor.php | 2 +- test/Object/SomeObject.php | 2 +- test/Object/SomeSecondObject.php | 2 +- test/SimpleMixedConstructor.php | 2 +- ...SimpleMixedConstructorWithDefaultValue.php | 2 +- test/SimpleScalarConstructor.php | 2 +- test/SomeAggregateRoot.php | 2 +- test/SomeObjectWithEmptyConstructor.php | 3 +- .../Factory/CodeGenerator/AnonymousTest.php | 10 ++--- .../unit/Builder/Blueprint/Factory/Simple.php | 2 +- test/unit/Builder/BlueprintTest.php | 4 +- test/unit/Builder/BuilderTest.php | 24 +++++----- .../ParameterNameStrategy/SimpleTest.php | 10 ++--- .../ParameterNameStrategy/SnakeCaseTest.php | 11 +++-- test/unit/Builder/ReflectionTest.php | 4 +- 40 files changed, 182 insertions(+), 92 deletions(-) create mode 100644 phpstan-test.neon create mode 100644 phpstan.neon create mode 100644 ruleset.xml diff --git a/.gitignore b/.gitignore index bf12543..3d1b147 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ composer.lock vendor -.idea \ No newline at end of file +.idea +.php_cs.cache +.phpcs.cache \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 4bc5f9a..7fa9d7e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,8 +30,8 @@ install: - stty cols 120 && composer show script: - - if [[ $TEST_COVERAGE == 'true' ]]; then composer test-coverage ; else composer test ; fi - - if [[ $CS_CHECK == 'true' ]]; then composer cs-check ; fi + - if [[ $TEST_COVERAGE == 'true' ]]; then composer test:unit:coverage ; else composer test ; fi + - if [[ $CS_CHECK == 'true' ]]; then composer cs:check ; fi after_script: - if [[ $TEST_COVERAGE == 'true' ]]; then vendor/bin/php-coveralls -v ; fi diff --git a/composer.json b/composer.json index 7a2c093..42b2e09 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,12 @@ "roave/better-reflection": "^3.0" }, "require-dev": { - "phpunit/phpunit": "^7.2" + "friendsofphp/php-cs-fixer": "^2.12", + "phpstan/phpstan": "^0.10.2", + "phpstan/phpstan-phpunit": "^0.10.0", + "phpstan/phpstan-strict-rules": "^0.10.1", + "phpunit/phpunit": "^7.2", + "slevomat/coding-standard": "^4.6" }, "autoload": { "psr-4": { @@ -28,7 +33,32 @@ "check": [ "@test" ], - "test": "phpunit --colors=always", - "test-coverage": "phpunit --colors=always --coverage-clover clover.xml" + "test:unit": "phpunit -c phpunit.xml --testsuite unit --colors=always ", + "test:unit:coverage": "phpunit -c phpunit.xml --testsuite unit --coverage-php test/result/unit.cov --colors=never", + "cs:phpstan": [ + "phpstan analyse --level=max --ansi --no-progress -c phpstan.neon src", + "phpstan analyse --level=max --ansi --no-progress -c phpstan-test.neon test" + ], + "cs:phpcs": [ + "phpcs --standard=ruleset.xml --extensions=php -v --ignore=.config.php src test --cache=.phpcs.cache" + ], + "cs:phpcs-fix": [ + "phpcbf --standard=ruleset.xml --extensions=php --ignore=.config.php src test --cache=.phpcs.cache" + ], + "cs:php-cs-fixer": [ + "php-cs-fixer fix src --allow-risky yes --diff --show-progress dots --verbose --dry-run" + ], + "cs:php-cs-fixer-fix": [ + "php-cs-fixer fix src --allow-risky yes --diff --show-progress dots --verbose" + ], + "cs:check": [ + "@cs:phpcs", + "@cs:php-cs-fixer", + "@cs:phpstan" + ], + "cs:fix": [ + "@cs:phpcs-fix", + "@cs:php-cs-fixer-fix" + ] } } diff --git a/phpstan-test.neon b/phpstan-test.neon new file mode 100644 index 0000000..f496094 --- /dev/null +++ b/phpstan-test.neon @@ -0,0 +1,6 @@ +parameters: + tmpDir: data/cache/phpstan-test +includes: + - vendor/phpstan/phpstan-phpunit/extension.neon + - vendor/phpstan/phpstan-phpunit/rules.neon + - vendor/phpstan/phpstan-strict-rules/rules.neon diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..79a46de --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,4 @@ +includes: + - vendor/phpstan/phpstan-strict-rules/rules.neon +parameters: + tmpDir: data/cache/phpstan diff --git a/ruleset.xml b/ruleset.xml new file mode 100644 index 0000000..7b97ac1 --- /dev/null +++ b/ruleset.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Builder.php b/src/Builder.php index fee1b06..faadd92 100644 --- a/src/Builder.php +++ b/src/Builder.php @@ -1,4 +1,4 @@ -generator->create($class); $prefix = 'getType()->getName(); - if ($type === 'array' + if ('array' === $type && $this->phpDocParser->isListOfObject($phpDoc, $parameter)) { $class = $this->phpDocParser->getListType($phpDoc, $parameter); @@ -127,9 +127,11 @@ private function getDefaultValues(Node $node) if ($node instanceof Complex) { foreach ($node->innerNodes() as $innerNode) { $innerNodeDefaultValues = $this->getDefaultValues($innerNode); - if(! empty($innerNodeDefaultValues)) { - $values[$innerNode->name()] = $innerNodeDefaultValues; + if (empty($innerNodeDefaultValues)) { + continue; } + + $values[$innerNode->name()] = $innerNodeDefaultValues; } } diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node.php index 63a5f96..6378fa2 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Node.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node.php @@ -1,4 +1,4 @@ -stmts as $node) { - if ($node instanceof Stmt\Use_) { - $uses[]= $node; + if (!($node instanceof Stmt\Use_)) { + continue; } + + $uses[]= $node; } return $uses; @@ -92,7 +94,7 @@ private function getUsesNamespaces(array $uses): array private function getFullClassName(string $name, array $namespaces, ReflectionClass $class): string { - if ($name[0] === '\\') { + if ('\\' === $name[0]) { return $name; } @@ -122,6 +124,6 @@ private function endsWith(string $haystack, string $needle): bool { $length = strlen($needle); - return $length === 0 || (substr($haystack, -$length) === $needle); + return 0 === $length || (substr($haystack, -$length) === $needle); } } diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Store.php b/src/Builder/Blueprint/Factory/CodeGenerator/Store.php index aaaddbf..3f85fc4 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Store.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Store.php @@ -1,4 +1,4 @@ -getUseStmts($namespace); $namespaces = $this->getUsesNamespaces($uses); - foreach($data as $objectConstructorData) { + foreach ($data as $objectConstructorData) { $list[] = $this->build( $this->getFullClassName($type->name, $namespaces, $constructor->getDeclaringClass()), - $objectConstructorData); + $objectConstructorData + ); } return $list; @@ -146,9 +147,11 @@ private function getUseStmts(Stmt\Namespace_ $node): array { $uses = []; foreach ($node->stmts as $node) { - if ($node instanceof Stmt\Use_) { - $uses[]= $node; + if (!($node instanceof Stmt\Use_)) { + continue; } + + $uses[]= $node; } return $uses; @@ -170,7 +173,7 @@ private function getUsesNamespaces(array $uses): array private function getFullClassName(string $name, array $namespaces, ReflectionClass $class): string { - if ($name[0] === '\\') { + if ('\\' === $name[0]) { return $name; } @@ -200,6 +203,6 @@ private function endsWith(string $haystack, string $needle): bool { $length = strlen($needle); - return $length === 0 || (substr($haystack, -$length) === $needle); + return 0 === $length || (substr($haystack, -$length) === $needle); } } diff --git a/src/BuilderException.php b/src/BuilderException.php index 5bee453..d8105b9 100644 --- a/src/BuilderException.php +++ b/src/BuilderException.php @@ -1,4 +1,4 @@ -create($class); $this->assertSame( -' 'some string', @@ -40,7 +38,7 @@ public function iCanBuildSimpleObjectWithScalarValuesInConstructor() } /** @test */ - public function iCanBuildSimpleObjectWithScalarAndObjectValuesInConstructor() + public function iCanBuildSimpleObjectWithScalarAndObjectValuesInConstructor(): void { $data = [ 'someString' => 'some string', @@ -59,7 +57,7 @@ public function iCanBuildSimpleObjectWithScalarAndObjectValuesInConstructor() } /** @test */ - public function iCanBuildSimpleObjectWithDefaultValuesInConstructor() + public function iCanBuildSimpleObjectWithDefaultValuesInConstructor(): void { $data = [ 'someObject' => [], @@ -76,7 +74,7 @@ public function iCanBuildSimpleObjectWithDefaultValuesInConstructor() } /** @test */ - public function iCanBuildObjectWithObjectCollectionWithoutUseInConstructor() + public function iCanBuildObjectWithObjectCollectionWithoutUseInConstructor(): void { $data = [ 'list' => [ @@ -97,13 +95,13 @@ public function iCanBuildObjectWithObjectCollectionWithoutUseInConstructor() $this->assertInstanceOf(ListOfObjectsWithoutUseStmtConstructor::class, $object); $this->assertCount(2, $object->list); - foreach($object->list as $element) { + foreach ($object->list as $element) { $this->assertInstanceOf(SimpleScalarConstructor::class, $element); } } /** @test */ - public function iCanBuildObjectWithObjectCollectionWithUseInConstructor() + public function iCanBuildObjectWithObjectCollectionWithUseInConstructor(): void { $data = [ 'list' => [ @@ -118,13 +116,13 @@ public function iCanBuildObjectWithObjectCollectionWithUseInConstructor() $this->assertInstanceOf(ListOfObjectsWithUseStmtConstructor::class, $object); $this->assertCount(2, $object->list); - foreach($object->list as $element) { + foreach ($object->list as $element) { $this->assertInstanceOf(SomeSecondObject::class, $element); } } /** @test */ - public function iCanBuildObjectWithObjectCollectionWithoutUseButWithFQNTypedArrayInConstructor() + public function iCanBuildObjectWithObjectCollectionWithoutUseButWithFQNTypedArrayInConstructor(): void { $data = [ 'list' => [ @@ -139,13 +137,13 @@ public function iCanBuildObjectWithObjectCollectionWithoutUseButWithFQNTypedArra $this->assertInstanceOf(ListOfObjectsWithoutUseButWithFQNTypedArrayConstructor::class, $object); $this->assertCount(2, $object->list); - foreach($object->list as $element) { + foreach ($object->list as $element) { $this->assertInstanceOf(SomeObject::class, $element); } } /** @test */ - public function iCanBuildAdvancedObjectHierarchy() + public function iCanBuildAdvancedObjectHierarchy(): void { $data = [ 'someString' => 'some string3', diff --git a/test/unit/Builder/ParameterNameStrategy/SimpleTest.php b/test/unit/Builder/ParameterNameStrategy/SimpleTest.php index a288125..c61ee92 100644 --- a/test/unit/Builder/ParameterNameStrategy/SimpleTest.php +++ b/test/unit/Builder/ParameterNameStrategy/SimpleTest.php @@ -1,4 +1,4 @@ -isFulfilled($string); @@ -30,7 +30,7 @@ public function simpleStrategyReturnTrueForStringsWithoutUnderscoreMinusAndSpace * @test * @dataProvider invalidStrings */ - public function simpleStrategyReturnFalseForStringsWitUnderscoreOrMinusOrSpace(string $string) + public function simpleStrategyReturnFalseForStringsWitUnderscoreOrMinusOrSpace(string $string): void { $isFulfilled = static::$strategy->isFulfilled($string); @@ -38,7 +38,7 @@ public function simpleStrategyReturnFalseForStringsWitUnderscoreOrMinusOrSpace(s } /** @test */ - public function simpleStrategyReturnGivenParameterWithoutModification() + public function simpleStrategyReturnGivenParameterWithoutModification(): void { $string = static::$strategy->getName('simpleCamelCase'); diff --git a/test/unit/Builder/ParameterNameStrategy/SnakeCaseTest.php b/test/unit/Builder/ParameterNameStrategy/SnakeCaseTest.php index ac8ec64..f2c6d8d 100644 --- a/test/unit/Builder/ParameterNameStrategy/SnakeCaseTest.php +++ b/test/unit/Builder/ParameterNameStrategy/SnakeCaseTest.php @@ -1,9 +1,8 @@ -isFulfilled($string); @@ -31,7 +30,7 @@ public function snakeCaseStrategyReturnTrueForStringsOnlyInSneakCaseFormat(strin * @test * @dataProvider invalidStrings */ - public function snakeCaseStrategyReturnFalseForStringsWitUnderscoreOrSpace(string $string) + public function snakeCaseStrategyReturnFalseForStringsWitUnderscoreOrSpace(string $string): void { $isFulfilled = static::$strategy->isFulfilled($string); @@ -39,7 +38,7 @@ public function snakeCaseStrategyReturnFalseForStringsWitUnderscoreOrSpace(strin } /** @test */ - public function snakeCaseStrategyReturnGivenParameterAsCamelCase() + public function snakeCaseStrategyReturnGivenParameterAsCamelCase(): void { $string = static::$strategy->getName('simple_snake_case'); diff --git a/test/unit/Builder/ReflectionTest.php b/test/unit/Builder/ReflectionTest.php index f9ef8c1..ebc15f8 100644 --- a/test/unit/Builder/ReflectionTest.php +++ b/test/unit/Builder/ReflectionTest.php @@ -1,4 +1,4 @@ - Date: Wed, 25 Jul 2018 22:24:29 +0200 Subject: [PATCH 09/32] Fix coding standards --- .gitignore | 3 +- composer.json | 3 +- ruleset.xml | 2 +- src/Builder.php | 1 + src/Builder/Blueprint.php | 2 + .../Factory/CodeGenerator/Anonymous.php | 15 ++--- .../Blueprint/Factory/CodeGenerator/Node.php | 5 ++ .../Factory/CodeGenerator/Node/Complex.php | 4 +- .../Factory/CodeGenerator/Node/ObjectList.php | 1 + .../Factory/CodeGenerator/PhpDocParser.php | 41 ++++++------ .../Factory/CodeGenerator/Store/Memory.php | 3 +- src/Builder/Reflection.php | 67 +++++++++++-------- ...BuilderException.php => BuildingError.php} | 2 +- .../Factory/CodeGenerator/AnonymousTest.php | 12 +++- 14 files changed, 95 insertions(+), 66 deletions(-) rename src/{BuilderException.php => BuildingError.php} (65%) diff --git a/.gitignore b/.gitignore index 3d1b147..70b2591 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ composer.lock vendor .idea .php_cs.cache -.phpcs.cache \ No newline at end of file +.phpcs.cache +data \ No newline at end of file diff --git a/composer.json b/composer.json index 42b2e09..32c6267 100644 --- a/composer.json +++ b/composer.json @@ -36,8 +36,7 @@ "test:unit": "phpunit -c phpunit.xml --testsuite unit --colors=always ", "test:unit:coverage": "phpunit -c phpunit.xml --testsuite unit --coverage-php test/result/unit.cov --colors=never", "cs:phpstan": [ - "phpstan analyse --level=max --ansi --no-progress -c phpstan.neon src", - "phpstan analyse --level=max --ansi --no-progress -c phpstan-test.neon test" + "phpstan analyse --level=max --ansi --no-progress -c phpstan.neon src" ], "cs:phpcs": [ "phpcs --standard=ruleset.xml --extensions=php -v --ignore=.config.php src test --cache=.phpcs.cache" diff --git a/ruleset.xml b/ruleset.xml index 7b97ac1..2647d87 100644 --- a/ruleset.xml +++ b/ruleset.xml @@ -17,7 +17,7 @@ diff --git a/src/Builder.php b/src/Builder.php index faadd92..7484229 100644 --- a/src/Builder.php +++ b/src/Builder.php @@ -4,5 +4,6 @@ interface Builder { + /** @param mixed[] $data */ public function build(string $class, array $data): object; } diff --git a/src/Builder/Blueprint.php b/src/Builder/Blueprint.php index dea0c83..54c9a0e 100644 --- a/src/Builder/Blueprint.php +++ b/src/Builder/Blueprint.php @@ -6,6 +6,7 @@ final class Blueprint implements Builder { + /** @var Builder\Blueprint\Factory */ private $blueprintFactory; public function __construct(Builder\Blueprint\Factory $factory) @@ -13,6 +14,7 @@ public function __construct(Builder\Blueprint\Factory $factory) $this->blueprintFactory = $factory; } + /** @param mixed[] $data */ public function build(string $class, array $data): object { $blueprint = $this->blueprintFactory->create($class); diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Anonymous.php b/src/Builder/Blueprint/Factory/CodeGenerator/Anonymous.php index 8809b78..59c97e9 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Anonymous.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Anonymous.php @@ -67,12 +67,11 @@ private function getNodes(ReflectionMethod $method, string $name = ''): Node $class = $parameter->getClass(); if (null === $class) { - $node->add( - $this->createNode( - $parameter, - $method->getDocComment() ? $method->getDocComment() : '' - ) - ); + $comment = (bool) $method->getDocComment() + ? (string) $method->getDocComment() + : ''; + $node->add($this->createNode($parameter, $comment)); + continue; } @@ -109,7 +108,7 @@ private function getDefaultSection(Node $node): string { $defaultSection = ''; $defaultValues = $this->getDefaultValues($node); - if (! empty($defaultValues)) { + if ([] !== $defaultValues) { $defaultSection = sprintf( self::DEFAULT_VALUES_PATTERN, var_export($defaultValues, true) @@ -127,7 +126,7 @@ private function getDefaultValues(Node $node) if ($node instanceof Complex) { foreach ($node->innerNodes() as $innerNode) { $innerNodeDefaultValues = $this->getDefaultValues($innerNode); - if (empty($innerNodeDefaultValues)) { + if ([] === $innerNodeDefaultValues) { continue; } diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node.php index 6378fa2..76c734b 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Node.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node.php @@ -4,10 +4,14 @@ abstract class Node { + /** @var string */ protected $name; + /** @var mixed|null */ private $defaultValue; + /** @var string */ private $type; + /** @param mixed $defaultValue */ public function __construct(string $type, string $name, $defaultValue = null) { $this->name = $name; @@ -30,6 +34,7 @@ public function withDefaultValue(): bool return null !== $this->defaultValue; } + /** @return mixed */ public function defaultValue() { return $this->defaultValue; diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php index 344327d..742efab 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php @@ -6,7 +6,7 @@ final class Complex extends Node { - /** @var Node */ + /** @var Node[] */ private $nodes = []; public function add(Node $node): void @@ -26,7 +26,7 @@ public function __toString(): string foreach ($this->nodes as $node) { if ($node instanceof Scalar) { $nodeSerialized = (string) $node; - if (! empty($this->name())) { + if ('' !== $this->name()) { $nodeSerialized = '[\'' . $this->name() . '\']' . $nodeSerialized; } $nodes[] = '$data' . $nodeSerialized; diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node/ObjectList.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node/ObjectList.php index ebe4280..954c96e 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Node/ObjectList.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node/ObjectList.php @@ -6,6 +6,7 @@ final class ObjectList extends Node { + /** @var Node */ private $objectNode; public function __construct(string $name, Node $objectNode) diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/PhpDocParser.php b/src/Builder/Blueprint/Factory/CodeGenerator/PhpDocParser.php index 58ce7a5..e06f386 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/PhpDocParser.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/PhpDocParser.php @@ -11,7 +11,7 @@ use ReflectionClass; use ReflectionParameter; use Roave\BetterReflection\BetterReflection; -use RstGroup\ObjectBuilder\BuilderException; +use RstGroup\ObjectBuilder\BuildingError; class PhpDocParser { @@ -39,12 +39,20 @@ public function getListType(string $comment, ReflectionParameter $parameter): st $type = $node->type->type; $parser = (new BetterReflection())->phpParser(); - $parsedFile = $parser->parse(file_get_contents($parameter->getDeclaringClass()->getFileName())); + /** @var ReflectionClass $class */ + $class = $parameter->getDeclaringClass(); + /** @var string $fileName */ + $fileName = $class->getFileName(); + /** @var string $phpFileContent */ + $phpFileContent = file_get_contents($fileName); + /** @var Stmt[] $parsedFile */ + $parsedFile = $parser->parse($phpFileContent); + $namespace = $this->getNamespaceStmt($parsedFile); $uses = $this->getUseStmts($namespace); $namespaces = $this->getUsesNamespaces($uses); - return $this->getFullClassName($type->name, $namespaces, $parameter->getDeclaringClass()); + return $this->getFullClassName($type->name, $namespaces, $class); } } } @@ -66,16 +74,9 @@ private function getNamespaceStmt(array $nodes): Stmt\Namespace_ /** @return Stmt\Use_[] */ private function getUseStmts(Stmt\Namespace_ $node): array { - $uses = []; - foreach ($node->stmts as $node) { - if (!($node instanceof Stmt\Use_)) { - continue; - } - - $uses[]= $node; - } - - return $uses; + return array_filter($node->stmts, function (Stmt $node): bool { + return $node instanceof Stmt\Use_; + }); } /** @@ -84,14 +85,12 @@ private function getUseStmts(Stmt\Namespace_ $node): array */ private function getUsesNamespaces(array $uses): array { - $names = []; - foreach ($uses as $use) { - $names[] = $use->uses[0]->name->toString(); - } - - return $names; + return array_map(function (Stmt\Use_ $use): string { + return $use->uses[0]->name->toString(); + }, $uses); } + /** @param string[] $namespaces */ private function getFullClassName(string $name, array $namespaces, ReflectionClass $class): string { if ('\\' === $name[0]) { @@ -107,7 +106,7 @@ private function getFullClassName(string $name, array $namespaces, ReflectionCla /** * @param string[] $namespaces - * @throws BuilderException + * @throws BuildingError */ private function getNamespaceForClass(string $className, array $namespaces): string { @@ -117,7 +116,7 @@ private function getNamespaceForClass(string $className, array $namespaces): str } } - throw new BuilderException('Can not resolve namespace for class ' . $className); + throw new BuildingError('Can not resolve namespace for class ' . $className); } private function endsWith(string $haystack, string $needle): bool diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Store/Memory.php b/src/Builder/Blueprint/Factory/CodeGenerator/Store/Memory.php index 61096b4..4ac839b 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Store/Memory.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Store/Memory.php @@ -7,9 +7,10 @@ final class Memory implements Store { + /** @var string[] */ private $store = []; - public function get(string $class): callable + public function get(string $class): ?callable { try { return eval($this->store[$class]); diff --git a/src/Builder/Reflection.php b/src/Builder/Reflection.php index 8de783b..4dc227f 100644 --- a/src/Builder/Reflection.php +++ b/src/Builder/Reflection.php @@ -13,11 +13,12 @@ use ReflectionParameter; use Roave\BetterReflection\BetterReflection; use RstGroup\ObjectBuilder\Builder; -use RstGroup\ObjectBuilder\BuilderException; +use RstGroup\ObjectBuilder\BuildingError; use Throwable; final class Reflection implements Builder { + /** @var ParameterNameStrategy */ private $parameterNameStrategy; public function __construct(ParameterNameStrategy $parameterNameStrategy) @@ -27,24 +28,32 @@ public function __construct(ParameterNameStrategy $parameterNameStrategy) /** * @param mixed[] $data - * @throws BuilderException + * @throws BuildingError */ public function build(string $class, array $data): object { try { $classReflection = new ReflectionClass($class); - /** @var ReflectionMethod $constructorMethod */ $constructor = $classReflection->getConstructor(); + $parameters = []; - $parameters = iterator_to_array($this->collect($constructor, $data)); + if (null !== $constructor) { + /** @var \Traversable $iterator */ + $iterator = $this->collect($constructor, $data); + $parameters = iterator_to_array($iterator); + } return new $class(...$parameters); } catch (Throwable $exception) { - throw new BuilderException('Cant build object', 0, $exception); + throw new BuildingError('Cant build object', 0, $exception); } } + /** + * @param mixed[] $data + * @return mixed[] + */ private function collect(ReflectionMethod $constructor, array $data): iterable { foreach ($constructor->getParameters() as $parameter) { @@ -66,6 +75,7 @@ private function collect(ReflectionMethod $constructor, array $data): iterable } } + /** @param mixed[] $data */ private function parameterDataIsInData(string $parameterName, array $data): bool { foreach (array_keys($data) as $key) { @@ -87,12 +97,13 @@ private function buildParameter(ReflectionParameter $parameter, $data, Reflectio if (null !== $class) { $name = $class->getName(); - /** @var ReflectionMethod $constructorMethod */ $constructorMethod = $class->getConstructor(); $parameters = []; if (null !== $constructorMethod) { - $parameters = iterator_to_array($this->collect($constructorMethod, $data)); + /** @var \Traversable $iterator */ + $iterator = $this->collect($constructorMethod, $data); + $parameters = iterator_to_array($iterator); } return new $name(...$parameters); @@ -100,15 +111,24 @@ private function buildParameter(ReflectionParameter $parameter, $data, Reflectio if ($parameter->isArray()) { $parser = new PhpDocParser(new TypeParser(), new ConstExprParser()); - $node = $parser->parse(new TokenIterator((new Lexer())->tokenize($constructor->getDocComment()))); + /** @var string $comment */ + $comment = $constructor->getDocComment(); + $node = $parser->parse(new TokenIterator((new Lexer())->tokenize($comment))); foreach ($node->getParamTagValues() as $node) { if ($node->parameterName === '$' . $parameter->getName()) { $type = $node->type->type; $list = []; - $parser = (new BetterReflection())->phpParser(); - $parsedFile = $parser->parse(file_get_contents($constructor->getDeclaringClass()->getFileName())); + /** @var ReflectionClass $class */ + $class = $parameter->getDeclaringClass(); + /** @var string $fileName */ + $fileName = $class->getFileName(); + /** @var string $phpFileContent */ + $phpFileContent = file_get_contents($fileName); + /** @var Stmt[] $parsedFile */ + $parsedFile = $parser->parse($phpFileContent); + $namespace = $this->getNamespaceStmt($parsedFile); $uses = $this->getUseStmts($namespace); $namespaces = $this->getUsesNamespaces($uses); @@ -145,16 +165,9 @@ private function getNamespaceStmt(array $nodes): Stmt\Namespace_ /** @return Stmt\Use_[] */ private function getUseStmts(Stmt\Namespace_ $node): array { - $uses = []; - foreach ($node->stmts as $node) { - if (!($node instanceof Stmt\Use_)) { - continue; - } - - $uses[]= $node; - } - - return $uses; + return array_filter($node->stmts, function (Stmt $node): bool { + return $node instanceof Stmt\Use_; + }); } /** @@ -163,14 +176,12 @@ private function getUseStmts(Stmt\Namespace_ $node): array */ private function getUsesNamespaces(array $uses): array { - $names = []; - foreach ($uses as $use) { - $names[] = $use->uses[0]->name->toString(); - } - - return $names; + return array_map(function (Stmt\Use_ $use): string { + return $use->uses[0]->name->toString(); + }, $uses); } + /** @param string[] $namespaces */ private function getFullClassName(string $name, array $namespaces, ReflectionClass $class): string { if ('\\' === $name[0]) { @@ -186,7 +197,7 @@ private function getFullClassName(string $name, array $namespaces, ReflectionCla /** * @param string[] $namespaces - * @throws BuilderException + * @throws BuildingError */ private function getNamespaceForClass(string $className, array $namespaces): string { @@ -196,7 +207,7 @@ private function getNamespaceForClass(string $className, array $namespaces): str } } - throw new BuilderException('Can not resolve namespace for class ' . $className); + throw new BuildingError('Can not resolve namespace for class ' . $className); } private function endsWith(string $haystack, string $needle): bool diff --git a/src/BuilderException.php b/src/BuildingError.php similarity index 65% rename from src/BuilderException.php rename to src/BuildingError.php index d8105b9..c73b292 100644 --- a/src/BuilderException.php +++ b/src/BuildingError.php @@ -4,6 +4,6 @@ use Exception; -final class BuilderException extends Exception +final class BuildingError extends Exception { } diff --git a/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php b/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php index 147e3a8..038362e 100644 --- a/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php +++ b/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php @@ -39,6 +39,7 @@ public function iCanBuildSimpleObjectWithScalarValuesInConstructor(): void $blueprint = $factory->create($class); +// @codingStandardsIgnoreStart $this->assertSame( 'create($class); +// @codingStandardsIgnoreStart $this->assertSame( 'create($class); +// @codingStandardsIgnoreStart $this->assertSame( 'create($class); +// @codingStandardsIgnoreStart $this->assertSame(' Date: Wed, 25 Jul 2018 22:41:48 +0200 Subject: [PATCH 10/32] Refactoring and code cleaning --- .../Blueprint/Factory/CodeGenerator.php | 7 ++++--- .../Factory/CodeGenerator/PatternGenerator.php | 8 ++++++++ .../{ => PatternGenerator}/Anonymous.php | 11 +++++++---- src/PhpDocParser.php | 18 ++++++++++++++++++ .../PhpStan.php} | 5 +++-- .../Factory/CodeGenerator/AnonymousTest.php | 13 +++++++------ test/unit/Builder/BlueprintTest.php | 7 ++++++- 7 files changed, 53 insertions(+), 16 deletions(-) create mode 100644 src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator.php rename src/Builder/Blueprint/Factory/CodeGenerator/{ => PatternGenerator}/Anonymous.php (91%) create mode 100644 src/PhpDocParser.php rename src/{Builder/Blueprint/Factory/CodeGenerator/PhpDocParser.php => PhpDocParser/PhpStan.php} (96%) diff --git a/src/Builder/Blueprint/Factory/CodeGenerator.php b/src/Builder/Blueprint/Factory/CodeGenerator.php index 81411b7..b6bf553 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator.php @@ -3,15 +3,16 @@ namespace RstGroup\ObjectBuilder\Builder\Blueprint\Factory; use RstGroup\ObjectBuilder\Builder\Blueprint\Factory; +use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\PatternGenerator; final class CodeGenerator implements Factory { - /** @var Factory\CodeGenerator\Anonymous */ + /** @var PatternGenerator */ private $generator; - public function __construct() + public function __construct(PatternGenerator $generator) { - $this->generator = new Factory\CodeGenerator\Anonymous(); + $this->generator = $generator; } public function create(string $class): callable diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator.php b/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator.php new file mode 100644 index 0000000..21574e2 --- /dev/null +++ b/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator.php @@ -0,0 +1,8 @@ +phpDocParser = new PhpDocParser(); + $this->phpDocParser = $parser; } public function create(string $class): string diff --git a/src/PhpDocParser.php b/src/PhpDocParser.php new file mode 100644 index 0000000..e83cdff --- /dev/null +++ b/src/PhpDocParser.php @@ -0,0 +1,18 @@ +create($class); @@ -34,7 +35,7 @@ public function iCanGenerateSimpleObjectClosure(): void /** @test */ public function iCanBuildSimpleObjectWithScalarValuesInConstructor(): void { - $factory = new Anonymous(); + $factory = new Anonymous(new PhpStan()); $class = SimpleScalarConstructor::class; $blueprint = $factory->create($class); @@ -55,7 +56,7 @@ public function iCanBuildSimpleObjectWithScalarValuesInConstructor(): void /** @test */ public function iCanBuildSimpleObjectWithDefaultValuesInConstructor(): void { - $factory = new Anonymous(); + $factory = new Anonymous(new PhpStan()); $class = SimpleMixedConstructorWithDefaultValue::class; $blueprint = $factory->create($class); @@ -81,7 +82,7 @@ public function iCanBuildSimpleObjectWithDefaultValuesInConstructor(): void /** @test */ public function iCanBuildAdvancedObjectHierarchy(): void { - $factory = new Anonymous(); + $factory = new Anonymous(new PhpStan()); $class = SomeAggregateRoot::class; $blueprint = $factory->create($class); @@ -102,7 +103,7 @@ public function iCanBuildAdvancedObjectHierarchy(): void /** @test */ public function iCanBuildObjectWithObjectCollectionWithoutUseInConstructor(): void { - $factory = new Anonymous(); + $factory = new Anonymous(new PhpStan()); $class = ListOfObjectsWithoutUseStmtConstructor::class; $blueprint = $factory->create($class); diff --git a/test/unit/Builder/BlueprintTest.php b/test/unit/Builder/BlueprintTest.php index 75c1b27..246e5d2 100644 --- a/test/unit/Builder/BlueprintTest.php +++ b/test/unit/Builder/BlueprintTest.php @@ -3,13 +3,18 @@ namespace RstGroup\ObjectBuilder\Test\unit\Builder; use RstGroup\ObjectBuilder\Builder\Blueprint; +use RstGroup\ObjectBuilder\PhpDocParser\PhpStan; class BlueprintTest extends BuilderTest { public static function setUpBeforeClass(): void { static::$builder = new Blueprint( - new Blueprint\Factory\CodeGenerator() + new Blueprint\Factory\CodeGenerator( + new Blueprint\Factory\CodeGenerator\PatternGenerator\Anonymous( + new PhpStan() + ) + ) ); } } From f423631f19f4328936058b6457cae4bc88029849 Mon Sep 17 00:00:00 2001 From: gardziejewskik Date: Wed, 25 Jul 2018 22:52:08 +0200 Subject: [PATCH 11/32] Fix travis --- .travis.yml | 4 ++-- composer.json | 31 ++++++++++++++----------------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7fa9d7e..4660319 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,8 +30,8 @@ install: - stty cols 120 && composer show script: - - if [[ $TEST_COVERAGE == 'true' ]]; then composer test:unit:coverage ; else composer test ; fi - - if [[ $CS_CHECK == 'true' ]]; then composer cs:check ; fi + - if [[ $CS_CHECK == 'true' ]]; then composer cs-check ; fi + - if [[ $TEST_COVERAGE == 'true' ]]; then composer test-unit-coverage ; else composer test ; fi after_script: - if [[ $TEST_COVERAGE == 'true' ]]; then vendor/bin/php-coveralls -v ; fi diff --git a/composer.json b/composer.json index 32c6267..0a32620 100644 --- a/composer.json +++ b/composer.json @@ -30,34 +30,31 @@ "sort-packages": true }, "scripts": { - "check": [ - "@test" - ], - "test:unit": "phpunit -c phpunit.xml --testsuite unit --colors=always ", - "test:unit:coverage": "phpunit -c phpunit.xml --testsuite unit --coverage-php test/result/unit.cov --colors=never", - "cs:phpstan": [ + "test-unit": "phpunit -c phpunit.xml --testsuite unit --colors=always ", + "test-unit-coverage": "phpunit -c phpunit.xml --testsuite unit --coverage-php test/result/unit.cov --colors=never", + "cs-phpstan": [ "phpstan analyse --level=max --ansi --no-progress -c phpstan.neon src" ], - "cs:phpcs": [ + "cs-phpcs": [ "phpcs --standard=ruleset.xml --extensions=php -v --ignore=.config.php src test --cache=.phpcs.cache" ], - "cs:phpcs-fix": [ + "cs-phpcs-fix": [ "phpcbf --standard=ruleset.xml --extensions=php --ignore=.config.php src test --cache=.phpcs.cache" ], - "cs:php-cs-fixer": [ + "cs-php-cs-fixer": [ "php-cs-fixer fix src --allow-risky yes --diff --show-progress dots --verbose --dry-run" ], - "cs:php-cs-fixer-fix": [ + "cs-php-cs-fixer-fix": [ "php-cs-fixer fix src --allow-risky yes --diff --show-progress dots --verbose" ], - "cs:check": [ - "@cs:phpcs", - "@cs:php-cs-fixer", - "@cs:phpstan" + "cs-check": [ + "@cs-phpcs", + "@cs-php-cs-fixer", + "@cs-phpstan" ], - "cs:fix": [ - "@cs:phpcs-fix", - "@cs:php-cs-fixer-fix" + "cs-fix": [ + "@cs-phpcs-fix", + "@cs-php-cs-fixer-fix" ] } } From 20b5e326c71bb854d5011454b6a9c84632fc78df Mon Sep 17 00:00:00 2001 From: gardziejewskik Date: Wed, 25 Jul 2018 22:55:03 +0200 Subject: [PATCH 12/32] Fix travis --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4660319..1653ce2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,8 +30,8 @@ install: - stty cols 120 && composer show script: - - if [[ $CS_CHECK == 'true' ]]; then composer cs-check ; fi - - if [[ $TEST_COVERAGE == 'true' ]]; then composer test-unit-coverage ; else composer test ; fi + - if [[ $CS_CHECK == 'true' ]]; then composer 'cs-check' ; fi + - if [[ $TEST_COVERAGE == 'true' ]]; then composer 'test-unit-coverage' ; else composer test ; fi after_script: - if [[ $TEST_COVERAGE == 'true' ]]; then vendor/bin/php-coveralls -v ; fi From b9b3db2ec806a71763314f79e7a4727f88bb7e66 Mon Sep 17 00:00:00 2001 From: gardziejewskik Date: Wed, 25 Jul 2018 22:57:25 +0200 Subject: [PATCH 13/32] Fix travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1653ce2..236e923 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,7 +31,7 @@ install: script: - if [[ $CS_CHECK == 'true' ]]; then composer 'cs-check' ; fi - - if [[ $TEST_COVERAGE == 'true' ]]; then composer 'test-unit-coverage' ; else composer test ; fi + - if [[ $TEST_COVERAGE == 'true' ]]; then composer 'test-unit-coverage' ; else composer 'test-unit' ; fi after_script: - if [[ $TEST_COVERAGE == 'true' ]]; then vendor/bin/php-coveralls -v ; fi From ff97ad3aeed1492564640f5e478f0f2fa39ffb6e Mon Sep 17 00:00:00 2001 From: gardziejewskik Date: Thu, 26 Jul 2018 22:24:29 +0200 Subject: [PATCH 14/32] Refactor with remove unnecessary dependency --- composer.json | 3 +- src/PhpDocParser/PhpStan.php | 24 +++++++----- .../Factory/CodeGenerator/AnonymousTest.php | 38 ++++++++++++++----- test/unit/Builder/BlueprintTest.php | 15 +++++++- 4 files changed, 58 insertions(+), 22 deletions(-) diff --git a/composer.json b/composer.json index 0a32620..5952b48 100644 --- a/composer.json +++ b/composer.json @@ -5,8 +5,7 @@ "keywords": [], "require": { "php": "^7.2", - "phpstan/phpdoc-parser": "^0.3.0", - "roave/better-reflection": "^3.0" + "phpstan/phpdoc-parser": "^0.3.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^2.12", diff --git a/src/PhpDocParser/PhpStan.php b/src/PhpDocParser/PhpStan.php index 5518d2f..1ec774c 100644 --- a/src/PhpDocParser/PhpStan.php +++ b/src/PhpDocParser/PhpStan.php @@ -3,23 +3,31 @@ namespace RstGroup\ObjectBuilder\PhpDocParser; use PhpParser\Node\Stmt; +use PhpParser\Parser; use PHPStan\PhpDocParser\Lexer\Lexer; -use PHPStan\PhpDocParser\Parser\ConstExprParser; use PHPStan\PhpDocParser\Parser\PhpDocParser as PhpStanPhpDocParser; use PHPStan\PhpDocParser\Parser\TokenIterator; -use PHPStan\PhpDocParser\Parser\TypeParser; use ReflectionClass; use ReflectionParameter; -use Roave\BetterReflection\BetterReflection; use RstGroup\ObjectBuilder\BuildingError; use RstGroup\ObjectBuilder\PhpDocParser; final class PhpStan implements PhpDocParser { + private $phpDocParser; + private $phpParser; + + public function __construct( + PhpStanPhpDocParser $phpDocParser, + Parser $phpParser + ) { + $this->phpDocParser = $phpDocParser; + $this->phpParser = $phpParser; + } + public function isListOfObject(string $comment, ReflectionParameter $parameter): bool { - $parser = new PhpStanPhpDocParser(new TypeParser(), new ConstExprParser()); - $node = $parser->parse(new TokenIterator((new Lexer())->tokenize($comment))); + $node = $this->phpDocParser->parse(new TokenIterator((new Lexer())->tokenize($comment))); foreach ($node->getParamTagValues() as $node) { if ($node->parameterName === '$' . $parameter->getName()) { @@ -32,13 +40,11 @@ public function isListOfObject(string $comment, ReflectionParameter $parameter): public function getListType(string $comment, ReflectionParameter $parameter): string { - $parser = new PhpStanPhpDocParser(new TypeParser(), new ConstExprParser()); - $node = $parser->parse(new TokenIterator((new Lexer())->tokenize($comment))); + $node = $this->phpDocParser->parse(new TokenIterator((new Lexer())->tokenize($comment))); foreach ($node->getParamTagValues() as $node) { if ($node->parameterName === '$' . $parameter->getName()) { $type = $node->type->type; - $parser = (new BetterReflection())->phpParser(); /** @var ReflectionClass $class */ $class = $parameter->getDeclaringClass(); @@ -47,7 +53,7 @@ public function getListType(string $comment, ReflectionParameter $parameter): st /** @var string $phpFileContent */ $phpFileContent = file_get_contents($fileName); /** @var Stmt[] $parsedFile */ - $parsedFile = $parser->parse($phpFileContent); + $parsedFile = $this->phpParser->parse($phpFileContent); $namespace = $this->getNamespaceStmt($parsedFile); $uses = $this->getUseStmts($namespace); diff --git a/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php b/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php index a5490e0..d83d1b0 100644 --- a/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php +++ b/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php @@ -2,6 +2,11 @@ namespace RstGroup\ObjectBuilder\Test\unit\Builder\Blueprint\Factory\CodeGenerator; +use PhpParser\Lexer\Emulative; +use PhpParser\ParserFactory; +use PHPStan\PhpDocParser\Parser\ConstExprParser; +use PHPStan\PhpDocParser\Parser\PhpDocParser; +use PHPStan\PhpDocParser\Parser\TypeParser; use PHPUnit\Framework\TestCase; use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\PatternGenerator\Anonymous; use RstGroup\ObjectBuilder\PhpDocParser\PhpStan; @@ -13,13 +18,30 @@ class AnonymousTest extends TestCase { + /** @var Anonymous */ + private static $factory; + + public static function setUpBeforeClass() + { + self::$factory = new Anonymous( + new PhpStan( + new PhpDocParser( + new TypeParser(), + new ConstExprParser() + ), + (new ParserFactory())->create(ParserFactory::PREFER_PHP7, new Emulative([ + 'usedAttributes' => ['comments', 'startLine', 'endLine', 'startFilePos', 'endFilePos'], + ])) + ) + ); + } + /** @test */ public function iCanGenerateSimpleObjectClosure(): void { - $factory = new Anonymous(new PhpStan()); $class = SomeObjectWithEmptyConstructor::class; - $blueprint = $factory->create($class); + $blueprint = self::$factory->create($class); $this->assertSame( 'create($class); + $blueprint = self::$factory->create($class); // @codingStandardsIgnoreStart $this->assertSame( @@ -56,10 +77,9 @@ public function iCanBuildSimpleObjectWithScalarValuesInConstructor(): void /** @test */ public function iCanBuildSimpleObjectWithDefaultValuesInConstructor(): void { - $factory = new Anonymous(new PhpStan()); $class = SimpleMixedConstructorWithDefaultValue::class; - $blueprint = $factory->create($class); + $blueprint = self::$factory->create($class); // @codingStandardsIgnoreStart $this->assertSame( @@ -82,10 +102,9 @@ public function iCanBuildSimpleObjectWithDefaultValuesInConstructor(): void /** @test */ public function iCanBuildAdvancedObjectHierarchy(): void { - $factory = new Anonymous(new PhpStan()); $class = SomeAggregateRoot::class; - $blueprint = $factory->create($class); + $blueprint = self::$factory->create($class); // @codingStandardsIgnoreStart $this->assertSame( @@ -103,10 +122,9 @@ public function iCanBuildAdvancedObjectHierarchy(): void /** @test */ public function iCanBuildObjectWithObjectCollectionWithoutUseInConstructor(): void { - $factory = new Anonymous(new PhpStan()); $class = ListOfObjectsWithoutUseStmtConstructor::class; - $blueprint = $factory->create($class); + $blueprint = self::$factory->create($class); // @codingStandardsIgnoreStart $this->assertSame('create(ParserFactory::PREFER_PHP7, new Emulative([ + 'usedAttributes' => ['comments', 'startLine', 'endLine', 'startFilePos', 'endFilePos'], + ])) + ) ) ) ); From ca15bfee9f2a07c9e92c31051cb90b47759d049a Mon Sep 17 00:00:00 2001 From: gardziejewskik Date: Thu, 26 Jul 2018 22:27:03 +0200 Subject: [PATCH 15/32] Fix --- src/Builder/Reflection.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Builder/Reflection.php b/src/Builder/Reflection.php index 4dc227f..a79027a 100644 --- a/src/Builder/Reflection.php +++ b/src/Builder/Reflection.php @@ -2,7 +2,9 @@ namespace RstGroup\ObjectBuilder\Builder; +use PhpParser\Lexer\Emulative; use PhpParser\Node\Stmt; +use PhpParser\ParserFactory; use PHPStan\PhpDocParser\Lexer\Lexer; use PHPStan\PhpDocParser\Parser\ConstExprParser; use PHPStan\PhpDocParser\Parser\PhpDocParser; @@ -11,7 +13,6 @@ use ReflectionClass; use ReflectionMethod; use ReflectionParameter; -use Roave\BetterReflection\BetterReflection; use RstGroup\ObjectBuilder\Builder; use RstGroup\ObjectBuilder\BuildingError; use Throwable; @@ -118,7 +119,18 @@ private function buildParameter(ReflectionParameter $parameter, $data, Reflectio if ($node->parameterName === '$' . $parameter->getName()) { $type = $node->type->type; $list = []; - $parser = (new BetterReflection())->phpParser(); + $parser = (new ParserFactory())->create( + ParserFactory::PREFER_PHP7, + new Emulative([ + 'usedAttributes' => [ + 'comments', + 'startLine', + 'endLine', + 'startFilePos', + 'endFilePos' + ], + ]) + ); /** @var ReflectionClass $class */ $class = $parameter->getDeclaringClass(); From 83d4fbea5a653e71ea0dcd85e773072266560d56 Mon Sep 17 00:00:00 2001 From: gardziejewskik Date: Sat, 28 Jul 2018 09:38:12 +0200 Subject: [PATCH 16/32] Small refactor CodeGenerator stack --- .../Blueprint/Factory/CodeGenerator.php | 9 ++--- .../PatternGenerator/Anonymous.php | 34 +++++++------------ src/Builder/Reflection.php | 2 +- src/PhpDocParser/PhpStan.php | 2 ++ .../Factory/CodeGenerator/AnonymousTest.php | 32 ++++++----------- test/unit/Builder/BlueprintTest.php | 2 +- 6 files changed, 30 insertions(+), 51 deletions(-) diff --git a/src/Builder/Blueprint/Factory/CodeGenerator.php b/src/Builder/Blueprint/Factory/CodeGenerator.php index b6bf553..bb0edcf 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator.php @@ -17,13 +17,8 @@ public function __construct(PatternGenerator $generator) public function create(string $class): callable { - $blueprint = $this->generator->create($class); - $prefix = 'generator->create($class); - if (substr($blueprint, 0, strlen($prefix)) === $prefix) { - $blueprint = substr($blueprint, strlen($prefix)); - } - - return eval($blueprint . ';'); + return eval($pattern); } } diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Anonymous.php b/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Anonymous.php index fc0e242..a54ec49 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Anonymous.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Anonymous.php @@ -5,26 +5,22 @@ use ReflectionClass; use ReflectionMethod; use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Node; -use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Node\Complex; -use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Node\ObjectList; -use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Node\Scalar; use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\PatternGenerator; use RstGroup\ObjectBuilder\PhpDocParser; final class Anonymous implements PatternGenerator { - private const FILE_BEGIN_WITH = - "getNode($reflection); - return self::FILE_BEGIN_WITH - . "\n" - . 'return ' - . sprintf( - self::FUNCTION_PATTERN, - $this->getDefaultSection($node), - $node - ) - ; + return sprintf( + self::FUNCTION_PATTERN, + $this->getDefaultSection($node), + $node + ); } private function getNode(ReflectionClass $class, string $name = ''): Node @@ -56,7 +48,7 @@ private function getNode(ReflectionClass $class, string $name = ''): Node $constructor = $class->getConstructor(); if (null === $constructor) { - return new Complex($class->getName(), $name); + return new Node\Complex($class->getName(), $name); } return $this->getNodes($constructor, $name); @@ -64,7 +56,7 @@ private function getNode(ReflectionClass $class, string $name = ''): Node private function getNodes(ReflectionMethod $method, string $name = ''): Node { - $node = new Complex($method->getDeclaringClass()->getName(), $name); + $node = new Node\Complex($method->getDeclaringClass()->getName(), $name); foreach ($method->getParameters() as $parameter) { $class = $parameter->getClass(); @@ -92,13 +84,13 @@ private function createNode(\ReflectionParameter $parameter, string $phpDoc): No && $this->phpDocParser->isListOfObject($phpDoc, $parameter)) { $class = $this->phpDocParser->getListType($phpDoc, $parameter); - return new ObjectList( + return new Node\ObjectList( $parameter->getName(), $this->getNode(new ReflectionClass($class)) ); } - return new Scalar( + return new Node\Scalar( $type, $parameter->getName(), $parameter->isDefaultValueAvailable() @@ -126,7 +118,7 @@ private function getDefaultValues(Node $node) { $values = []; - if ($node instanceof Complex) { + if ($node instanceof Node\Complex) { foreach ($node->innerNodes() as $innerNode) { $innerNodeDefaultValues = $this->getDefaultValues($innerNode); if ([] === $innerNodeDefaultValues) { diff --git a/src/Builder/Reflection.php b/src/Builder/Reflection.php index a79027a..fe3e0a8 100644 --- a/src/Builder/Reflection.php +++ b/src/Builder/Reflection.php @@ -127,7 +127,7 @@ private function buildParameter(ReflectionParameter $parameter, $data, Reflectio 'startLine', 'endLine', 'startFilePos', - 'endFilePos' + 'endFilePos', ], ]) ); diff --git a/src/PhpDocParser/PhpStan.php b/src/PhpDocParser/PhpStan.php index 1ec774c..ada25ec 100644 --- a/src/PhpDocParser/PhpStan.php +++ b/src/PhpDocParser/PhpStan.php @@ -14,7 +14,9 @@ final class PhpStan implements PhpDocParser { + /** @var PhpStanPhpDocParser */ private $phpDocParser; + /** @var Parser */ private $phpParser; public function __construct( diff --git a/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php b/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php index d83d1b0..660860a 100644 --- a/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php +++ b/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php @@ -21,7 +21,7 @@ class AnonymousTest extends TestCase /** @var Anonymous */ private static $factory; - public static function setUpBeforeClass() + public static function setUpBeforeClass(): void { self::$factory = new Anonymous( new PhpStan( @@ -44,12 +44,10 @@ public function iCanGenerateSimpleObjectClosure(): void $blueprint = self::$factory->create($class); $this->assertSame( - 'assertSame( - 'assertSame( - ' \'some string\', \'someInt\' => 999, @@ -93,7 +87,7 @@ public function iCanBuildSimpleObjectWithDefaultValuesInConstructor(): void $data = array_merge($default, $data); return new RstGroup\ObjectBuilder\Test\SimpleMixedConstructorWithDefaultValue(new RstGroup\ObjectBuilder\Test\SomeObjectWithEmptyConstructor(), $data[\'someString\'], $data[\'someInt\']); -}', +};', $blueprint ); // @codingStandardsIgnoreEnd @@ -108,12 +102,10 @@ public function iCanBuildAdvancedObjectHierarchy(): void // @codingStandardsIgnoreStart $this->assertSame( - 'create($class); // @codingStandardsIgnoreStart - $this->assertSame('assertSame('return function(array $data) use ($class): object { return new RstGroup\ObjectBuilder\Test\ListOfObjectsWithoutUseStmtConstructor((function (array $list) { $arr = []; @@ -139,7 +129,7 @@ public function iCanBuildObjectWithObjectCollectionWithoutUseInConstructor(): vo return $arr; })($data[\'list\'])); -}', +};', $blueprint ); // @codingStandardsIgnoreEnd diff --git a/test/unit/Builder/BlueprintTest.php b/test/unit/Builder/BlueprintTest.php index bb47806..fb549fd 100644 --- a/test/unit/Builder/BlueprintTest.php +++ b/test/unit/Builder/BlueprintTest.php @@ -5,10 +5,10 @@ use PhpParser\Lexer\Emulative; use PhpParser\ParserFactory; use PHPStan\PhpDocParser\Parser\ConstExprParser; +use PHPStan\PhpDocParser\Parser\PhpDocParser; use PHPStan\PhpDocParser\Parser\TypeParser; use RstGroup\ObjectBuilder\Builder\Blueprint; use RstGroup\ObjectBuilder\PhpDocParser\PhpStan; -use PHPStan\PhpDocParser\Parser\PhpDocParser; class BlueprintTest extends BuilderTest { From 7e414c437df2c0da810e492fe1ac5568884d84db Mon Sep 17 00:00:00 2001 From: gardziejewskik Date: Sat, 28 Jul 2018 22:39:51 +0200 Subject: [PATCH 17/32] Refactor to use serializer for Node --- .../Blueprint/Factory/CodeGenerator/Node.php | 2 - .../Factory/CodeGenerator/Node/Complex.php | 24 +----- .../Factory/CodeGenerator/Node/ObjectList.php | 15 +--- .../Factory/CodeGenerator/Node/Scalar.php | 4 - .../Factory/CodeGenerator/Node/Serializer.php | 10 +++ .../Node/Serializer/ArrayAccess.php | 75 +++++++++++++++++++ .../PatternGenerator/Anonymous.php | 11 ++- .../Factory/CodeGenerator/AnonymousTest.php | 18 +++-- .../Node/Serializer/ArrayAccessTest.php | 72 ++++++++++++++++++ test/unit/Builder/BlueprintTest.php | 3 +- 10 files changed, 180 insertions(+), 54 deletions(-) create mode 100644 src/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer.php create mode 100644 src/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccess.php create mode 100644 test/unit/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccessTest.php diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node.php index 76c734b..3100767 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Node.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node.php @@ -39,6 +39,4 @@ public function defaultValue() { return $this->defaultValue; } - - abstract public function __toString(): string; } diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php index 742efab..d521dea 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php @@ -15,30 +15,8 @@ public function add(Node $node): void } /** @return Node[] */ - public function innerNodes(): array + public function innerNodes(): iterable { return $this->nodes; } - - public function __toString(): string - { - $nodes = []; - foreach ($this->nodes as $node) { - if ($node instanceof Scalar) { - $nodeSerialized = (string) $node; - if ('' !== $this->name()) { - $nodeSerialized = '[\'' . $this->name() . '\']' . $nodeSerialized; - } - $nodes[] = '$data' . $nodeSerialized; - continue; - } - - $nodes[] = (string) $node; - } - return sprintf( - 'new %s(%s)', - $this->type(), - implode(', ', $nodes) - ); - } } diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node/ObjectList.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node/ObjectList.php index 954c96e..0e6cd09 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Node/ObjectList.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node/ObjectList.php @@ -15,19 +15,8 @@ public function __construct(string $name, Node $objectNode) $this->objectNode = $objectNode; } - public function __toString(): string + public function objectNode(): Node { - return sprintf( - '(function (array $list) { - $arr = []; - foreach ($list as $data) { - $arr[] = %s; - } - - return $arr; - })($data[\'%s\'])', - $this->objectNode, - $this->name() - ); + return $this->objectNode; } } diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Scalar.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Scalar.php index 873dfbc..de04a6b 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Scalar.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Scalar.php @@ -6,8 +6,4 @@ final class Scalar extends Node { - public function __toString(): string - { - return sprintf('[\'%s\']', $this->name); - } } diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer.php new file mode 100644 index 0000000..0833b80 --- /dev/null +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer.php @@ -0,0 +1,10 @@ +serializeScalarNode($node); + break; + case Node\Complex::class: + return $this->serializeComplexNode($node); + break; + case Node\ObjectList::class: + return $this->serializeObjectListNode($node); + break; + } + + throw new BuildingError(); + } + + private function serializeScalarNode(Node\Scalar $node): string + { + return sprintf(static::SCALAR_PATTERN, $node->name()); + } + + private function serializeComplexNode(Node\Complex $node): string + { + $nodes = []; + foreach ($node->innerNodes() as $innerNode) { + if ($innerNode instanceof Node\Scalar) { + $serializedInnerNode = $this->serialize($innerNode); + if ('' !== $node->name()) { + $serializedInnerNode = '[\'' . $node->name() . '\']' . $serializedInnerNode; + } + $nodes[] = '$data' . $serializedInnerNode; + continue; + } + $nodes[] = $this->serialize($innerNode); + } + + return sprintf( + self::COMPLEX_PATTERN, + $node->type(), + implode(', ', $nodes) + ); + } + + private function serializeObjectListNode(Node\ObjectList $node): string + { + return sprintf( + static::OBJECT_LIST_PATTERN, + $this->serialize($node->objectNode()), + $node->name() + ); + } +} diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Anonymous.php b/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Anonymous.php index a54ec49..48108e3 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Anonymous.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Anonymous.php @@ -24,10 +24,15 @@ final class Anonymous implements PatternGenerator /** @var PhpDocParser */ private $phpDocParser; + /** @var Node\Serializer */ + private $serializer; - public function __construct(PhpDocParser $parser) - { + public function __construct( + PhpDocParser $parser, + Node\Serializer $serializer + ) { $this->phpDocParser = $parser; + $this->serializer = $serializer; } public function create(string $class): string @@ -39,7 +44,7 @@ public function create(string $class): string return sprintf( self::FUNCTION_PATTERN, $this->getDefaultSection($node), - $node + $this->serializer->serialize($node) ); } diff --git a/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php b/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php index 660860a..5e1479c 100644 --- a/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php +++ b/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php @@ -8,6 +8,7 @@ use PHPStan\PhpDocParser\Parser\PhpDocParser; use PHPStan\PhpDocParser\Parser\TypeParser; use PHPUnit\Framework\TestCase; +use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Node\Serializer\ArrayAccess; use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\PatternGenerator\Anonymous; use RstGroup\ObjectBuilder\PhpDocParser\PhpStan; use RstGroup\ObjectBuilder\Test\ListOfObjectsWithoutUseStmtConstructor; @@ -32,7 +33,8 @@ public static function setUpBeforeClass(): void (new ParserFactory())->create(ParserFactory::PREFER_PHP7, new Emulative([ 'usedAttributes' => ['comments', 'startLine', 'endLine', 'startFilePos', 'endFilePos'], ])) - ) + ), + new ArrayAccess() ); } @@ -122,13 +124,13 @@ public function iCanBuildObjectWithObjectCollectionWithoutUseInConstructor(): vo $this->assertSame('return function(array $data) use ($class): object { return new RstGroup\ObjectBuilder\Test\ListOfObjectsWithoutUseStmtConstructor((function (array $list) { - $arr = []; - foreach ($list as $data) { - $arr[] = new RstGroup\ObjectBuilder\Test\SimpleScalarConstructor($data[\'someString\'], $data[\'someInt\']); - } - - return $arr; - })($data[\'list\'])); + $arr = []; + foreach ($list as $data) { + $arr[] = new RstGroup\ObjectBuilder\Test\SimpleScalarConstructor($data[\'someString\'], $data[\'someInt\']); + } + + return $arr; + })($data[\'list\'])); };', $blueprint ); diff --git a/test/unit/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccessTest.php b/test/unit/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccessTest.php new file mode 100644 index 0000000..a4d9bff --- /dev/null +++ b/test/unit/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccessTest.php @@ -0,0 +1,72 @@ +serialize($node); + + $this->assertSame('[\'someName\']', $serializedNode); + } + + /** @test */ + public function serializeComplexNodeToArrayAccessString() + { + $node = new Complex('SomeClassName', 'someName'); + $scalarStringNode = new Scalar('string', 'someStringName'); + $scalarIntNode = new Scalar('int', 'someInt'); + $node->add($scalarStringNode); + $node->add($scalarIntNode); + + $serializedNode = static::$serializer->serialize($node); + + $this->assertSame( + 'new SomeClassName($data[\'someName\'][\'someStringName\'], $data[\'someName\'][\'someInt\'])', + $serializedNode + ); + } + + /** @test */ + public function serializeObjectListNodeToArrayAccessString() + { + $objectNode = new Complex('SomeClassName', 'someName'); + $scalarStringNode = new Scalar('string', 'someStringName'); + $scalarIntNode = new Scalar('int', 'someInt'); + $objectNode->add($scalarStringNode); + $objectNode->add($scalarIntNode); + $node = new ObjectList('someList', $objectNode); + + $serializedNode = static::$serializer->serialize($node); + + $this->assertSame( + '(function (array $list) { + $arr = []; + foreach ($list as $data) { + $arr[] = new SomeClassName($data[\'someName\'][\'someStringName\'], $data[\'someName\'][\'someInt\']); + } + + return $arr; + })($data[\'someList\'])', + $serializedNode + ); + } +} diff --git a/test/unit/Builder/BlueprintTest.php b/test/unit/Builder/BlueprintTest.php index fb549fd..3d07885 100644 --- a/test/unit/Builder/BlueprintTest.php +++ b/test/unit/Builder/BlueprintTest.php @@ -25,7 +25,8 @@ public static function setUpBeforeClass(): void (new ParserFactory())->create(ParserFactory::PREFER_PHP7, new Emulative([ 'usedAttributes' => ['comments', 'startLine', 'endLine', 'startFilePos', 'endFilePos'], ])) - ) + ), + new Blueprint\Factory\CodeGenerator\Node\Serializer\ArrayAccess() ) ) ); From a8a5991d5012e7038b856dbc25f408f623cb36b7 Mon Sep 17 00:00:00 2001 From: gardziejewskik Date: Sat, 28 Jul 2018 22:44:41 +0200 Subject: [PATCH 18/32] CS fix --- .../Factory/CodeGenerator/Node/Serializer.php | 2 +- .../CodeGenerator/Node/Serializer/ArrayAccess.php | 4 ++-- .../Node/Serializer/ArrayAccessTest.php | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer.php index 0833b80..8cb4c3e 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer.php @@ -1,4 +1,4 @@ -serialize($node); $this->assertSame( - '(function (array $list) { + '(function (array $list) { $arr = []; foreach ($list as $data) { $arr[] = new SomeClassName($data[\'someName\'][\'someStringName\'], $data[\'someName\'][\'someInt\']); From 45d54611f067f281a0bff8e1f3ad08f36b3a1e63 Mon Sep 17 00:00:00 2001 From: gardziejewskik Date: Mon, 30 Jul 2018 19:51:51 +0200 Subject: [PATCH 19/32] FilesystemStore implementation --- phpunit.xml | 5 +- .../CodeGenerator/Store/Filesystem.php | 31 +++++++++ .../CodeGenerator/Store/FilesystemTest.php | 66 +++++++++++++++++++ .../Factory/CodeGenerator/AnonymousTest.php | 2 +- .../Node/Serializer/ArrayAccessTest.php | 2 +- .../Builder/Blueprint/Factory/Simple.php | 2 +- test/{unit => Unit}/Builder/BlueprintTest.php | 2 +- test/{unit => Unit}/Builder/BuilderTest.php | 2 +- .../ParameterNameStrategy/SimpleTest.php | 2 +- .../ParameterNameStrategy/SnakeCaseTest.php | 2 +- .../{unit => Unit}/Builder/ReflectionTest.php | 2 +- 11 files changed, 109 insertions(+), 9 deletions(-) create mode 100644 src/Builder/Blueprint/Factory/CodeGenerator/Store/Filesystem.php create mode 100644 test/Integration/Builder/Blueprint/Factory/CodeGenerator/Store/FilesystemTest.php rename test/{unit => Unit}/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php (98%) rename test/{unit => Unit}/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccessTest.php (97%) rename test/{unit => Unit}/Builder/Blueprint/Factory/Simple.php (85%) rename test/{unit => Unit}/Builder/BlueprintTest.php (95%) rename test/{unit => Unit}/Builder/BuilderTest.php (99%) rename test/{unit => Unit}/Builder/ParameterNameStrategy/SimpleTest.php (96%) rename test/{unit => Unit}/Builder/ParameterNameStrategy/SnakeCaseTest.php (96%) rename test/{unit => Unit}/Builder/ReflectionTest.php (85%) diff --git a/phpunit.xml b/phpunit.xml index 2db3447..30c5d27 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -13,7 +13,10 @@ - test/unit/ + test/Unit/ + + + test/Integration/ diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Store/Filesystem.php b/src/Builder/Blueprint/Factory/CodeGenerator/Store/Filesystem.php new file mode 100644 index 0000000..2623de6 --- /dev/null +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Store/Filesystem.php @@ -0,0 +1,31 @@ +path = $path; + } + + public function get(string $class): ?callable + { + $fileFullPath = $this->path . $class; + if (file_exists($fileFullPath)) { + return require $fileFullPath; + } + + return null; + } + + public function save(string $class, string $blueprint): void + { + file_put_contents($this->path . $class, $blueprint); + } +} diff --git a/test/Integration/Builder/Blueprint/Factory/CodeGenerator/Store/FilesystemTest.php b/test/Integration/Builder/Blueprint/Factory/CodeGenerator/Store/FilesystemTest.php new file mode 100644 index 0000000..d836efd --- /dev/null +++ b/test/Integration/Builder/Blueprint/Factory/CodeGenerator/Store/FilesystemTest.php @@ -0,0 +1,66 @@ +save('SomeClass', 'content php'); + + $savedBlueprint = file_get_contents('/tmp/SomeClass'); + $this->assertSame('content php', $savedBlueprint); + } + + /** @test */ + public function iCanGetSavedBlueprintInFilesystem() + { + $store = new Filesystem('/tmp/'); + file_put_contents( + '/tmp/SomeClass', + 'get('SomeClass'); + + $this->assertSame('some string', $function()); + } + + /** @test */ + public function whenFileExistsInFilesystemThenOverrideIt() + { + $store = new Filesystem('/tmp/'); + file_put_contents( + '/tmp/SomeClass', + 'some string' + ); + + $store->save('SomeClass', 'override'); + + $savedBlueprint = file_get_contents('/tmp/SomeClass'); + $this->assertSame('override', $savedBlueprint); + } + + /** @test */ + public function whenFileDoesNotExistInFilesystemThenReturnNull() + { + $store = new Filesystem('/tmp/'); + + $function = $store->get('SomeClass'); + + $this->assertNull($function); + } +} diff --git a/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php b/test/Unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php similarity index 98% rename from test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php rename to test/Unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php index 5e1479c..2acdd41 100644 --- a/test/unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php +++ b/test/Unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php @@ -1,6 +1,6 @@ Date: Thu, 2 Aug 2018 18:47:23 +0200 Subject: [PATCH 20/32] Improve uTests --- .../Factory/CodeGenerator/Store/Memory.php | 13 ++++- .../Node/Serializer/ArrayAccessTest.php | 12 ++++ .../CodeGenerator/Store/InMemoryTest.php | 55 +++++++++++++++++++ test/Unit/Builder/ReflectionTest.php | 15 +++++ 4 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 test/Unit/Builder/Blueprint/Factory/CodeGenerator/Store/InMemoryTest.php diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Store/Memory.php b/src/Builder/Blueprint/Factory/CodeGenerator/Store/Memory.php index 4ac839b..baff016 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Store/Memory.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Store/Memory.php @@ -7,9 +7,14 @@ final class Memory implements Store { - /** @var string[] */ + /** @var callable[] */ private $store = []; + public function __construct(array $blueprints = []) + { + $this->store = $blueprints; + } + public function get(string $class): ?callable { try { @@ -23,4 +28,10 @@ public function save(string $class, string $blueprint): void { $this->store[$class] = $blueprint; } + + /** @return callable[] */ + public function store(): array + { + return $this->store; + } } diff --git a/test/Unit/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccessTest.php b/test/Unit/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccessTest.php index 0048f9f..e388c64 100644 --- a/test/Unit/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccessTest.php +++ b/test/Unit/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccessTest.php @@ -3,10 +3,12 @@ namespace RstGroup\ObjectBuilder\Test\Unit\Builder\Blueprint\Factory\CodeGenerator\Node\Serializer; use PHPUnit\Framework\TestCase; +use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Node; use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Node\Complex; use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Node\ObjectList; use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Node\Scalar; use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Node\Serializer\ArrayAccess; +use RstGroup\ObjectBuilder\BuildingError; class ArrayAccessTest extends TestCase { @@ -69,4 +71,14 @@ public function serializeObjectListNodeToArrayAccessString(): void $serializedNode ); } + + /** @test */ + public function whenNodeObjectIsNotKnowThenThrowsException(): void + { + $node = new class('a', 'a') extends Node {}; + + $this->expectException(BuildingError::class); + + static::$serializer->serialize($node); + } } diff --git a/test/Unit/Builder/Blueprint/Factory/CodeGenerator/Store/InMemoryTest.php b/test/Unit/Builder/Blueprint/Factory/CodeGenerator/Store/InMemoryTest.php new file mode 100644 index 0000000..22766b0 --- /dev/null +++ b/test/Unit/Builder/Blueprint/Factory/CodeGenerator/Store/InMemoryTest.php @@ -0,0 +1,55 @@ +save('SomeClass', 'blueprint'); + + $this->assertSame('blueprint', $store->store()['SomeClass']); + } + + /** @test */ + public function iCanGetSavedBlueprintInMemory() + { + $store = new Memory([ + 'SomeClass' => 'return function() { return \'some string\'; };', + ]); + + $function = $store->get('SomeClass'); + + $this->assertSame('some string', $function()); + } + + /** @test */ + public function whenFileExistsInMemoryThenOverrideIt() + { + $store = new Memory([ + 'SomeClass' => 'some string', + ]); + + $store->save('SomeClass', 'override'); + + $memoryStore = $store->store(); + $this->assertSame('override', reset($memoryStore)); + } + + /** @test */ + public function whenFileDoesNotExistInMemoryThenReturnNull() + { + $store = new Memory(); + + $function = $store->get('SomeClass'); + + $this->assertNull($function); + } + +} diff --git a/test/Unit/Builder/ReflectionTest.php b/test/Unit/Builder/ReflectionTest.php index ba94ae3..adf357e 100644 --- a/test/Unit/Builder/ReflectionTest.php +++ b/test/Unit/Builder/ReflectionTest.php @@ -4,6 +4,8 @@ use RstGroup\ObjectBuilder\Builder\ParameterNameStrategy\Simple; use RstGroup\ObjectBuilder\Builder\Reflection; +use RstGroup\ObjectBuilder\BuildingError; +use RstGroup\ObjectBuilder\Test\SimpleMixedConstructor; class ReflectionTest extends BuilderTest { @@ -11,4 +13,17 @@ public static function setUpBeforeClass(): void { static::$builder = new Reflection(new Simple()); } + + public function tooFewArgumentsWillTHrowBuildingException() + { + $data = [ + 'someString' => 'some string', + 'someInt' => 999, + ]; + $class = SimpleMixedConstructor::class; + + $this->expectException(BuildingError::class); + + static::$builder->build($class, $data); + } } From c11de989099ab155cba30f37303014822fc7fd3a Mon Sep 17 00:00:00 2001 From: gardziejewskik Date: Thu, 2 Aug 2018 19:07:06 +0200 Subject: [PATCH 21/32] Resolve conflicts --- src/Builder/Reflection.php | 26 ++++++++- test/Unit/Builder/BuilderTest.php | 95 +++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 2 deletions(-) diff --git a/src/Builder/Reflection.php b/src/Builder/Reflection.php index fe3e0a8..149ad0f 100644 --- a/src/Builder/Reflection.php +++ b/src/Builder/Reflection.php @@ -68,10 +68,16 @@ private function collect(ReflectionMethod $constructor, array $data): iterable } yield $this->buildParameter($parameter, $parsedData[$name], $constructor); + continue; } if ($parameter->isDefaultValueAvailable()) { yield $parameter->getDefaultValue(); + continue; + } + + if ($parameter->allowsNull()) { + yield null; } } } @@ -117,7 +123,10 @@ private function buildParameter(ReflectionParameter $parameter, $data, Reflectio $node = $parser->parse(new TokenIterator((new Lexer())->tokenize($comment))); foreach ($node->getParamTagValues() as $node) { if ($node->parameterName === '$' . $parameter->getName()) { - $type = $node->type->type; + $typeName = $node->type->type->name; + if ($this->isScalar($typeName)) { + continue; + } $list = []; $parser = (new ParserFactory())->create( ParserFactory::PREFER_PHP7, @@ -147,7 +156,7 @@ private function buildParameter(ReflectionParameter $parameter, $data, Reflectio foreach ($data as $objectConstructorData) { $list[] = $this->build( - $this->getFullClassName($type->name, $namespaces, $constructor->getDeclaringClass()), + $this->getFullClassName($typeName, $namespaces, $constructor->getDeclaringClass()), $objectConstructorData ); } @@ -160,6 +169,19 @@ private function buildParameter(ReflectionParameter $parameter, $data, Reflectio return $data; } + private function isScalar(string $value): bool + { + $scalars = [ + 'string', + 'int', + 'float', + 'double', + 'mixed', + ]; + + return in_array($value, $scalars); + } + /** * @param Stmt[] $nodes */ diff --git a/test/Unit/Builder/BuilderTest.php b/test/Unit/Builder/BuilderTest.php index 3e32c85..8fd9e54 100644 --- a/test/Unit/Builder/BuilderTest.php +++ b/test/Unit/Builder/BuilderTest.php @@ -6,11 +6,14 @@ use RstGroup\ObjectBuilder\Builder; use RstGroup\ObjectBuilder\Test\ListOfObjectsWithoutUseButWithFQNTypedArrayConstructor; use RstGroup\ObjectBuilder\Test\ListOfObjectsWithoutUseStmtConstructor; +use RstGroup\ObjectBuilder\Test\ListOfObjectsWithScalarTypedArrayAndObjectListConstructor; +use RstGroup\ObjectBuilder\Test\ListOfObjectsWithScalarTypedArrayConstructor; use RstGroup\ObjectBuilder\Test\ListOfObjectsWithUseStmtConstructor; use RstGroup\ObjectBuilder\Test\Object\SomeObject; use RstGroup\ObjectBuilder\Test\Object\SomeSecondObject; use RstGroup\ObjectBuilder\Test\SimpleMixedConstructor; use RstGroup\ObjectBuilder\Test\SimpleMixedConstructorWithDefaultValue; +use RstGroup\ObjectBuilder\Test\SimpleNullableConstructor; use RstGroup\ObjectBuilder\Test\SimpleScalarConstructor; use RstGroup\ObjectBuilder\Test\SomeAggregateRoot; use RstGroup\ObjectBuilder\Test\SomeObjectWithEmptyConstructor; @@ -173,4 +176,96 @@ public function iCanBuildAdvancedObjectHierarchy(): void $this->assertSame(2, $object->simpleObject2->someInt); $this->assertSame('some string2', $object->simpleObject2->someString); } + + /** @test */ + public function iCanBuildObjectWithScalarCollectionTypedArrayInConstructor() + { + $data = [ + 'list1' => ['str', 'str'], + 'list2' => ['str', 'str'], + ]; + $class = ListOfObjectsWithScalarTypedArrayConstructor::class; + + /** @var ListOfObjectsWithScalarTypedArrayConstructor $object */ + $object = static::$builder->build($class, $data); + + $this->assertInstanceOf(ListOfObjectsWithScalarTypedArrayConstructor::class, $object); + $this->assertCount(2, $object->list1); + $this->assertCount(2, $object->list2); + foreach($object->list1 as $element) { + $this->assertSame('str', $element); + } + foreach($object->list2 as $element) { + $this->assertSame('str', $element); + } + } + + /** @test */ + public function iCanBuildObjectWithBothScalarAndObjectCollectionTypedArrayInConstructor() + { + $data = [ + 'list1' => ['str', 'str'], + 'list2' => [ + [ + 'someString' => 'some string1', + 'someInt' => 1, + ], + [ + 'someString' => 'some string2', + 'someInt' => 2, + ], + ], + ]; + $class = ListOfObjectsWithScalarTypedArrayAndObjectListConstructor::class; + + /** @var ListOfObjectsWithScalarTypedArrayAndObjectListConstructor $object */ + $object = static::$builder->build($class, $data); + + $this->assertInstanceOf(ListOfObjectsWithScalarTypedArrayAndObjectListConstructor::class, $object); + $this->assertCount(2, $object->list1); + $this->assertCount(2, $object->list2); + foreach($object->list1 as $element) { + $this->assertSame('str', $element); + } + foreach($object->list2 as $element) { + $this->assertInstanceOf(SimpleScalarConstructor::class, $element); + } + } + + /** @test */ + public function iCanBuildObjectWithNullableParameterWithoutDefaultValue() + { + $data = [ + 'someString1' => 'some string1', + 'someString2' => 'some string2', + ]; + $class = SimpleNullableConstructor::class; + + /** @var SimpleNullableConstructor $object */ + $object = static::$builder->build($class, $data); + + $this->assertInstanceOf(SimpleNullableConstructor::class, $object); + $this->assertSame('some string1', $object->someString1); + $this->assertSame('some string2', $object->someString2); + $this->assertNull($object->someInt); + } + + /** @test */ + public function iCanBuildObjectWithNullableParameterWithHimValueValue() + { + $data = [ + 'someString1' => 'some string1', + 'someInt' => 123, + 'someString2' => 'some string2', + ]; + $class = SimpleNullableConstructor::class; + + /** @var SimpleNullableConstructor $object */ + $object = static::$builder->build($class, $data); + + $this->assertInstanceOf(SimpleNullableConstructor::class, $object); + $this->assertSame('some string1', $object->someString1); + $this->assertSame('some string2', $object->someString2); + $this->assertSame(123, $object->someInt); + } } From 5c22a4adee5c8e664f17a754f0141aa85bc72f6a Mon Sep 17 00:00:00 2001 From: gardziejewskik Date: Sat, 4 Aug 2018 23:03:55 +0200 Subject: [PATCH 22/32] Improve quality of tests --- .gitignore | 3 ++- src/Builder/Blueprint.php | 1 + .../Blueprint/Factory/CodeGenerator.php | 1 + .../Blueprint/Factory/CodeGenerator/Node.php | 10 +++++++++- .../Node/Serializer/ArrayAccess.php | 20 +++++++++---------- .../PatternGenerator/Anonymous.php | 8 +++++++- .../CodeGenerator/Store/Filesystem.php | 2 +- .../Factory/CodeGenerator/Store/Memory.php | 5 +++-- src/Builder/Reflection.php | 3 ++- src/PhpDocParser/PhpStan.php | 20 +++++++++++++++++++ .../CodeGenerator/Store/FilesystemTest.php | 18 +++++++++-------- ...alarTypedArrayAndObjectListConstructor.php | 2 +- ...ObjectsWithScalarTypedArrayConstructor.php | 2 +- test/SimpleNullableConstructor.php | 2 +- .../Node/Serializer/ArrayAccessTest.php | 3 ++- .../CodeGenerator/Store/InMemoryTest.php | 11 +++++----- test/Unit/Builder/BuilderTest.php | 17 ++++++++-------- .../Unit/Builder/PhpDocParser/PhpStanTest.php | 10 ++++++++++ test/Unit/Builder/ReflectionTest.php | 2 +- 19 files changed, 96 insertions(+), 44 deletions(-) create mode 100644 test/Unit/Builder/PhpDocParser/PhpStanTest.php diff --git a/.gitignore b/.gitignore index 70b2591..37f1cd6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ vendor .idea .php_cs.cache .phpcs.cache -data \ No newline at end of file +data +test/result \ No newline at end of file diff --git a/src/Builder/Blueprint.php b/src/Builder/Blueprint.php index 54c9a0e..a020128 100644 --- a/src/Builder/Blueprint.php +++ b/src/Builder/Blueprint.php @@ -9,6 +9,7 @@ final class Blueprint implements Builder /** @var Builder\Blueprint\Factory */ private $blueprintFactory; + /** @codeCoverageIgnore */ public function __construct(Builder\Blueprint\Factory $factory) { $this->blueprintFactory = $factory; diff --git a/src/Builder/Blueprint/Factory/CodeGenerator.php b/src/Builder/Blueprint/Factory/CodeGenerator.php index bb0edcf..1478a99 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator.php @@ -10,6 +10,7 @@ final class CodeGenerator implements Factory /** @var PatternGenerator */ private $generator; + /** @codeCoverageIgnore */ public function __construct(PatternGenerator $generator) { $this->generator = $generator; diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node.php index 3100767..3c76285 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Node.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node.php @@ -10,13 +10,16 @@ abstract class Node private $defaultValue; /** @var string */ private $type; + /** @var bool */ + private $nullable; /** @param mixed $defaultValue */ - public function __construct(string $type, string $name, $defaultValue = null) + public function __construct(string $type, string $name, bool $nullable = false, $defaultValue = null) { $this->name = $name; $this->defaultValue = $defaultValue; $this->type = $type; + $this->nullable = $nullable; } public function type(): string @@ -34,6 +37,11 @@ public function withDefaultValue(): bool return null !== $this->defaultValue; } + public function nullable(): bool + { + return $this->nullable; + } + /** @return mixed */ public function defaultValue() { diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccess.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccess.php index 94b830c..a5561e3 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccess.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccess.php @@ -22,16 +22,16 @@ final class ArrayAccess implements Serializer public function serialize(Node $node): string { - switch (get_class($node)) { - case Node\Scalar::class: - return $this->serializeScalarNode($node); - break; - case Node\Complex::class: - return $this->serializeComplexNode($node); - break; - case Node\ObjectList::class: - return $this->serializeObjectListNode($node); - break; + if ($node instanceof Node\Scalar) { + return $this->serializeScalarNode($node); + } + + if ($node instanceof Node\Complex) { + return $this->serializeComplexNode($node); + } + + if ($node instanceof Node\ObjectList) { + return $this->serializeObjectListNode($node); } throw new BuildingError(); diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Anonymous.php b/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Anonymous.php index 48108e3..0c8e794 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Anonymous.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Anonymous.php @@ -27,6 +27,7 @@ final class Anonymous implements PatternGenerator /** @var Node\Serializer */ private $serializer; + /** @codeCoverageIgnore */ public function __construct( PhpDocParser $parser, Node\Serializer $serializer @@ -98,6 +99,7 @@ private function createNode(\ReflectionParameter $parameter, string $phpDoc): No return new Node\Scalar( $type, $parameter->getName(), + $parameter->allowsNull(), $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null @@ -135,7 +137,11 @@ private function getDefaultValues(Node $node) } if ($node->withDefaultValue()) { - $values = $node->defaultValue(); + return $node->defaultValue(); + } + + if ($node->nullable()) { + return null; } return $values; diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Store/Filesystem.php b/src/Builder/Blueprint/Factory/CodeGenerator/Store/Filesystem.php index 2623de6..7987916 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Store/Filesystem.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Store/Filesystem.php @@ -1,4 +1,4 @@ -store = $blueprints; @@ -29,7 +30,7 @@ public function save(string $class, string $blueprint): void $this->store[$class] = $blueprint; } - /** @return callable[] */ + /** @return string[] */ public function store(): array { return $this->store; diff --git a/src/Builder/Reflection.php b/src/Builder/Reflection.php index 149ad0f..34747be 100644 --- a/src/Builder/Reflection.php +++ b/src/Builder/Reflection.php @@ -22,6 +22,7 @@ final class Reflection implements Builder /** @var ParameterNameStrategy */ private $parameterNameStrategy; + /** @codeCoverageIgnore */ public function __construct(ParameterNameStrategy $parameterNameStrategy) { $this->parameterNameStrategy = $parameterNameStrategy; @@ -179,7 +180,7 @@ private function isScalar(string $value): bool 'mixed', ]; - return in_array($value, $scalars); + return in_array($value, $scalars, true); } /** diff --git a/src/PhpDocParser/PhpStan.php b/src/PhpDocParser/PhpStan.php index ada25ec..3eeabcd 100644 --- a/src/PhpDocParser/PhpStan.php +++ b/src/PhpDocParser/PhpStan.php @@ -33,6 +33,11 @@ public function isListOfObject(string $comment, ReflectionParameter $parameter): foreach ($node->getParamTagValues() as $node) { if ($node->parameterName === '$' . $parameter->getName()) { + $typeName = $node->type->type->name; + if ($this->isScalar($typeName)) { + continue; + } + return true; } } @@ -64,6 +69,21 @@ public function getListType(string $comment, ReflectionParameter $parameter): st return $this->getFullClassName($type->name, $namespaces, $class); } } + + throw new BuildingError(); + } + + private function isScalar(string $value): bool + { + $scalars = [ + 'string', + 'int', + 'float', + 'double', + 'mixed', + ]; + + return in_array($value, $scalars, true); } /** diff --git a/test/Integration/Builder/Blueprint/Factory/CodeGenerator/Store/FilesystemTest.php b/test/Integration/Builder/Blueprint/Factory/CodeGenerator/Store/FilesystemTest.php index d836efd..ac091f0 100644 --- a/test/Integration/Builder/Blueprint/Factory/CodeGenerator/Store/FilesystemTest.php +++ b/test/Integration/Builder/Blueprint/Factory/CodeGenerator/Store/FilesystemTest.php @@ -1,4 +1,4 @@ -expectException(BuildingError::class); diff --git a/test/Unit/Builder/Blueprint/Factory/CodeGenerator/Store/InMemoryTest.php b/test/Unit/Builder/Blueprint/Factory/CodeGenerator/Store/InMemoryTest.php index 22766b0..0bfbc5b 100644 --- a/test/Unit/Builder/Blueprint/Factory/CodeGenerator/Store/InMemoryTest.php +++ b/test/Unit/Builder/Blueprint/Factory/CodeGenerator/Store/InMemoryTest.php @@ -1,4 +1,4 @@ - 'return function() { return \'some string\'; };', @@ -30,7 +30,7 @@ public function iCanGetSavedBlueprintInMemory() } /** @test */ - public function whenFileExistsInMemoryThenOverrideIt() + public function whenFileExistsInMemoryThenOverrideIt(): void { $store = new Memory([ 'SomeClass' => 'some string', @@ -43,7 +43,7 @@ public function whenFileExistsInMemoryThenOverrideIt() } /** @test */ - public function whenFileDoesNotExistInMemoryThenReturnNull() + public function whenFileDoesNotExistInMemoryThenReturnNull(): void { $store = new Memory(); @@ -51,5 +51,4 @@ public function whenFileDoesNotExistInMemoryThenReturnNull() $this->assertNull($function); } - } diff --git a/test/Unit/Builder/BuilderTest.php b/test/Unit/Builder/BuilderTest.php index 8fd9e54..70ad884 100644 --- a/test/Unit/Builder/BuilderTest.php +++ b/test/Unit/Builder/BuilderTest.php @@ -18,6 +18,7 @@ use RstGroup\ObjectBuilder\Test\SomeAggregateRoot; use RstGroup\ObjectBuilder\Test\SomeObjectWithEmptyConstructor; +// TODO move to component abstract class BuilderTest extends TestCase { /** @var Builder */ @@ -178,7 +179,7 @@ public function iCanBuildAdvancedObjectHierarchy(): void } /** @test */ - public function iCanBuildObjectWithScalarCollectionTypedArrayInConstructor() + public function iCanBuildObjectWithScalarCollectionTypedArrayInConstructor(): void { $data = [ 'list1' => ['str', 'str'], @@ -192,16 +193,16 @@ public function iCanBuildObjectWithScalarCollectionTypedArrayInConstructor() $this->assertInstanceOf(ListOfObjectsWithScalarTypedArrayConstructor::class, $object); $this->assertCount(2, $object->list1); $this->assertCount(2, $object->list2); - foreach($object->list1 as $element) { + foreach ($object->list1 as $element) { $this->assertSame('str', $element); } - foreach($object->list2 as $element) { + foreach ($object->list2 as $element) { $this->assertSame('str', $element); } } /** @test */ - public function iCanBuildObjectWithBothScalarAndObjectCollectionTypedArrayInConstructor() + public function iCanBuildObjectWithBothScalarAndObjectCollectionTypedArrayInConstructor(): void { $data = [ 'list1' => ['str', 'str'], @@ -224,16 +225,16 @@ public function iCanBuildObjectWithBothScalarAndObjectCollectionTypedArrayInCons $this->assertInstanceOf(ListOfObjectsWithScalarTypedArrayAndObjectListConstructor::class, $object); $this->assertCount(2, $object->list1); $this->assertCount(2, $object->list2); - foreach($object->list1 as $element) { + foreach ($object->list1 as $element) { $this->assertSame('str', $element); } - foreach($object->list2 as $element) { + foreach ($object->list2 as $element) { $this->assertInstanceOf(SimpleScalarConstructor::class, $element); } } /** @test */ - public function iCanBuildObjectWithNullableParameterWithoutDefaultValue() + public function iCanBuildObjectWithNullableParameterWithoutDefaultValue(): void { $data = [ 'someString1' => 'some string1', @@ -251,7 +252,7 @@ public function iCanBuildObjectWithNullableParameterWithoutDefaultValue() } /** @test */ - public function iCanBuildObjectWithNullableParameterWithHimValueValue() + public function iCanBuildObjectWithNullableParameterWithHimValueValue(): void { $data = [ 'someString1' => 'some string1', diff --git a/test/Unit/Builder/PhpDocParser/PhpStanTest.php b/test/Unit/Builder/PhpDocParser/PhpStanTest.php new file mode 100644 index 0000000..c98cd5d --- /dev/null +++ b/test/Unit/Builder/PhpDocParser/PhpStanTest.php @@ -0,0 +1,10 @@ + 'some string', From 4049d30bca3352fac2b2164d9021f804b8612109 Mon Sep 17 00:00:00 2001 From: gardziejewskik Date: Sun, 5 Aug 2018 11:14:07 +0200 Subject: [PATCH 23/32] Change PhpStan DocParser interface and unit improvement --- .../PatternGenerator/Anonymous.php | 3 +- src/PhpDocParser.php | 2 +- src/PhpDocParser/PhpStan.php | 7 +- .../Unit/Builder/PhpDocParser/PhpStanTest.php | 179 +++++++++++++++++- 4 files changed, 185 insertions(+), 6 deletions(-) diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Anonymous.php b/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Anonymous.php index 0c8e794..67b0b66 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Anonymous.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Anonymous.php @@ -87,7 +87,8 @@ private function createNode(\ReflectionParameter $parameter, string $phpDoc): No $type = $parameter->getType()->getName(); if ('array' === $type - && $this->phpDocParser->isListOfObject($phpDoc, $parameter)) { + && $this->phpDocParser->isListOfObject($phpDoc, $parameter->getName())) { + $class = $this->phpDocParser->getListType($phpDoc, $parameter); return new Node\ObjectList( diff --git a/src/PhpDocParser.php b/src/PhpDocParser.php index e83cdff..24610ab 100644 --- a/src/PhpDocParser.php +++ b/src/PhpDocParser.php @@ -8,7 +8,7 @@ interface PhpDocParser { public function isListOfObject( string $comment, - ReflectionParameter $parameter + string $parameterName ): bool; public function getListType( diff --git a/src/PhpDocParser/PhpStan.php b/src/PhpDocParser/PhpStan.php index 3eeabcd..a4d6d1f 100644 --- a/src/PhpDocParser/PhpStan.php +++ b/src/PhpDocParser/PhpStan.php @@ -27,12 +27,12 @@ public function __construct( $this->phpParser = $phpParser; } - public function isListOfObject(string $comment, ReflectionParameter $parameter): bool + public function isListOfObject(string $comment, string $parameterName): bool { $node = $this->phpDocParser->parse(new TokenIterator((new Lexer())->tokenize($comment))); foreach ($node->getParamTagValues() as $node) { - if ($node->parameterName === '$' . $parameter->getName()) { + if ($node->parameterName === '$' . $parameterName) { $typeName = $node->type->type->name; if ($this->isScalar($typeName)) { continue; @@ -77,6 +77,7 @@ private function isScalar(string $value): bool { $scalars = [ 'string', + 'bool', 'int', 'float', 'double', @@ -122,7 +123,7 @@ private function getUsesNamespaces(array $uses): array /** @param string[] $namespaces */ private function getFullClassName(string $name, array $namespaces, ReflectionClass $class): string { - if ('\\' === $name[0]) { + if ('\\' === $name[0] || explode('\\', $name)[0] !== $name) { return $name; } diff --git a/test/Unit/Builder/PhpDocParser/PhpStanTest.php b/test/Unit/Builder/PhpDocParser/PhpStanTest.php index c98cd5d..7fca4fb 100644 --- a/test/Unit/Builder/PhpDocParser/PhpStanTest.php +++ b/test/Unit/Builder/PhpDocParser/PhpStanTest.php @@ -2,9 +2,186 @@ namespace RstGroup\ObjectBuilder\Test\Unit\Builder\PhpDocParser; +use PhpParser\Lexer\Emulative; +use PhpParser\ParserFactory; +use PHPStan\PhpDocParser\Parser\ConstExprParser; +use PHPStan\PhpDocParser\Parser\PhpDocParser; +use PHPStan\PhpDocParser\Parser\TypeParser; use PHPUnit\Framework\TestCase; +use ReflectionClass; +use ReflectionParameter; +use RstGroup\ObjectBuilder\PhpDocParser\PhpStan; +use RstGroup\ObjectBuilder\Test\SomeObjectWithEmptyConstructor; + class PhpStanTest extends TestCase { - // TODO + /** @var PhpStan */ + private static $parser; + + public static function setUpBeforeClass() + { + self::$parser = new PhpStan( + new PhpDocParser( + new TypeParser(), + new ConstExprParser() + ), + (new ParserFactory())->create(ParserFactory::PREFER_PHP7, new Emulative([ + 'usedAttributes' => ['comments', 'startLine', 'endLine', 'startFilePos', 'endFilePos'], + ])) + ); + } + + /** + * @test + * @dataProvider commentsWithObjectList + */ + public function whenPhpDockContainListOfObjectThenReturnTrue(string $doc) + { + $containsListOfObjects = self::$parser->isListOfObject($doc, 'list'); + + $this->assertTrue($containsListOfObjects); + } + + /** + * @test + * @dataProvider commentsWithoutObjectList + */ + public function whenPhpDockDoesNotContainListOfObjectThenReturnFalse(string $doc) + { + $containsListOfObjects = self::$parser->isListOfObject($doc, 'list'); + + $this->assertFalse($containsListOfObjects); + } + + /** + * @test + * @dataProvider commentsWithScalarList + */ + public function whenPhpDockContainListOfScalarInsteadObjectThenReturnFalse(string $doc) + { + $containsListOfObjects = self::$parser->isListOfObject($doc, 'list'); + + $this->assertFalse($containsListOfObjects); + } + + /** + * @test + * @dataProvider commentsWithDifferentObjectListDeclaration + */ + public function returnObjectClassOfObjectList(string $doc) + { + $class = self::$parser->getListType( + $doc, + new class extends ReflectionParameter { + public function __construct() {} + + public function getName() + { + return 'list'; + } + + public function getDeclaringClass() + { + return new ReflectionClass(SomeObjectWithEmptyConstructor::class); + } + } + ); + + $this->assertSame(SomeObjectWithEmptyConstructor::class, $class); + } + + /** @return string[][] */ + public function commentsWithObjectList(): array + { + return [ + 'only param' => [ + '/** @param SimpleObject[] $list */', + ], + 'multi params' => [ + '/** + * @param string[] $strings + * @param int $int + * @param SimpleObject[] $list + * @param SimpleObjectTwo[] $collection + */', + ], + 'param and return' => [ + '/** + * @param SimpleObject[] $list + * @return SimpleObject[] + */', + ], + ]; + } + + /** @return string[][] */ + public function commentsWithoutObjectList(): array + { + return [ + 'only return' => [ + '/** @return SimpleObject[] */', + ], + 'multi params' => [ + '/** + * @param string[] $strings + * @param int $int + * @param SimpleObjectTwo[] $collection + */', + ], + 'param and return' => [ + '/** + * @param SimpleObject[] $collection + * @return SimpleObject[] + */', + ], + ]; + } + + /** @return string[][] */ + public function commentsWithScalarList(): array + { + return [ + 'string' => [ + '/** @param string[] $list */', + ], + 'int' => [ + '/** @param int[] $list */', + ], + 'bool' => [ + '/** @param bool[] $list */', + ], + 'float' => [ + '/** @param float[] $list */', + ], + 'double' => [ + '/** @param double[] $list */', + ], + 'mixed' => [ + '/** @param mixed[] $list */', + ], + ]; + } + + /** @return string[][] */ + public function commentsWithDifferentObjectListDeclaration(): array + { + return [ + 'FQCN' => [ + '/** @param ' + . SomeObjectWithEmptyConstructor::class + . '[] $list */', + ], + 'with use statement' => [ + '/** @param SomeObjectWithEmptyConstructor[] $list */', + ], + 'without use statement in same namespace' => [ + '/** @param SomeObjectWithEmptyConstructor[] $list */', + ], +// TODO +// 'with partial use statement' => [ +// '/** @param Object\SomeObjectWithEmptyConstructor[] $list */', +// ], + ]; + } } From 94a9f37dc9293c7481f61fc4dc97b6d3b87befaa Mon Sep 17 00:00:00 2001 From: gardziejewskik Date: Sun, 5 Aug 2018 15:36:09 +0200 Subject: [PATCH 24/32] Better segregation of object for testing --- test/ListOfObjectsWithUseStmtConstructor.php | 21 ---- ...houtUseButWithFQNTypedArrayConstructor.php | 16 --- test/Object/Collection/ScalarConstructor.php | 15 +++ ...larTypedArrayAndObjectListConstructor.php} | 6 +- .../WithScalarTypedArrayConstructor.php} | 4 +- .../Collection/WithUseStmtConstructor.php | 21 ++++ ...houtUseButWithFQNTypedArrayConstructor.php | 16 +++ .../Collection/WithoutUseStmtConstructor.php} | 6 +- .../ComplexHierarchy.php} | 8 +- .../{SomeObject.php => EmptyConstructor.php} | 2 +- .../MixedConstructor.php} | 6 +- .../MixedConstructorWithDefaultValue.php} | 6 +- .../NullableConstructor.php} | 4 +- .../ScalarConstructor.php} | 4 +- ...dObject.php => SecondEmptyConstructor.php} | 2 +- test/SomeObjectWithEmptyConstructor.php | 7 -- .../Factory/CodeGenerator/AnonymousTest.php | 32 +++--- test/Unit/Builder/BuilderTest.php | 103 +++++++++--------- .../Unit/Builder/PhpDocParser/PhpStanTest.php | 14 +-- test/Unit/Builder/ReflectionTest.php | 4 +- 20 files changed, 150 insertions(+), 147 deletions(-) delete mode 100644 test/ListOfObjectsWithUseStmtConstructor.php delete mode 100644 test/ListOfObjectsWithoutUseButWithFQNTypedArrayConstructor.php create mode 100644 test/Object/Collection/ScalarConstructor.php rename test/{ListOfObjectsWithScalarTypedArrayAndObjectListConstructor.php => Object/Collection/WithScalarTypedArrayAndObjectListConstructor.php} (63%) rename test/{ListOfObjectsWithScalarTypedArrayConstructor.php => Object/Collection/WithScalarTypedArrayConstructor.php} (75%) create mode 100644 test/Object/Collection/WithUseStmtConstructor.php create mode 100644 test/Object/Collection/WithoutUseButWithFQNTypedArrayConstructor.php rename test/{ListOfObjectsWithoutUseStmtConstructor.php => Object/Collection/WithoutUseStmtConstructor.php} (55%) rename test/{SomeAggregateRoot.php => Object/ComplexHierarchy.php} (71%) rename test/Object/{SomeObject.php => EmptyConstructor.php} (85%) rename test/{SimpleMixedConstructor.php => Object/MixedConstructor.php} (72%) rename test/{SimpleMixedConstructorWithDefaultValue.php => Object/MixedConstructorWithDefaultValue.php} (71%) rename test/{SimpleNullableConstructor.php => Object/NullableConstructor.php} (82%) rename test/{SimpleScalarConstructor.php => Object/ScalarConstructor.php} (77%) rename test/Object/{SomeSecondObject.php => SecondEmptyConstructor.php} (81%) delete mode 100644 test/SomeObjectWithEmptyConstructor.php diff --git a/test/ListOfObjectsWithUseStmtConstructor.php b/test/ListOfObjectsWithUseStmtConstructor.php deleted file mode 100644 index a6808a0..0000000 --- a/test/ListOfObjectsWithUseStmtConstructor.php +++ /dev/null @@ -1,21 +0,0 @@ -list = $list; - $this->object = new SomeObject(); - } -} diff --git a/test/ListOfObjectsWithoutUseButWithFQNTypedArrayConstructor.php b/test/ListOfObjectsWithoutUseButWithFQNTypedArrayConstructor.php deleted file mode 100644 index 8d06e9c..0000000 --- a/test/ListOfObjectsWithoutUseButWithFQNTypedArrayConstructor.php +++ /dev/null @@ -1,16 +0,0 @@ -list = $list; - } -} diff --git a/test/Object/Collection/ScalarConstructor.php b/test/Object/Collection/ScalarConstructor.php new file mode 100644 index 0000000..6ed4588 --- /dev/null +++ b/test/Object/Collection/ScalarConstructor.php @@ -0,0 +1,15 @@ +someString = $someString; + $this->someInt = $someInt; + } +} diff --git a/test/ListOfObjectsWithScalarTypedArrayAndObjectListConstructor.php b/test/Object/Collection/WithScalarTypedArrayAndObjectListConstructor.php similarity index 63% rename from test/ListOfObjectsWithScalarTypedArrayAndObjectListConstructor.php rename to test/Object/Collection/WithScalarTypedArrayAndObjectListConstructor.php index 1ae15f2..065fe1d 100644 --- a/test/ListOfObjectsWithScalarTypedArrayAndObjectListConstructor.php +++ b/test/Object/Collection/WithScalarTypedArrayAndObjectListConstructor.php @@ -1,15 +1,15 @@ list = $list; + $this->object = new EmptyConstructor(); + } +} diff --git a/test/Object/Collection/WithoutUseButWithFQNTypedArrayConstructor.php b/test/Object/Collection/WithoutUseButWithFQNTypedArrayConstructor.php new file mode 100644 index 0000000..46c8bba --- /dev/null +++ b/test/Object/Collection/WithoutUseButWithFQNTypedArrayConstructor.php @@ -0,0 +1,16 @@ +list = $list; + } +} diff --git a/test/ListOfObjectsWithoutUseStmtConstructor.php b/test/Object/Collection/WithoutUseStmtConstructor.php similarity index 55% rename from test/ListOfObjectsWithoutUseStmtConstructor.php rename to test/Object/Collection/WithoutUseStmtConstructor.php index 6c520b1..27d9403 100644 --- a/test/ListOfObjectsWithoutUseStmtConstructor.php +++ b/test/Object/Collection/WithoutUseStmtConstructor.php @@ -1,13 +1,13 @@ someString = $someString; diff --git a/test/Object/SomeObject.php b/test/Object/EmptyConstructor.php similarity index 85% rename from test/Object/SomeObject.php rename to test/Object/EmptyConstructor.php index a1ac8fe..bc11305 100644 --- a/test/Object/SomeObject.php +++ b/test/Object/EmptyConstructor.php @@ -2,7 +2,7 @@ namespace RstGroup\ObjectBuilder\Test\Object; -class SomeObject +class EmptyConstructor { public function __construct() { diff --git a/test/SimpleMixedConstructor.php b/test/Object/MixedConstructor.php similarity index 72% rename from test/SimpleMixedConstructor.php rename to test/Object/MixedConstructor.php index 0954ec0..96f6607 100644 --- a/test/SimpleMixedConstructor.php +++ b/test/Object/MixedConstructor.php @@ -1,14 +1,14 @@ someString = $someString; $this->someInt = $someInt; diff --git a/test/SimpleMixedConstructorWithDefaultValue.php b/test/Object/MixedConstructorWithDefaultValue.php similarity index 71% rename from test/SimpleMixedConstructorWithDefaultValue.php rename to test/Object/MixedConstructorWithDefaultValue.php index dc1e8ca..7b746f7 100644 --- a/test/SimpleMixedConstructorWithDefaultValue.php +++ b/test/Object/MixedConstructorWithDefaultValue.php @@ -1,15 +1,15 @@ create($class); $this->assertSame( 'return function(array $data) use ($class): object { - return new RstGroup\ObjectBuilder\Test\SomeObjectWithEmptyConstructor(); + return new RstGroup\ObjectBuilder\Test\Object\EmptyConstructor(); };', $blueprint ); @@ -57,7 +57,7 @@ public function iCanGenerateSimpleObjectClosure(): void /** @test */ public function iCanBuildSimpleObjectWithScalarValuesInConstructor(): void { - $class = SimpleScalarConstructor::class; + $class = ScalarConstructor::class; $blueprint = self::$factory->create($class); @@ -65,7 +65,7 @@ public function iCanBuildSimpleObjectWithScalarValuesInConstructor(): void $this->assertSame( 'return function(array $data) use ($class): object { - return new RstGroup\ObjectBuilder\Test\SimpleScalarConstructor($data[\'someString\'], $data[\'someInt\']); + return new RstGroup\ObjectBuilder\Test\Object\ScalarConstructor($data[\'someString\'], $data[\'someInt\']); };', $blueprint ); @@ -75,7 +75,7 @@ public function iCanBuildSimpleObjectWithScalarValuesInConstructor(): void /** @test */ public function iCanBuildSimpleObjectWithDefaultValuesInConstructor(): void { - $class = SimpleMixedConstructorWithDefaultValue::class; + $class = MixedConstructorWithDefaultValue::class; $blueprint = self::$factory->create($class); @@ -88,7 +88,7 @@ public function iCanBuildSimpleObjectWithDefaultValuesInConstructor(): void ); $data = array_merge($default, $data); - return new RstGroup\ObjectBuilder\Test\SimpleMixedConstructorWithDefaultValue(new RstGroup\ObjectBuilder\Test\SomeObjectWithEmptyConstructor(), $data[\'someString\'], $data[\'someInt\']); + return new RstGroup\ObjectBuilder\Test\Object\MixedConstructorWithDefaultValue(new RstGroup\ObjectBuilder\Test\Object\EmptyConstructor(), $data[\'someString\'], $data[\'someInt\']); };', $blueprint ); @@ -98,7 +98,7 @@ public function iCanBuildSimpleObjectWithDefaultValuesInConstructor(): void /** @test */ public function iCanBuildAdvancedObjectHierarchy(): void { - $class = SomeAggregateRoot::class; + $class = ComplexHierarchy::class; $blueprint = self::$factory->create($class); @@ -106,7 +106,7 @@ public function iCanBuildAdvancedObjectHierarchy(): void $this->assertSame( 'return function(array $data) use ($class): object { - return new RstGroup\ObjectBuilder\Test\SomeAggregateRoot($data[\'someString\'], new RstGroup\ObjectBuilder\Test\SimpleScalarConstructor($data[\'simpleObject1\'][\'someString\'], $data[\'simpleObject1\'][\'someInt\']), new RstGroup\ObjectBuilder\Test\SimpleMixedConstructor($data[\'simpleObject2\'][\'someString\'], $data[\'simpleObject2\'][\'someInt\'], new RstGroup\ObjectBuilder\Test\SomeObjectWithEmptyConstructor()), $data[\'someInt\']); + return new RstGroup\ObjectBuilder\Test\Object\ComplexHierarchy($data[\'someString\'], new RstGroup\ObjectBuilder\Test\Object\ScalarConstructor($data[\'simpleObject1\'][\'someString\'], $data[\'simpleObject1\'][\'someInt\']), new RstGroup\ObjectBuilder\Test\Object\MixedConstructor($data[\'simpleObject2\'][\'someString\'], $data[\'simpleObject2\'][\'someInt\'], new RstGroup\ObjectBuilder\Test\Object\EmptyConstructor()), $data[\'someInt\']); };', $blueprint ); @@ -116,17 +116,17 @@ public function iCanBuildAdvancedObjectHierarchy(): void /** @test */ public function iCanBuildObjectWithObjectCollectionWithoutUseInConstructor(): void { - $class = ListOfObjectsWithoutUseStmtConstructor::class; + $class = WithoutUseStmtConstructor::class; $blueprint = self::$factory->create($class); // @codingStandardsIgnoreStart $this->assertSame('return function(array $data) use ($class): object { - return new RstGroup\ObjectBuilder\Test\ListOfObjectsWithoutUseStmtConstructor((function (array $list) { + return new RstGroup\ObjectBuilder\Test\Object\Collection\WithoutUseStmtConstructor((function (array $list) { $arr = []; foreach ($list as $data) { - $arr[] = new RstGroup\ObjectBuilder\Test\SimpleScalarConstructor($data[\'someString\'], $data[\'someInt\']); + $arr[] = new RstGroup\ObjectBuilder\Test\Object\Collection\ScalarConstructor($data[\'someString\'], $data[\'someInt\']); } return $arr; diff --git a/test/Unit/Builder/BuilderTest.php b/test/Unit/Builder/BuilderTest.php index 70ad884..a2d01eb 100644 --- a/test/Unit/Builder/BuilderTest.php +++ b/test/Unit/Builder/BuilderTest.php @@ -4,19 +4,14 @@ use PHPUnit\Framework\TestCase; use RstGroup\ObjectBuilder\Builder; -use RstGroup\ObjectBuilder\Test\ListOfObjectsWithoutUseButWithFQNTypedArrayConstructor; -use RstGroup\ObjectBuilder\Test\ListOfObjectsWithoutUseStmtConstructor; -use RstGroup\ObjectBuilder\Test\ListOfObjectsWithScalarTypedArrayAndObjectListConstructor; -use RstGroup\ObjectBuilder\Test\ListOfObjectsWithScalarTypedArrayConstructor; -use RstGroup\ObjectBuilder\Test\ListOfObjectsWithUseStmtConstructor; -use RstGroup\ObjectBuilder\Test\Object\SomeObject; -use RstGroup\ObjectBuilder\Test\Object\SomeSecondObject; -use RstGroup\ObjectBuilder\Test\SimpleMixedConstructor; -use RstGroup\ObjectBuilder\Test\SimpleMixedConstructorWithDefaultValue; -use RstGroup\ObjectBuilder\Test\SimpleNullableConstructor; -use RstGroup\ObjectBuilder\Test\SimpleScalarConstructor; -use RstGroup\ObjectBuilder\Test\SomeAggregateRoot; -use RstGroup\ObjectBuilder\Test\SomeObjectWithEmptyConstructor; +use RstGroup\ObjectBuilder\Test\Object\Collection; +use RstGroup\ObjectBuilder\Test\Object\EmptyConstructor; +use RstGroup\ObjectBuilder\Test\Object\SecondEmptyConstructor; +use RstGroup\ObjectBuilder\Test\Object\MixedConstructor; +use RstGroup\ObjectBuilder\Test\Object\MixedConstructorWithDefaultValue; +use RstGroup\ObjectBuilder\Test\Object\NullableConstructor; +use RstGroup\ObjectBuilder\Test\Object\ScalarConstructor; +use RstGroup\ObjectBuilder\Test\Object\ComplexHierarchy; // TODO move to component abstract class BuilderTest extends TestCase @@ -31,12 +26,12 @@ public function iCanBuildSimpleObjectWithScalarValuesInConstructor(): void 'someString' => 'some string', 'someInt' => 999, ]; - $class = SimpleScalarConstructor::class; + $class = ScalarConstructor::class; - /** @var SimpleScalarConstructor $object */ + /** @var ScalarConstructor $object */ $object = static::$builder->build($class, $data); - $this->assertInstanceOf(SimpleScalarConstructor::class, $object); + $this->assertInstanceOf(ScalarConstructor::class, $object); $this->assertSame('some string', $object->someString); $this->assertSame(999, $object->someInt); } @@ -49,15 +44,15 @@ public function iCanBuildSimpleObjectWithScalarAndObjectValuesInConstructor(): v 'someInt' => 999, 'someObject' => [], ]; - $class = SimpleMixedConstructor::class; + $class = MixedConstructor::class; - /** @var SimpleMixedConstructor $object */ + /** @var MixedConstructor $object */ $object = static::$builder->build($class, $data); - $this->assertInstanceOf(SimpleMixedConstructor::class, $object); + $this->assertInstanceOf(MixedConstructor::class, $object); $this->assertSame('some string', $object->someString); $this->assertSame(999, $object->someInt); - $this->assertInstanceOf(SomeObjectWithEmptyConstructor::class, $object->someObject); + $this->assertInstanceOf(EmptyConstructor::class, $object->someObject); } /** @test */ @@ -66,15 +61,15 @@ public function iCanBuildSimpleObjectWithDefaultValuesInConstructor(): void $data = [ 'someObject' => [], ]; - $class = SimpleMixedConstructorWithDefaultValue::class; + $class = MixedConstructorWithDefaultValue::class; - /** @var SimpleMixedConstructorWithDefaultValue $object */ + /** @var MixedConstructorWithDefaultValue $object */ $object = static::$builder->build($class, $data); - $this->assertInstanceOf(SimpleMixedConstructorWithDefaultValue::class, $object); + $this->assertInstanceOf(MixedConstructorWithDefaultValue::class, $object); $this->assertSame('some string', $object->someString); $this->assertSame(999, $object->someInt); - $this->assertInstanceOf(SomeObjectWithEmptyConstructor::class, $object->someObject); + $this->assertInstanceOf(EmptyConstructor::class, $object->someObject); } /** @test */ @@ -92,15 +87,15 @@ public function iCanBuildObjectWithObjectCollectionWithoutUseInConstructor(): vo ], ], ]; - $class = ListOfObjectsWithoutUseStmtConstructor::class; + $class = Collection\WithoutUseStmtConstructor::class; - /** @var ListOfObjectsWithoutUseStmtConstructor $object */ + /** @var Collection\WithoutUseStmtConstructor $object */ $object = static::$builder->build($class, $data); - $this->assertInstanceOf(ListOfObjectsWithoutUseStmtConstructor::class, $object); + $this->assertInstanceOf(Collection\WithoutUseStmtConstructor::class, $object); $this->assertCount(2, $object->list); foreach ($object->list as $element) { - $this->assertInstanceOf(SimpleScalarConstructor::class, $element); + $this->assertInstanceOf(Collection\ScalarConstructor::class, $element); } } @@ -113,15 +108,15 @@ public function iCanBuildObjectWithObjectCollectionWithUseInConstructor(): void [], ], ]; - $class = ListOfObjectsWithUseStmtConstructor::class; + $class = Collection\WithUseStmtConstructor::class; - /** @var ListOfObjectsWithUseStmtConstructor $object */ + /** @var Collection\WithUseStmtConstructor $object */ $object = static::$builder->build($class, $data); - $this->assertInstanceOf(ListOfObjectsWithUseStmtConstructor::class, $object); + $this->assertInstanceOf(Collection\WithUseStmtConstructor::class, $object); $this->assertCount(2, $object->list); foreach ($object->list as $element) { - $this->assertInstanceOf(SomeSecondObject::class, $element); + $this->assertInstanceOf(SecondEmptyConstructor::class, $element); } } @@ -134,15 +129,15 @@ public function iCanBuildObjectWithObjectCollectionWithoutUseButWithFQNTypedArra [], ], ]; - $class = ListOfObjectsWithoutUseButWithFQNTypedArrayConstructor::class; + $class = Collection\WithoutUseButWithFQNTypedArrayConstructor::class; - /** @var ListOfObjectsWithoutUseButWithFQNTypedArrayConstructor $object */ + /** @var Collection\WithoutUseButWithFQNTypedArrayConstructor $object */ $object = static::$builder->build($class, $data); - $this->assertInstanceOf(ListOfObjectsWithoutUseButWithFQNTypedArrayConstructor::class, $object); + $this->assertInstanceOf(Collection\WithoutUseButWithFQNTypedArrayConstructor::class, $object); $this->assertCount(2, $object->list); foreach ($object->list as $element) { - $this->assertInstanceOf(SomeObject::class, $element); + $this->assertInstanceOf(EmptyConstructor::class, $element); } } @@ -162,16 +157,16 @@ public function iCanBuildAdvancedObjectHierarchy(): void ], 'someInt' => 3, ]; - $class = SomeAggregateRoot::class; + $class = ComplexHierarchy::class; - /** @var SomeAggregateRoot $object */ + /** @var ComplexHierarchy $object */ $object = static::$builder->build($class, $data); - $this->assertInstanceOf(SomeAggregateRoot::class, $object); + $this->assertInstanceOf(ComplexHierarchy::class, $object); $this->assertSame('some string3', $object->someString); $this->assertSame(3, $object->someInt); - $this->assertInstanceOf(SimpleScalarConstructor::class, $object->simpleObject1); - $this->assertInstanceOf(SimpleMixedConstructor::class, $object->simpleObject2); + $this->assertInstanceOf(ScalarConstructor::class, $object->simpleObject1); + $this->assertInstanceOf(MixedConstructor::class, $object->simpleObject2); $this->assertSame(1, $object->simpleObject1->someInt); $this->assertSame('some string1', $object->simpleObject1->someString); $this->assertSame(2, $object->simpleObject2->someInt); @@ -185,12 +180,12 @@ public function iCanBuildObjectWithScalarCollectionTypedArrayInConstructor(): vo 'list1' => ['str', 'str'], 'list2' => ['str', 'str'], ]; - $class = ListOfObjectsWithScalarTypedArrayConstructor::class; + $class = Collection\WithScalarTypedArrayConstructor::class; - /** @var ListOfObjectsWithScalarTypedArrayConstructor $object */ + /** @var Collection\WithScalarTypedArrayConstructor $object */ $object = static::$builder->build($class, $data); - $this->assertInstanceOf(ListOfObjectsWithScalarTypedArrayConstructor::class, $object); + $this->assertInstanceOf(Collection\WithScalarTypedArrayConstructor::class, $object); $this->assertCount(2, $object->list1); $this->assertCount(2, $object->list2); foreach ($object->list1 as $element) { @@ -217,19 +212,19 @@ public function iCanBuildObjectWithBothScalarAndObjectCollectionTypedArrayInCons ], ], ]; - $class = ListOfObjectsWithScalarTypedArrayAndObjectListConstructor::class; + $class = Collection\WithScalarTypedArrayAndObjectListConstructor::class; - /** @var ListOfObjectsWithScalarTypedArrayAndObjectListConstructor $object */ + /** @var Collection\WithScalarTypedArrayAndObjectListConstructor $object */ $object = static::$builder->build($class, $data); - $this->assertInstanceOf(ListOfObjectsWithScalarTypedArrayAndObjectListConstructor::class, $object); + $this->assertInstanceOf(Collection\WithScalarTypedArrayAndObjectListConstructor::class, $object); $this->assertCount(2, $object->list1); $this->assertCount(2, $object->list2); foreach ($object->list1 as $element) { $this->assertSame('str', $element); } foreach ($object->list2 as $element) { - $this->assertInstanceOf(SimpleScalarConstructor::class, $element); + $this->assertInstanceOf(Collection\ScalarConstructor::class, $element); } } @@ -240,12 +235,12 @@ public function iCanBuildObjectWithNullableParameterWithoutDefaultValue(): void 'someString1' => 'some string1', 'someString2' => 'some string2', ]; - $class = SimpleNullableConstructor::class; + $class = NullableConstructor::class; - /** @var SimpleNullableConstructor $object */ + /** @var NullableConstructor $object */ $object = static::$builder->build($class, $data); - $this->assertInstanceOf(SimpleNullableConstructor::class, $object); + $this->assertInstanceOf(NullableConstructor::class, $object); $this->assertSame('some string1', $object->someString1); $this->assertSame('some string2', $object->someString2); $this->assertNull($object->someInt); @@ -259,12 +254,12 @@ public function iCanBuildObjectWithNullableParameterWithHimValueValue(): void 'someInt' => 123, 'someString2' => 'some string2', ]; - $class = SimpleNullableConstructor::class; + $class = NullableConstructor::class; - /** @var SimpleNullableConstructor $object */ + /** @var NullableConstructor $object */ $object = static::$builder->build($class, $data); - $this->assertInstanceOf(SimpleNullableConstructor::class, $object); + $this->assertInstanceOf(NullableConstructor::class, $object); $this->assertSame('some string1', $object->someString1); $this->assertSame('some string2', $object->someString2); $this->assertSame(123, $object->someInt); diff --git a/test/Unit/Builder/PhpDocParser/PhpStanTest.php b/test/Unit/Builder/PhpDocParser/PhpStanTest.php index 7fca4fb..1a60d1f 100644 --- a/test/Unit/Builder/PhpDocParser/PhpStanTest.php +++ b/test/Unit/Builder/PhpDocParser/PhpStanTest.php @@ -11,7 +11,7 @@ use ReflectionClass; use ReflectionParameter; use RstGroup\ObjectBuilder\PhpDocParser\PhpStan; -use RstGroup\ObjectBuilder\Test\SomeObjectWithEmptyConstructor; +use RstGroup\ObjectBuilder\Test\Object\EmptyConstructor; class PhpStanTest extends TestCase @@ -83,12 +83,12 @@ public function getName() public function getDeclaringClass() { - return new ReflectionClass(SomeObjectWithEmptyConstructor::class); + return new ReflectionClass(EmptyConstructor::class); } } ); - $this->assertSame(SomeObjectWithEmptyConstructor::class, $class); + $this->assertSame(EmptyConstructor::class, $class); } /** @return string[][] */ @@ -169,18 +169,18 @@ public function commentsWithDifferentObjectListDeclaration(): array return [ 'FQCN' => [ '/** @param ' - . SomeObjectWithEmptyConstructor::class + . EmptyConstructor::class . '[] $list */', ], 'with use statement' => [ - '/** @param SomeObjectWithEmptyConstructor[] $list */', + '/** @param EmptyConstructor[] $list */', ], 'without use statement in same namespace' => [ - '/** @param SomeObjectWithEmptyConstructor[] $list */', + '/** @param EmptyConstructor[] $list */', ], // TODO // 'with partial use statement' => [ -// '/** @param Object\SomeObjectWithEmptyConstructor[] $list */', +// '/** @param Object\EmptyConstructor[] $list */', // ], ]; } diff --git a/test/Unit/Builder/ReflectionTest.php b/test/Unit/Builder/ReflectionTest.php index 6ea9606..b9109e0 100644 --- a/test/Unit/Builder/ReflectionTest.php +++ b/test/Unit/Builder/ReflectionTest.php @@ -5,7 +5,7 @@ use RstGroup\ObjectBuilder\Builder\ParameterNameStrategy\Simple; use RstGroup\ObjectBuilder\Builder\Reflection; use RstGroup\ObjectBuilder\BuildingError; -use RstGroup\ObjectBuilder\Test\SimpleMixedConstructor; +use RstGroup\ObjectBuilder\Test\MixedConstructor; class ReflectionTest extends BuilderTest { @@ -20,7 +20,7 @@ public function tooFewArgumentsWillTHrowBuildingException(): void 'someString' => 'some string', 'someInt' => 999, ]; - $class = SimpleMixedConstructor::class; + $class = MixedConstructor::class; $this->expectException(BuildingError::class); From 9b48cf49e43cfa458da7404d59d55e4f1cc454c8 Mon Sep 17 00:00:00 2001 From: gardziejewskik Date: Sun, 5 Aug 2018 15:42:19 +0200 Subject: [PATCH 25/32] Move some tests to component Improve travis build --- .travis.yml | 2 ++ composer.json | 4 ++++ phpunit.xml | 3 +++ test/{Unit => Component}/Builder/BlueprintTest.php | 2 +- test/{Unit => Component}/Builder/BuilderTest.php | 7 +++---- test/{Unit => Component}/Builder/ReflectionTest.php | 4 ++-- 6 files changed, 15 insertions(+), 7 deletions(-) rename test/{Unit => Component}/Builder/BlueprintTest.php (95%) rename test/{Unit => Component}/Builder/BuilderTest.php (99%) rename test/{Unit => Component}/Builder/ReflectionTest.php (85%) diff --git a/.travis.yml b/.travis.yml index 236e923..a7d81cc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,6 +32,8 @@ install: script: - if [[ $CS_CHECK == 'true' ]]; then composer 'cs-check' ; fi - if [[ $TEST_COVERAGE == 'true' ]]; then composer 'test-unit-coverage' ; else composer 'test-unit' ; fi + - if [[ $TEST_COVERAGE == 'true' ]]; then composer 'test-component-coverage' ; else composer 'test-component' ; fi + - if [[ $TEST_COVERAGE == 'true' ]]; then composer 'test-integration-coverage' ; else composer 'test-integration' ; fi after_script: - if [[ $TEST_COVERAGE == 'true' ]]; then vendor/bin/php-coveralls -v ; fi diff --git a/composer.json b/composer.json index 5952b48..ebe39ce 100644 --- a/composer.json +++ b/composer.json @@ -31,6 +31,10 @@ "scripts": { "test-unit": "phpunit -c phpunit.xml --testsuite unit --colors=always ", "test-unit-coverage": "phpunit -c phpunit.xml --testsuite unit --coverage-php test/result/unit.cov --colors=never", + "test-component": "phpunit -c phpunit.xml --testsuite component --colors=always ", + "test-component-coverage": "phpunit -c phpunit.xml --testsuite component --coverage-php test/result/component.cov --colors=never", + "test-integration": "phpunit -c phpunit.xml --testsuite integration --colors=always ", + "test-integration-coverage": "phpunit -c phpunit.xml --testsuite integration --coverage-php test/result/integration.cov --colors=never", "cs-phpstan": [ "phpstan analyse --level=max --ansi --no-progress -c phpstan.neon src" ], diff --git a/phpunit.xml b/phpunit.xml index 30c5d27..8a6fe84 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -15,6 +15,9 @@ test/Unit/ + + test/Component/ + test/Integration/ diff --git a/test/Unit/Builder/BlueprintTest.php b/test/Component/Builder/BlueprintTest.php similarity index 95% rename from test/Unit/Builder/BlueprintTest.php rename to test/Component/Builder/BlueprintTest.php index 4c7660c..eca780f 100644 --- a/test/Unit/Builder/BlueprintTest.php +++ b/test/Component/Builder/BlueprintTest.php @@ -1,6 +1,6 @@ Date: Sun, 5 Aug 2018 17:43:01 +0200 Subject: [PATCH 26/32] Fix CS --- .../PatternGenerator/Anonymous.php | 1 - src/PhpDocParser/PhpStan.php | 2 +- .../Unit/Builder/PhpDocParser/PhpStanTest.php | 19 ++++++++++--------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Anonymous.php b/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Anonymous.php index 67b0b66..b8f7ba5 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Anonymous.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Anonymous.php @@ -88,7 +88,6 @@ private function createNode(\ReflectionParameter $parameter, string $phpDoc): No if ('array' === $type && $this->phpDocParser->isListOfObject($phpDoc, $parameter->getName())) { - $class = $this->phpDocParser->getListType($phpDoc, $parameter); return new Node\ObjectList( diff --git a/src/PhpDocParser/PhpStan.php b/src/PhpDocParser/PhpStan.php index a4d6d1f..ca3b0ae 100644 --- a/src/PhpDocParser/PhpStan.php +++ b/src/PhpDocParser/PhpStan.php @@ -32,7 +32,7 @@ public function isListOfObject(string $comment, string $parameterName): bool $node = $this->phpDocParser->parse(new TokenIterator((new Lexer())->tokenize($comment))); foreach ($node->getParamTagValues() as $node) { - if ($node->parameterName === '$' . $parameterName) { + if ('$' . $parameterName === $node->parameterName) { $typeName = $node->type->type->name; if ($this->isScalar($typeName)) { continue; diff --git a/test/Unit/Builder/PhpDocParser/PhpStanTest.php b/test/Unit/Builder/PhpDocParser/PhpStanTest.php index 1a60d1f..d218879 100644 --- a/test/Unit/Builder/PhpDocParser/PhpStanTest.php +++ b/test/Unit/Builder/PhpDocParser/PhpStanTest.php @@ -13,13 +13,12 @@ use RstGroup\ObjectBuilder\PhpDocParser\PhpStan; use RstGroup\ObjectBuilder\Test\Object\EmptyConstructor; - class PhpStanTest extends TestCase { /** @var PhpStan */ private static $parser; - public static function setUpBeforeClass() + public static function setUpBeforeClass(): void { self::$parser = new PhpStan( new PhpDocParser( @@ -36,7 +35,7 @@ public static function setUpBeforeClass() * @test * @dataProvider commentsWithObjectList */ - public function whenPhpDockContainListOfObjectThenReturnTrue(string $doc) + public function whenPhpDockContainListOfObjectThenReturnTrue(string $doc): void { $containsListOfObjects = self::$parser->isListOfObject($doc, 'list'); @@ -47,7 +46,7 @@ public function whenPhpDockContainListOfObjectThenReturnTrue(string $doc) * @test * @dataProvider commentsWithoutObjectList */ - public function whenPhpDockDoesNotContainListOfObjectThenReturnFalse(string $doc) + public function whenPhpDockDoesNotContainListOfObjectThenReturnFalse(string $doc): void { $containsListOfObjects = self::$parser->isListOfObject($doc, 'list'); @@ -58,7 +57,7 @@ public function whenPhpDockDoesNotContainListOfObjectThenReturnFalse(string $doc * @test * @dataProvider commentsWithScalarList */ - public function whenPhpDockContainListOfScalarInsteadObjectThenReturnFalse(string $doc) + public function whenPhpDockContainListOfScalarInsteadObjectThenReturnFalse(string $doc): void { $containsListOfObjects = self::$parser->isListOfObject($doc, 'list'); @@ -69,19 +68,21 @@ public function whenPhpDockContainListOfScalarInsteadObjectThenReturnFalse(strin * @test * @dataProvider commentsWithDifferentObjectListDeclaration */ - public function returnObjectClassOfObjectList(string $doc) + public function returnObjectClassOfObjectList(string $doc): void { $class = self::$parser->getListType( $doc, new class extends ReflectionParameter { - public function __construct() {} + public function __construct() + { + } - public function getName() + public function getName(): string { return 'list'; } - public function getDeclaringClass() + public function getDeclaringClass(): ReflectionClass { return new ReflectionClass(EmptyConstructor::class); } From bfb49a749659d434e84766e5f533ee9ed30bc67c Mon Sep 17 00:00:00 2001 From: gardziejewskik Date: Sun, 5 Aug 2018 19:04:15 +0200 Subject: [PATCH 27/32] PhpDocParser tests --- src/PhpDocParser/PhpStan.php | 4 +- .../Unit/Builder/PhpDocParser/PhpStanTest.php | 67 ++++++++++--------- 2 files changed, 38 insertions(+), 33 deletions(-) diff --git a/src/PhpDocParser/PhpStan.php b/src/PhpDocParser/PhpStan.php index ca3b0ae..76b4e4b 100644 --- a/src/PhpDocParser/PhpStan.php +++ b/src/PhpDocParser/PhpStan.php @@ -128,10 +128,10 @@ private function getFullClassName(string $name, array $namespaces, ReflectionCla } if (0 === count($namespaces)) { - return $class->getNamespaceName() . '\\' . $name; + return '\\' . $class->getNamespaceName() . '\\' . $name; } - return $this->getNamespaceForClass($name, $namespaces); + return '\\' . $this->getNamespaceForClass($name, $namespaces); } /** diff --git a/test/Unit/Builder/PhpDocParser/PhpStanTest.php b/test/Unit/Builder/PhpDocParser/PhpStanTest.php index d218879..66034e3 100644 --- a/test/Unit/Builder/PhpDocParser/PhpStanTest.php +++ b/test/Unit/Builder/PhpDocParser/PhpStanTest.php @@ -11,7 +11,9 @@ use ReflectionClass; use ReflectionParameter; use RstGroup\ObjectBuilder\PhpDocParser\PhpStan; -use RstGroup\ObjectBuilder\Test\Object\EmptyConstructor; +use RstGroup\ObjectBuilder\Test\Object\Collection\WithoutUseButWithFQNTypedArrayConstructor; +use RstGroup\ObjectBuilder\Test\Object\Collection\WithoutUseStmtConstructor; +use RstGroup\ObjectBuilder\Test\Object\Collection\WithUseStmtConstructor; class PhpStanTest extends TestCase { @@ -68,28 +70,14 @@ public function whenPhpDockContainListOfScalarInsteadObjectThenReturnFalse(strin * @test * @dataProvider commentsWithDifferentObjectListDeclaration */ - public function returnObjectClassOfObjectList(string $doc): void - { - $class = self::$parser->getListType( - $doc, - new class extends ReflectionParameter { - public function __construct() - { - } - - public function getName(): string - { - return 'list'; - } - - public function getDeclaringClass(): ReflectionClass - { - return new ReflectionClass(EmptyConstructor::class); - } - } - ); - - $this->assertSame(EmptyConstructor::class, $class); + public function returnObjectClassOfObjectList( + string $doc, + ReflectionParameter $param, + string $expectedClass + ): void { + $class = self::$parser->getListType($doc, $param); + + $this->assertSame($expectedClass, $class); } /** @return string[][] */ @@ -164,24 +152,41 @@ public function commentsWithScalarList(): array ]; } - /** @return string[][] */ + /** @return mixed[][] */ public function commentsWithDifferentObjectListDeclaration(): array { + $constructors = [ + 'FQCN' => (new ReflectionClass( + WithoutUseButWithFQNTypedArrayConstructor::class + ))->getConstructor(), + 'with use statement' => (new ReflectionClass( + WithUseStmtConstructor::class + ))->getConstructor(), + 'without use statement in same namespace' => (new ReflectionClass( + WithoutUseStmtConstructor::class + ))->getConstructor() + ]; + return [ 'FQCN' => [ - '/** @param ' - . EmptyConstructor::class - . '[] $list */', + $constructors['FQCN']->getDocComment(), + $constructors['FQCN']->getParameters()[0], + '\RstGroup\ObjectBuilder\Test\Object\EmptyConstructor', ], 'with use statement' => [ - '/** @param EmptyConstructor[] $list */', + $constructors['with use statement']->getDocComment(), + $constructors['with use statement']->getParameters()[0], + '\RstGroup\ObjectBuilder\Test\Object\SecondEmptyConstructor', ], 'without use statement in same namespace' => [ - '/** @param EmptyConstructor[] $list */', + $constructors['without use statement in same namespace']->getDocComment(), + $constructors['without use statement in same namespace']->getParameters()[0], + '\RstGroup\ObjectBuilder\Test\Object\Collection\ScalarConstructor', ], // TODO -// 'with partial use statement' => [ -// '/** @param Object\EmptyConstructor[] $list */', +// 'partial with use statement' => [ +// ], +// 'partial without use statement' => [ // ], ]; } From 30589be521ea046f44b385cba3c8d8fb9ef94506 Mon Sep 17 00:00:00 2001 From: gardziejewskik Date: Sun, 5 Aug 2018 19:27:48 +0200 Subject: [PATCH 28/32] Improve CodeCoverage --- src/PhpDocParser/PhpStan.php | 1 + test/Component/Builder/BuilderTest.php | 12 +++++++++ test/Object/WithoutConstructor.php | 7 +++++ .../Unit/Builder/PhpDocParser/PhpStanTest.php | 27 +++++++++++++++++-- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 test/Object/WithoutConstructor.php diff --git a/src/PhpDocParser/PhpStan.php b/src/PhpDocParser/PhpStan.php index 76b4e4b..9f9f6ad 100644 --- a/src/PhpDocParser/PhpStan.php +++ b/src/PhpDocParser/PhpStan.php @@ -19,6 +19,7 @@ final class PhpStan implements PhpDocParser /** @var Parser */ private $phpParser; + /** @codeCoverageIgnore */ public function __construct( PhpStanPhpDocParser $phpDocParser, Parser $phpParser diff --git a/test/Component/Builder/BuilderTest.php b/test/Component/Builder/BuilderTest.php index 790884a..77902e1 100644 --- a/test/Component/Builder/BuilderTest.php +++ b/test/Component/Builder/BuilderTest.php @@ -12,12 +12,24 @@ use RstGroup\ObjectBuilder\Test\Object\NullableConstructor; use RstGroup\ObjectBuilder\Test\Object\ScalarConstructor; use RstGroup\ObjectBuilder\Test\Object\SecondEmptyConstructor; +use RstGroup\ObjectBuilder\Test\Object\WithoutConstructor; abstract class BuilderTest extends TestCase { /** @var Builder */ protected static $builder; + /** @test */ + public function iCanBuildObjectWithoutConstructor(): void + { + $class = WithoutConstructor::class; + + /** @var WithoutConstructor $object */ + $object = static::$builder->build($class, []); + + $this->assertInstanceOf(WithoutConstructor::class, $object); + } + /** @test */ public function iCanBuildSimpleObjectWithScalarValuesInConstructor(): void { diff --git a/test/Object/WithoutConstructor.php b/test/Object/WithoutConstructor.php new file mode 100644 index 0000000..24eafbe --- /dev/null +++ b/test/Object/WithoutConstructor.php @@ -0,0 +1,7 @@ +assertSame($expectedClass, $class); } + /** @test */ + public function throwExceptionWhenParameterIsNotDeclaredInPhpDoc() + { + $this->expectException(BuildingError::class); + $paramReflection = new class extends ReflectionParameter + { + public function __construct() + { + } + + public function getName(): string + { + return 'unexistedName'; + } + }; + + self::$parser->getListType( + '/** @param SimpleObject[] $list */', + $paramReflection + ); + } + /** @return string[][] */ public function commentsWithObjectList(): array { @@ -184,9 +207,9 @@ public function commentsWithDifferentObjectListDeclaration(): array '\RstGroup\ObjectBuilder\Test\Object\Collection\ScalarConstructor', ], // TODO -// 'partial with use statement' => [ +// 'partial namespace with use statement' => [ // ], -// 'partial without use statement' => [ +// 'partial namespace without use statement' => [ // ], ]; } From 029d3cce657255a783e34367d90b3d72484b19ab Mon Sep 17 00:00:00 2001 From: gardziejewskik Date: Sun, 5 Aug 2018 19:53:53 +0200 Subject: [PATCH 29/32] Add StoreDecorator for PatternGenerator --- .../CodeGenerator/PatternGenerator/Dummy.php | 22 +++++++++++ .../PatternGenerator/StoreDecorator.php | 25 +++++++++++++ .../{Store.php => PatternStore.php} | 4 +- .../{Store => PatternStore}/Filesystem.php | 10 ++--- .../{Store => PatternStore}/Memory.php | 10 ++--- test/Component/Builder/BlueprintTest.php | 23 +++++++----- .../CodeGenerator/Store/FilesystemTest.php | 9 +++-- .../{ => PatternGenerator}/AnonymousTest.php | 2 +- .../PatternGenerator/StoreDecoratorTest.php | 37 +++++++++++++++++++ .../CodeGenerator/Store/InMemoryTest.php | 7 +++- 10 files changed, 121 insertions(+), 28 deletions(-) create mode 100644 src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Dummy.php create mode 100644 src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/StoreDecorator.php rename src/Builder/Blueprint/Factory/CodeGenerator/{Store.php => PatternStore.php} (71%) rename src/Builder/Blueprint/Factory/CodeGenerator/{Store => PatternStore}/Filesystem.php (75%) rename src/Builder/Blueprint/Factory/CodeGenerator/{Store => PatternStore}/Memory.php (80%) rename test/Unit/Builder/Blueprint/Factory/CodeGenerator/{ => PatternGenerator}/AnonymousTest.php (99%) create mode 100644 test/Unit/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/StoreDecoratorTest.php diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Dummy.php b/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Dummy.php new file mode 100644 index 0000000..e8c50b6 --- /dev/null +++ b/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Dummy.php @@ -0,0 +1,22 @@ +store = $store; + } + + public function create(string $class): string + { + return $this->store[$class]; + } +} diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/StoreDecorator.php b/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/StoreDecorator.php new file mode 100644 index 0000000..523181b --- /dev/null +++ b/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/StoreDecorator.php @@ -0,0 +1,25 @@ +store = $store; + $this->generator = $generator; + } + + public function create(string $class): string + { + return $this->store->get($class) ?? $this->generator->create($class); + } +} diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Store.php b/src/Builder/Blueprint/Factory/CodeGenerator/PatternStore.php similarity index 71% rename from src/Builder/Blueprint/Factory/CodeGenerator/Store.php rename to src/Builder/Blueprint/Factory/CodeGenerator/PatternStore.php index 3f85fc4..108731c 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Store.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/PatternStore.php @@ -2,8 +2,8 @@ namespace RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator; -interface Store +interface PatternStore { - public function get(string $class): ?callable; + public function get(string $class): ?string; public function save(string $class, string $blueprint): void; } diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Store/Filesystem.php b/src/Builder/Blueprint/Factory/CodeGenerator/PatternStore/Filesystem.php similarity index 75% rename from src/Builder/Blueprint/Factory/CodeGenerator/Store/Filesystem.php rename to src/Builder/Blueprint/Factory/CodeGenerator/PatternStore/Filesystem.php index 7987916..d388ee1 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Store/Filesystem.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/PatternStore/Filesystem.php @@ -1,10 +1,10 @@ path = $path; } - public function get(string $class): ?callable + public function get(string $class): ?string { $fileFullPath = $this->path . $class; if (file_exists($fileFullPath)) { - return require $fileFullPath; + return file_get_contents($fileFullPath); } return null; diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Store/Memory.php b/src/Builder/Blueprint/Factory/CodeGenerator/PatternStore/Memory.php similarity index 80% rename from src/Builder/Blueprint/Factory/CodeGenerator/Store/Memory.php rename to src/Builder/Blueprint/Factory/CodeGenerator/PatternStore/Memory.php index 00a5e43..729e024 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Store/Memory.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/PatternStore/Memory.php @@ -1,11 +1,11 @@ store = $blueprints; } - public function get(string $class): ?callable + public function get(string $class): ?string { try { - return eval($this->store[$class]); + return $this->store[$class]; } catch (Throwable $exception) { return null; } diff --git a/test/Component/Builder/BlueprintTest.php b/test/Component/Builder/BlueprintTest.php index eca780f..51873a4 100644 --- a/test/Component/Builder/BlueprintTest.php +++ b/test/Component/Builder/BlueprintTest.php @@ -16,17 +16,20 @@ public static function setUpBeforeClass(): void { static::$builder = new Blueprint( new Blueprint\Factory\CodeGenerator( - new Blueprint\Factory\CodeGenerator\PatternGenerator\Anonymous( - new PhpStan( - new PhpDocParser( - new TypeParser(), - new ConstExprParser() + new Blueprint\Factory\CodeGenerator\PatternGenerator\StoreDecorator( + new Blueprint\Factory\CodeGenerator\PatternStore\Memory(), + new Blueprint\Factory\CodeGenerator\PatternGenerator\Anonymous( + new PhpStan( + new PhpDocParser( + new TypeParser(), + new ConstExprParser() + ), + (new ParserFactory())->create(ParserFactory::PREFER_PHP7, new Emulative([ + 'usedAttributes' => ['comments', 'startLine', 'endLine', 'startFilePos', 'endFilePos'], + ])) ), - (new ParserFactory())->create(ParserFactory::PREFER_PHP7, new Emulative([ - 'usedAttributes' => ['comments', 'startLine', 'endLine', 'startFilePos', 'endFilePos'], - ])) - ), - new Blueprint\Factory\CodeGenerator\Node\Serializer\ArrayAccess() + new Blueprint\Factory\CodeGenerator\Node\Serializer\ArrayAccess() + ) ) ) ); diff --git a/test/Integration/Builder/Blueprint/Factory/CodeGenerator/Store/FilesystemTest.php b/test/Integration/Builder/Blueprint/Factory/CodeGenerator/Store/FilesystemTest.php index ac091f0..0d11dfb 100644 --- a/test/Integration/Builder/Blueprint/Factory/CodeGenerator/Store/FilesystemTest.php +++ b/test/Integration/Builder/Blueprint/Factory/CodeGenerator/Store/FilesystemTest.php @@ -3,7 +3,7 @@ namespace RstGroup\ObjectBuilder\Test\Integration\Builder\Blueprint\Factory\CodeGenerator\Store; use PHPUnit\Framework\TestCase; -use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Store\Filesystem; +use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\PatternStore\Filesystem; class FilesystemTest extends TestCase { @@ -33,12 +33,15 @@ public function iCanGetSavedBlueprintInFilesystem(): void $store = new Filesystem('/tmp/'); file_put_contents( '/tmp/SomeClass', - 'get('SomeClass'); - $this->assertSame('some string', $function()); + $this->assertSame( + 'return function() { return \'some string\'; };', + $function + ); } /** @test */ diff --git a/test/Unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php b/test/Unit/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/AnonymousTest.php similarity index 99% rename from test/Unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php rename to test/Unit/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/AnonymousTest.php index e42ada9..9ecf77c 100644 --- a/test/Unit/Builder/Blueprint/Factory/CodeGenerator/AnonymousTest.php +++ b/test/Unit/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/AnonymousTest.php @@ -1,6 +1,6 @@ 'pattern']), + new Dummy() + ); + + $pattern = $patternGeneratorDecorator->create('class'); + + $this->assertSame('pattern', $pattern); + } + + /** @test */ + public function returnNewCreatedPatternWhenPatterDoesNotExistInMemory(): void + { + $patternGeneratorDecorator = new StoreDecorator( + new Memory(), + new Dummy(['class' => 'pattern']) + ); + + $pattern = $patternGeneratorDecorator->create('class'); + + $this->assertSame('pattern', $pattern); + } +} diff --git a/test/Unit/Builder/Blueprint/Factory/CodeGenerator/Store/InMemoryTest.php b/test/Unit/Builder/Blueprint/Factory/CodeGenerator/Store/InMemoryTest.php index 0bfbc5b..dbab1a5 100644 --- a/test/Unit/Builder/Blueprint/Factory/CodeGenerator/Store/InMemoryTest.php +++ b/test/Unit/Builder/Blueprint/Factory/CodeGenerator/Store/InMemoryTest.php @@ -3,7 +3,7 @@ namespace RstGroup\ObjectBuilder\Test\Unit\Builder\Blueprint\Factory\CodeGenerator\Store; use PHPUnit\Framework\TestCase; -use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Store\Memory; +use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\PatternStore\Memory; class InMemoryTest extends TestCase { @@ -26,7 +26,10 @@ public function iCanGetSavedBlueprintInMemory(): void $function = $store->get('SomeClass'); - $this->assertSame('some string', $function()); + $this->assertSame( + 'return function() { return \'some string\'; };', + $function + ); } /** @test */ From 73aa8d2e2ac1f5762a83a9db4177fec651e6a36a Mon Sep 17 00:00:00 2001 From: gardziejewskik Date: Wed, 15 Aug 2018 11:46:41 +0200 Subject: [PATCH 30/32] Different parameter name strategy for Blueprint --- src/Builder/Blueprint.php | 36 +++++++++++-- .../CodeGenerator/PatternGenerator/Dummy.php | 3 +- .../PatternGenerator/StoreDecorator.php | 2 +- .../CodeGenerator/PatternStore/Filesystem.php | 5 +- .../ParameterNameStrategy/SnakeCase.php | 4 +- test/Component/Builder/BlueprintTest.php | 4 +- test/Component/Builder/BuilderTest.php | 54 +++++++++---------- test/Component/Builder/ReflectionTest.php | 11 ++-- .../PatternGenerator/StoreDecoratorTest.php | 2 +- .../Unit/Builder/PhpDocParser/PhpStanTest.php | 14 ++--- 10 files changed, 86 insertions(+), 49 deletions(-) diff --git a/src/Builder/Blueprint.php b/src/Builder/Blueprint.php index a020128..3be582b 100644 --- a/src/Builder/Blueprint.php +++ b/src/Builder/Blueprint.php @@ -8,11 +8,16 @@ final class Blueprint implements Builder { /** @var Builder\Blueprint\Factory */ private $blueprintFactory; + /** @var ParameterNameStrategy */ + private $strategy; /** @codeCoverageIgnore */ - public function __construct(Builder\Blueprint\Factory $factory) - { + public function __construct( + Builder\Blueprint\Factory $factory, + ParameterNameStrategy $strategy + ) { $this->blueprintFactory = $factory; + $this->strategy = $strategy; } /** @param mixed[] $data */ @@ -20,6 +25,31 @@ public function build(string $class, array $data): object { $blueprint = $this->blueprintFactory->create($class); - return $blueprint($data); + $preparedData = $this->prepareData($data); + + return $blueprint($preparedData); + } + + /** + * @param mixed[] $data + * @return mixed[] + */ + private function prepareData(array $data): array + { + $preparedData = []; + + foreach ($data as $key => $value) { + if (is_array($value)) { + $value = $this->prepareData($value); + } + + if (!is_int($key)) { + $key = $this->strategy->getName($key); + } + + $preparedData[$key] = $value; + } + + return $preparedData; } } diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Dummy.php b/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Dummy.php index e8c50b6..411d6d7 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Dummy.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Dummy.php @@ -1,4 +1,4 @@ -store = $store; diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/StoreDecorator.php b/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/StoreDecorator.php index 523181b..3f95d22 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/StoreDecorator.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/StoreDecorator.php @@ -1,4 +1,4 @@ -path . $class; if (file_exists($fileFullPath)) { - return file_get_contents($fileFullPath); + /** @var string $content */ + $content = file_get_contents($fileFullPath); + + return $content; } return null; diff --git a/src/Builder/ParameterNameStrategy/SnakeCase.php b/src/Builder/ParameterNameStrategy/SnakeCase.php index 0945f7a..c68cc6f 100644 --- a/src/Builder/ParameterNameStrategy/SnakeCase.php +++ b/src/Builder/ParameterNameStrategy/SnakeCase.php @@ -13,10 +13,10 @@ public function isFulfilled(string $parameterName): bool public function getName(string $parameterName): string { - return $this->snakeCaseToCamelCase($parameterName); + return $this->toCamelCase($parameterName); } - private function snakeCaseToCamelCase(string $string): string + private function toCamelCase(string $string): string { $str = str_replace('_', '', ucwords($string, '_')); diff --git a/test/Component/Builder/BlueprintTest.php b/test/Component/Builder/BlueprintTest.php index 51873a4..a1c0b4b 100644 --- a/test/Component/Builder/BlueprintTest.php +++ b/test/Component/Builder/BlueprintTest.php @@ -8,6 +8,7 @@ use PHPStan\PhpDocParser\Parser\PhpDocParser; use PHPStan\PhpDocParser\Parser\TypeParser; use RstGroup\ObjectBuilder\Builder\Blueprint; +use RstGroup\ObjectBuilder\Builder\ParameterNameStrategy\SnakeCase; use RstGroup\ObjectBuilder\PhpDocParser\PhpStan; class BlueprintTest extends BuilderTest @@ -31,7 +32,8 @@ public static function setUpBeforeClass(): void new Blueprint\Factory\CodeGenerator\Node\Serializer\ArrayAccess() ) ) - ) + ), + new SnakeCase() ); } } diff --git a/test/Component/Builder/BuilderTest.php b/test/Component/Builder/BuilderTest.php index 77902e1..eb019b7 100644 --- a/test/Component/Builder/BuilderTest.php +++ b/test/Component/Builder/BuilderTest.php @@ -34,8 +34,8 @@ public function iCanBuildObjectWithoutConstructor(): void public function iCanBuildSimpleObjectWithScalarValuesInConstructor(): void { $data = [ - 'someString' => 'some string', - 'someInt' => 999, + 'some_string' => 'some string', + 'some_int' => 999, ]; $class = ScalarConstructor::class; @@ -51,9 +51,9 @@ public function iCanBuildSimpleObjectWithScalarValuesInConstructor(): void public function iCanBuildSimpleObjectWithScalarAndObjectValuesInConstructor(): void { $data = [ - 'someString' => 'some string', - 'someInt' => 999, - 'someObject' => [], + 'some_string' => 'some string', + 'some_int' => 999, + 'some_object' => [], ]; $class = MixedConstructor::class; @@ -70,7 +70,7 @@ public function iCanBuildSimpleObjectWithScalarAndObjectValuesInConstructor(): v public function iCanBuildSimpleObjectWithDefaultValuesInConstructor(): void { $data = [ - 'someObject' => [], + 'some_object' => [], ]; $class = MixedConstructorWithDefaultValue::class; @@ -89,12 +89,12 @@ public function iCanBuildObjectWithObjectCollectionWithoutUseInConstructor(): vo $data = [ 'list' => [ [ - 'someString' => 'some string1', - 'someInt' => 1, + 'some_string' => 'some string1', + 'some_int' => 1, ], [ - 'someString' => 'some string2', - 'someInt' => 2, + 'some_string' => 'some string2', + 'some_int' => 2, ], ], ]; @@ -156,17 +156,17 @@ public function iCanBuildObjectWithObjectCollectionWithoutUseButWithFQNTypedArra public function iCanBuildAdvancedObjectHierarchy(): void { $data = [ - 'someString' => 'some string3', - 'simpleObject1' => [ - 'someString' => 'some string1', + 'some_string' => 'some string3', + 'simple_object_1' => [ + 'some_string' => 'some string1', 'someInt' => 1, ], - 'simpleObject2' => [ - 'someString' => 'some string2', - 'someInt' => 2, - 'someObject' => [], + 'simple_object_2' => [ + 'some_string' => 'some string2', + 'some_int' => 2, + 'some_object' => [], ], - 'someInt' => 3, + 'some_int' => 3, ]; $class = ComplexHierarchy::class; @@ -214,12 +214,12 @@ public function iCanBuildObjectWithBothScalarAndObjectCollectionTypedArrayInCons 'list1' => ['str', 'str'], 'list2' => [ [ - 'someString' => 'some string1', - 'someInt' => 1, + 'some_string' => 'some string1', + 'some_int' => 1, ], [ - 'someString' => 'some string2', - 'someInt' => 2, + 'some_string' => 'some string2', + 'some_int' => 2, ], ], ]; @@ -243,8 +243,8 @@ public function iCanBuildObjectWithBothScalarAndObjectCollectionTypedArrayInCons public function iCanBuildObjectWithNullableParameterWithoutDefaultValue(): void { $data = [ - 'someString1' => 'some string1', - 'someString2' => 'some string2', + 'some_string_1' => 'some string1', + 'some_string_2' => 'some string2', ]; $class = NullableConstructor::class; @@ -261,9 +261,9 @@ public function iCanBuildObjectWithNullableParameterWithoutDefaultValue(): void public function iCanBuildObjectWithNullableParameterWithHimValueValue(): void { $data = [ - 'someString1' => 'some string1', - 'someInt' => 123, - 'someString2' => 'some string2', + 'some_string_1' => 'some string1', + 'some_int' => 123, + 'some_string_2' => 'some string2', ]; $class = NullableConstructor::class; diff --git a/test/Component/Builder/ReflectionTest.php b/test/Component/Builder/ReflectionTest.php index f7eb68c..5408173 100644 --- a/test/Component/Builder/ReflectionTest.php +++ b/test/Component/Builder/ReflectionTest.php @@ -2,7 +2,7 @@ namespace RstGroup\ObjectBuilder\Test\Component\Builder; -use RstGroup\ObjectBuilder\Builder\ParameterNameStrategy\Simple; +use RstGroup\ObjectBuilder\Builder\ParameterNameStrategy\SnakeCase; use RstGroup\ObjectBuilder\Builder\Reflection; use RstGroup\ObjectBuilder\BuildingError; use RstGroup\ObjectBuilder\Test\Object\MixedConstructor; @@ -11,14 +11,15 @@ class ReflectionTest extends BuilderTest { public static function setUpBeforeClass(): void { - static::$builder = new Reflection(new Simple()); + static::$builder = new Reflection(new SnakeCase()); } - public function tooFewArgumentsWillTHrowBuildingException(): void + /** @test */ + public function tooFewArgumentsWillThrowBuildingException(): void { $data = [ - 'someString' => 'some string', - 'someInt' => 999, + 'some_string' => 'some string', + 'some_int' => 999, ]; $class = MixedConstructor::class; diff --git a/test/Unit/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/StoreDecoratorTest.php b/test/Unit/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/StoreDecoratorTest.php index 5ccbdb3..b25a5b9 100644 --- a/test/Unit/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/StoreDecoratorTest.php +++ b/test/Unit/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/StoreDecoratorTest.php @@ -1,4 +1,4 @@ -expectException(BuildingError::class); $paramReflection = new class extends ReflectionParameter @@ -180,14 +180,14 @@ public function commentsWithDifferentObjectListDeclaration(): array { $constructors = [ 'FQCN' => (new ReflectionClass( - WithoutUseButWithFQNTypedArrayConstructor::class - ))->getConstructor(), + WithoutUseButWithFQNTypedArrayConstructor::class + ))->getConstructor(), 'with use statement' => (new ReflectionClass( - WithUseStmtConstructor::class - ))->getConstructor(), + WithUseStmtConstructor::class + ))->getConstructor(), 'without use statement in same namespace' => (new ReflectionClass( - WithoutUseStmtConstructor::class - ))->getConstructor() + WithoutUseStmtConstructor::class + ))->getConstructor(), ]; return [ From 48f08e66a63a0358a7724e2e283d3d6e4b8663e9 Mon Sep 17 00:00:00 2001 From: gardziejewskik Date: Sat, 18 Aug 2018 09:46:33 +0200 Subject: [PATCH 31/32] Fixes after CR --- .../Blueprint/Factory/CodeGenerator.php | 13 ++++++- .../Blueprint/Factory/CodeGenerator/Node.php | 8 +++- .../Factory/CodeGenerator/Node/ObjectList.php | 7 +++- .../Node/Serializer/ArrayAccess.php | 4 +- .../PatternGenerator/Anonymous.php | 11 ++++-- .../CodeGenerator/PatternStore/Memory.php | 7 ++-- src/Builder/Reflection.php | 12 +----- .../Node/Serializer/ArrayAccessTest.php | 16 ++++---- .../Blueprint/Factory/CodeGeneratorTest.php | 38 +++++++++++++++++++ 9 files changed, 85 insertions(+), 31 deletions(-) create mode 100644 test/Unit/Builder/Blueprint/Factory/CodeGeneratorTest.php diff --git a/src/Builder/Blueprint/Factory/CodeGenerator.php b/src/Builder/Blueprint/Factory/CodeGenerator.php index 1478a99..2a5a0b6 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator.php @@ -4,6 +4,7 @@ use RstGroup\ObjectBuilder\Builder\Blueprint\Factory; use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\PatternGenerator; +use RstGroup\ObjectBuilder\BuildingError; final class CodeGenerator implements Factory { @@ -20,6 +21,16 @@ public function create(string $class): callable { $pattern = $this->generator->create($class); - return eval($pattern); + $blueprint = eval($pattern); + if (! is_callable($blueprint)) { + throw new BuildingError( + sprintf( + 'Generated blueprint is not valid %s', + (string) $blueprint + ) + ); + } + + return $blueprint; } } diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node.php index 3c76285..bb8f201 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Node.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node.php @@ -14,7 +14,7 @@ abstract class Node private $nullable; /** @param mixed $defaultValue */ - public function __construct(string $type, string $name, bool $nullable = false, $defaultValue = null) + public function __construct(string $type, string $name, bool $nullable, $defaultValue) { $this->name = $name; $this->defaultValue = $defaultValue; @@ -32,8 +32,12 @@ public function name(): string return $this->name; } - public function withDefaultValue(): bool + public function hasDefaultValue(): bool { + if ($this->nullable()) { + return true; + } + return null !== $this->defaultValue; } diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node/ObjectList.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node/ObjectList.php index 0e6cd09..6c2c180 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Node/ObjectList.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node/ObjectList.php @@ -11,7 +11,12 @@ final class ObjectList extends Node public function __construct(string $name, Node $objectNode) { - parent::__construct($objectNode->type(), $name); + parent::__construct( + $objectNode->type(), + $name, + false, + null + ); $this->objectNode = $objectNode; } diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccess.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccess.php index a5561e3..f5ca3bc 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccess.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccess.php @@ -34,7 +34,9 @@ public function serialize(Node $node): string return $this->serializeObjectListNode($node); } - throw new BuildingError(); + throw new BuildingError( + sprintf('Undefined node type: %s', get_class($node)) + ); } private function serializeScalarNode(Node\Scalar $node): string diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Anonymous.php b/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Anonymous.php index b8f7ba5..ef2f5d1 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Anonymous.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Anonymous.php @@ -54,7 +54,7 @@ private function getNode(ReflectionClass $class, string $name = ''): Node $constructor = $class->getConstructor(); if (null === $constructor) { - return new Node\Complex($class->getName(), $name); + return new Node\Complex($class->getName(), $name, false, null); } return $this->getNodes($constructor, $name); @@ -62,7 +62,12 @@ private function getNode(ReflectionClass $class, string $name = ''): Node private function getNodes(ReflectionMethod $method, string $name = ''): Node { - $node = new Node\Complex($method->getDeclaringClass()->getName(), $name); + $node = new Node\Complex( + $method->getDeclaringClass()->getName(), + $name, + false, + null + ); foreach ($method->getParameters() as $parameter) { $class = $parameter->getClass(); @@ -136,7 +141,7 @@ private function getDefaultValues(Node $node) } } - if ($node->withDefaultValue()) { + if ($node->hasDefaultValue()) { return $node->defaultValue(); } diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/PatternStore/Memory.php b/src/Builder/Blueprint/Factory/CodeGenerator/PatternStore/Memory.php index 729e024..e933733 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/PatternStore/Memory.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/PatternStore/Memory.php @@ -3,7 +3,6 @@ namespace RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\PatternStore; use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\PatternStore; -use Throwable; final class Memory implements PatternStore { @@ -18,11 +17,11 @@ public function __construct(array $blueprints = []) public function get(string $class): ?string { - try { - return $this->store[$class]; - } catch (Throwable $exception) { + if (! isset($this->store[$class])) { return null; } + + return $this->store[$class]; } public function save(string $class, string $blueprint): void diff --git a/src/Builder/Reflection.php b/src/Builder/Reflection.php index 34747be..4658554 100644 --- a/src/Builder/Reflection.php +++ b/src/Builder/Reflection.php @@ -104,17 +104,7 @@ private function buildParameter(ReflectionParameter $parameter, $data, Reflectio $class = $parameter->getClass(); if (null !== $class) { - $name = $class->getName(); - $constructorMethod = $class->getConstructor(); - $parameters = []; - - if (null !== $constructorMethod) { - /** @var \Traversable $iterator */ - $iterator = $this->collect($constructorMethod, $data); - $parameters = iterator_to_array($iterator); - } - - return new $name(...$parameters); + return $this->build($class->getName(), $data); } if ($parameter->isArray()) { diff --git a/test/Unit/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccessTest.php b/test/Unit/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccessTest.php index 4f32dd0..5301baf 100644 --- a/test/Unit/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccessTest.php +++ b/test/Unit/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccessTest.php @@ -23,7 +23,7 @@ public static function setUpBeforeClass(): void /** @test */ public function serializeScalarNodeToArrayAccessStringWithNodeName(): void { - $node = new Scalar('string', 'someName'); + $node = new Scalar('string', 'someName', false, null); $serializedNode = static::$serializer->serialize($node); @@ -33,9 +33,9 @@ public function serializeScalarNodeToArrayAccessStringWithNodeName(): void /** @test */ public function serializeComplexNodeToArrayAccessString(): void { - $node = new Complex('SomeClassName', 'someName'); - $scalarStringNode = new Scalar('string', 'someStringName'); - $scalarIntNode = new Scalar('int', 'someInt'); + $node = new Complex('SomeClassName', 'someName', false, null); + $scalarStringNode = new Scalar('string', 'someStringName', false, null); + $scalarIntNode = new Scalar('int', 'someInt', false, null); $node->add($scalarStringNode); $node->add($scalarIntNode); @@ -50,9 +50,9 @@ public function serializeComplexNodeToArrayAccessString(): void /** @test */ public function serializeObjectListNodeToArrayAccessString(): void { - $objectNode = new Complex('SomeClassName', 'someName'); - $scalarStringNode = new Scalar('string', 'someStringName'); - $scalarIntNode = new Scalar('int', 'someInt'); + $objectNode = new Complex('SomeClassName', 'someName', false, null); + $scalarStringNode = new Scalar('string', 'someStringName', false, null); + $scalarIntNode = new Scalar('int', 'someInt', false, null); $objectNode->add($scalarStringNode); $objectNode->add($scalarIntNode); $node = new ObjectList('someList', $objectNode); @@ -75,7 +75,7 @@ public function serializeObjectListNodeToArrayAccessString(): void /** @test */ public function whenNodeObjectIsNotKnowThenThrowsException(): void { - $node = new class('a', 'a') extends Node { + $node = new class('a', 'a', false, null) extends Node { }; $this->expectException(BuildingError::class); diff --git a/test/Unit/Builder/Blueprint/Factory/CodeGeneratorTest.php b/test/Unit/Builder/Blueprint/Factory/CodeGeneratorTest.php new file mode 100644 index 0000000..41bb3fc --- /dev/null +++ b/test/Unit/Builder/Blueprint/Factory/CodeGeneratorTest.php @@ -0,0 +1,38 @@ + 'return 123;', + ]) + ); + + $this->expectException(BuildingError::class); + + $codeGenerator->create('someClass'); + } + + /** @test */ + public function whenCreatedBlueprintIsCallableThenReturnIt(): void + { + $codeGenerator = new CodeGenerator( + new CodeGenerator\PatternGenerator\Dummy([ + 'someClass' => 'return function() { return 123; };', + ]) + ); + + $blueprint = $codeGenerator->create('someClass'); + + $this->assertSame(123, $blueprint()); + } +} From ce5f444423ba4f8c409efc5894cb22631de4af0d Mon Sep 17 00:00:00 2001 From: gardziejewskik Date: Sun, 17 Nov 2019 16:59:08 +0100 Subject: [PATCH 32/32] Refactor - rename --- src/Builder/Blueprint/Factory/CodeGenerator.php | 6 +++--- .../Node/{Complex.php => Composite.php} | 2 +- .../CodeGenerator/Node/Serializer/ArrayAccess.php | 4 ++-- .../Generator.php} | 4 ++-- .../Generator}/Anonymous.php | 12 ++++++------ .../Generator}/Dummy.php | 6 +++--- .../Generator}/StoreDecorator.php | 14 +++++++------- .../{PatternStore.php => Pattern/Store.php} | 4 ++-- .../{PatternStore => Pattern/Store}/Filesystem.php | 6 +++--- .../{PatternStore => Pattern/Store}/Memory.php | 6 +++--- test/Component/Builder/BlueprintTest.php | 6 +++--- .../Factory/CodeGenerator/Store/FilesystemTest.php | 2 +- .../Node/Serializer/ArrayAccessTest.php | 6 +++--- .../PatternGenerator/AnonymousTest.php | 2 +- .../PatternGenerator/StoreDecoratorTest.php | 6 +++--- .../Factory/CodeGenerator/Store/InMemoryTest.php | 2 +- .../Blueprint/Factory/CodeGeneratorTest.php | 4 ++-- 17 files changed, 46 insertions(+), 46 deletions(-) rename src/Builder/Blueprint/Factory/CodeGenerator/Node/{Complex.php => Composite.php} (92%) rename src/Builder/Blueprint/Factory/CodeGenerator/{PatternGenerator.php => Pattern/Generator.php} (80%) rename src/Builder/Blueprint/Factory/CodeGenerator/{PatternGenerator => Pattern/Generator}/Anonymous.php (94%) rename src/Builder/Blueprint/Factory/CodeGenerator/{PatternGenerator => Pattern/Generator}/Dummy.php (84%) rename src/Builder/Blueprint/Factory/CodeGenerator/{PatternGenerator => Pattern/Generator}/StoreDecorator.php (65%) rename src/Builder/Blueprint/Factory/CodeGenerator/{PatternStore.php => Pattern/Store.php} (86%) rename src/Builder/Blueprint/Factory/CodeGenerator/{PatternStore => Pattern/Store}/Filesystem.php (89%) rename src/Builder/Blueprint/Factory/CodeGenerator/{PatternStore => Pattern/Store}/Memory.php (90%) diff --git a/src/Builder/Blueprint/Factory/CodeGenerator.php b/src/Builder/Blueprint/Factory/CodeGenerator.php index 2a5a0b6..5a3b6cf 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator.php @@ -3,16 +3,16 @@ namespace RstGroup\ObjectBuilder\Builder\Blueprint\Factory; use RstGroup\ObjectBuilder\Builder\Blueprint\Factory; -use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\PatternGenerator; +use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Pattern\Generator; use RstGroup\ObjectBuilder\BuildingError; final class CodeGenerator implements Factory { - /** @var PatternGenerator */ + /** @var Generator */ private $generator; /** @codeCoverageIgnore */ - public function __construct(PatternGenerator $generator) + public function __construct(Generator $generator) { $this->generator = $generator; } diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Composite.php similarity index 92% rename from src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php rename to src/Builder/Blueprint/Factory/CodeGenerator/Node/Composite.php index d521dea..8bd32a8 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Complex.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Composite.php @@ -4,7 +4,7 @@ use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Node; -final class Complex extends Node +final class Composite extends Node { /** @var Node[] */ private $nodes = []; diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccess.php b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccess.php index f5ca3bc..f5f8acd 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccess.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Node/Serializer/ArrayAccess.php @@ -26,7 +26,7 @@ public function serialize(Node $node): string return $this->serializeScalarNode($node); } - if ($node instanceof Node\Complex) { + if ($node instanceof Node\Composite) { return $this->serializeComplexNode($node); } @@ -44,7 +44,7 @@ private function serializeScalarNode(Node\Scalar $node): string return sprintf(static::SCALAR_PATTERN, $node->name()); } - private function serializeComplexNode(Node\Complex $node): string + private function serializeComplexNode(Node\Composite $node): string { $nodes = []; foreach ($node->innerNodes() as $innerNode) { diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator.php b/src/Builder/Blueprint/Factory/CodeGenerator/Pattern/Generator.php similarity index 80% rename from src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator.php rename to src/Builder/Blueprint/Factory/CodeGenerator/Pattern/Generator.php index 21574e2..70a8ca6 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Pattern/Generator.php @@ -1,8 +1,8 @@ getConstructor(); if (null === $constructor) { - return new Node\Complex($class->getName(), $name, false, null); + return new Node\Composite($class->getName(), $name, false, null); } return $this->getNodes($constructor, $name); @@ -62,7 +62,7 @@ private function getNode(ReflectionClass $class, string $name = ''): Node private function getNodes(ReflectionMethod $method, string $name = ''): Node { - $node = new Node\Complex( + $node = new Node\Composite( $method->getDeclaringClass()->getName(), $name, false, @@ -130,7 +130,7 @@ private function getDefaultValues(Node $node) { $values = []; - if ($node instanceof Node\Complex) { + if ($node instanceof Node\Composite) { foreach ($node->innerNodes() as $innerNode) { $innerNodeDefaultValues = $this->getDefaultValues($innerNode); if ([] === $innerNodeDefaultValues) { diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Dummy.php b/src/Builder/Blueprint/Factory/CodeGenerator/Pattern/Generator/Dummy.php similarity index 84% rename from src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Dummy.php rename to src/Builder/Blueprint/Factory/CodeGenerator/Pattern/Generator/Dummy.php index 411d6d7..a106bcc 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/Dummy.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Pattern/Generator/Dummy.php @@ -1,11 +1,11 @@ store = $store; $this->generator = $generator; diff --git a/src/Builder/Blueprint/Factory/CodeGenerator/PatternStore.php b/src/Builder/Blueprint/Factory/CodeGenerator/Pattern/Store.php similarity index 86% rename from src/Builder/Blueprint/Factory/CodeGenerator/PatternStore.php rename to src/Builder/Blueprint/Factory/CodeGenerator/Pattern/Store.php index 108731c..5289bfd 100644 --- a/src/Builder/Blueprint/Factory/CodeGenerator/PatternStore.php +++ b/src/Builder/Blueprint/Factory/CodeGenerator/Pattern/Store.php @@ -1,8 +1,8 @@ add($scalarStringNode); @@ -50,7 +50,7 @@ public function serializeComplexNodeToArrayAccessString(): void /** @test */ public function serializeObjectListNodeToArrayAccessString(): void { - $objectNode = new Complex('SomeClassName', 'someName', false, null); + $objectNode = new Composite('SomeClassName', 'someName', false, null); $scalarStringNode = new Scalar('string', 'someStringName', false, null); $scalarIntNode = new Scalar('int', 'someInt', false, null); $objectNode->add($scalarStringNode); diff --git a/test/Unit/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/AnonymousTest.php b/test/Unit/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/AnonymousTest.php index 9ecf77c..8ae53cb 100644 --- a/test/Unit/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/AnonymousTest.php +++ b/test/Unit/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/AnonymousTest.php @@ -9,7 +9,7 @@ use PHPStan\PhpDocParser\Parser\TypeParser; use PHPUnit\Framework\TestCase; use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Node\Serializer\ArrayAccess; -use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\PatternGenerator\Anonymous; +use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Pattern\Generator\Anonymous; use RstGroup\ObjectBuilder\PhpDocParser\PhpStan; use RstGroup\ObjectBuilder\Test\Object\Collection\WithoutUseStmtConstructor; use RstGroup\ObjectBuilder\Test\Object\ComplexHierarchy; diff --git a/test/Unit/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/StoreDecoratorTest.php b/test/Unit/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/StoreDecoratorTest.php index b25a5b9..bdd28bd 100644 --- a/test/Unit/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/StoreDecoratorTest.php +++ b/test/Unit/Builder/Blueprint/Factory/CodeGenerator/PatternGenerator/StoreDecoratorTest.php @@ -3,9 +3,9 @@ namespace RstGroup\ObjectBuilder\Test\Unit\Builder\Blueprint\Factory\CodeGenerator\PatternGenerator; use PHPUnit\Framework\TestCase; -use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\PatternGenerator\Dummy; -use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\PatternGenerator\StoreDecorator; -use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\PatternStore\Memory; +use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Pattern\Generator\Dummy; +use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Pattern\Generator\StoreDecorator; +use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Pattern\Store\Memory; class StoreDecoratorTest extends TestCase { diff --git a/test/Unit/Builder/Blueprint/Factory/CodeGenerator/Store/InMemoryTest.php b/test/Unit/Builder/Blueprint/Factory/CodeGenerator/Store/InMemoryTest.php index dbab1a5..0f6cf46 100644 --- a/test/Unit/Builder/Blueprint/Factory/CodeGenerator/Store/InMemoryTest.php +++ b/test/Unit/Builder/Blueprint/Factory/CodeGenerator/Store/InMemoryTest.php @@ -3,7 +3,7 @@ namespace RstGroup\ObjectBuilder\Test\Unit\Builder\Blueprint\Factory\CodeGenerator\Store; use PHPUnit\Framework\TestCase; -use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\PatternStore\Memory; +use RstGroup\ObjectBuilder\Builder\Blueprint\Factory\CodeGenerator\Pattern\Store\Memory; class InMemoryTest extends TestCase { diff --git a/test/Unit/Builder/Blueprint/Factory/CodeGeneratorTest.php b/test/Unit/Builder/Blueprint/Factory/CodeGeneratorTest.php index 41bb3fc..db5674b 100644 --- a/test/Unit/Builder/Blueprint/Factory/CodeGeneratorTest.php +++ b/test/Unit/Builder/Blueprint/Factory/CodeGeneratorTest.php @@ -12,7 +12,7 @@ class CodeGeneratorTest extends TestCase public function whenCreatedBlueprintIsNotCallableThenThrowBuildingError(): void { $codeGenerator = new CodeGenerator( - new CodeGenerator\PatternGenerator\Dummy([ + new CodeGenerator\Pattern\Generator\Dummy([ 'someClass' => 'return 123;', ]) ); @@ -26,7 +26,7 @@ public function whenCreatedBlueprintIsNotCallableThenThrowBuildingError(): void public function whenCreatedBlueprintIsCallableThenReturnIt(): void { $codeGenerator = new CodeGenerator( - new CodeGenerator\PatternGenerator\Dummy([ + new CodeGenerator\Pattern\Generator\Dummy([ 'someClass' => 'return function() { return 123; };', ]) );