diff --git a/src/Reflection/Domain/Entities/ClassReflection.php b/src/Reflection/Domain/Entities/ClassReflection.php index 0701971..83e3213 100644 --- a/src/Reflection/Domain/Entities/ClassReflection.php +++ b/src/Reflection/Domain/Entities/ClassReflection.php @@ -54,6 +54,10 @@ public static function create( throw new ReflectionException("Cannot create instance of $name class.", "Please do not use `ClassReflection::create` method to create instance of $name class.", 1); } + if ($root->hasClassReflection($name)) { + return $root->classReflections($name); + } + try { $reflection = new \ReflectionClass($name); } catch (\ReflectionException $e) { @@ -78,15 +82,36 @@ public static function create( if (true === $result = $root->addClassReflection($instance)) { foreach ($reflection->getAttributes() as $attribute) { - AttributeReflection::create($attribute, $instance); + $instance->addAttribute( + AttributeReflection::create($attribute, $instance) + ); } foreach ($reflection->getProperties() as $property) { - PropertyReflection::create($property, $instance, $parentProperty); + $instance->addProperty( + $property = PropertyReflection::create($property, $instance, $parentProperty) + ); + $classes = $property->type()->getListOfClasses(); + foreach ($classes as $class) { + ClassReflection::create($class, $root, $property); + } } foreach ($reflection->getMethods() as $method) { - MethodReflection::create($method, $instance); + $instance->addMethod( + $method = MethodReflection::create($method, $instance) + ); + $classes = $method->returnType()->getListOfClasses(); + foreach ($classes as $class) { + ClassReflection::create($class, $root); + } + + foreach ($method->parameters() as $parameter) { + $classes = $parameter->type()->getListOfClasses(); + foreach ($classes as $class) { + ClassReflection::create($class, $root); + } + } } return $instance; diff --git a/src/Reflection/Domain/Entities/PropertyReflection.php b/src/Reflection/Domain/Entities/PropertyReflection.php index 6610087..a7bc15e 100644 --- a/src/Reflection/Domain/Entities/PropertyReflection.php +++ b/src/Reflection/Domain/Entities/PropertyReflection.php @@ -19,7 +19,7 @@ final class PropertyReflection extends Entity implements Normalizable, Attribute use Attributes; private ClassReflection $parent; - private PropertyReflection $parentProperty; + private ?PropertyReflection $parentProperty; private function __construct( private ReflectionId $id, @@ -30,7 +30,7 @@ private function __construct( $this->attributes = $attributes; } - public static function create(\ReflectionProperty $reflectionProperty, ClassReflection $parent, ?\ReflectionProperty $parentProperty): static + public static function create(\ReflectionProperty $reflectionProperty, ClassReflection $parent, ?PropertyReflection $parentProperty): static { $instance = new static( id: ReflectionId::uuid(), @@ -88,7 +88,7 @@ public function parent(?ClassReflection $parent = null): ClassReflection throw new \InvalidArgumentException('Cannot set parent property. Parent property is read-only.'); } - public function parentProperty(?PropertyReflection $parentProperty = null): PropertyReflection + public function parentProperty(?PropertyReflection $parentProperty = null): ?PropertyReflection { if (!$parentProperty) { return $this->parentProperty; diff --git a/src/Reflection/Domain/Entities/Type/CollectionTypeReflection.php b/src/Reflection/Domain/Entities/Type/CollectionTypeReflection.php index de2f696..4c54149 100644 --- a/src/Reflection/Domain/Entities/Type/CollectionTypeReflection.php +++ b/src/Reflection/Domain/Entities/Type/CollectionTypeReflection.php @@ -34,6 +34,18 @@ public function allowsNull(): bool return false; } + public function getListOfClasses(): array + { + $types = [$this->collectionType, $this->keyType, $this->valueType]; + + $classes = []; + foreach ($types as $type) { + $classes = array_merge($classes, $type->getListOfClasses()); + } + + return array_unique($classes); + } + public function normalize(): array { return [ diff --git a/src/Reflection/Domain/Entities/Type/IntersectionTypeReflection.php b/src/Reflection/Domain/Entities/Type/IntersectionTypeReflection.php index dbdc8ef..b04e1c3 100644 --- a/src/Reflection/Domain/Entities/Type/IntersectionTypeReflection.php +++ b/src/Reflection/Domain/Entities/Type/IntersectionTypeReflection.php @@ -50,6 +50,16 @@ public function normalize(): array ]; } + public function getListOfClasses(): array + { + $classes = []; + foreach ($this->types as $type) { + $classes = array_merge($classes, $type->getListOfClasses()); + } + + return array_unique($classes); + } + public static function denormalize(array $data): static { return static::recreate( diff --git a/src/Reflection/Domain/Entities/Type/NamedTypeReflection.php b/src/Reflection/Domain/Entities/Type/NamedTypeReflection.php index 2a83f01..6360f3d 100644 --- a/src/Reflection/Domain/Entities/Type/NamedTypeReflection.php +++ b/src/Reflection/Domain/Entities/Type/NamedTypeReflection.php @@ -99,6 +99,15 @@ public function allowsNull(): bool return 'null' === $this->name || 'mixed' === $this->name; } + public function getListOfClasses(): array + { + if ($this->isClass() && !$this->isAbstractClass()) { + return [$this->name]; + } + + return []; + } + public function normalize(): array { return [ diff --git a/src/Reflection/Domain/Entities/Type/TypeReflection.php b/src/Reflection/Domain/Entities/Type/TypeReflection.php index 3ab6995..65079e2 100644 --- a/src/Reflection/Domain/Entities/Type/TypeReflection.php +++ b/src/Reflection/Domain/Entities/Type/TypeReflection.php @@ -9,4 +9,7 @@ abstract class TypeReflection implements Normalizable { abstract public function allowsNull(): bool; + + /** @return class-string[] */ + abstract public function getListOfClasses(): array; } diff --git a/src/Reflection/Domain/Entities/Type/UnionTypeReflection.php b/src/Reflection/Domain/Entities/Type/UnionTypeReflection.php index 56ff2bb..b7e38c1 100644 --- a/src/Reflection/Domain/Entities/Type/UnionTypeReflection.php +++ b/src/Reflection/Domain/Entities/Type/UnionTypeReflection.php @@ -45,6 +45,16 @@ public function allowsNull(): bool return false; } + public function getListOfClasses(): array + { + $classes = []; + foreach ($this->types as $type) { + $classes = array_merge($classes, $type->getListOfClasses()); + } + + return array_unique($classes); + } + public function normalize(): array { return [ diff --git a/src/Reflection/Domain/Reflection.php b/src/Reflection/Domain/Reflection.php index 330a791..2ddd08a 100644 --- a/src/Reflection/Domain/Reflection.php +++ b/src/Reflection/Domain/Reflection.php @@ -88,6 +88,16 @@ public function classReflections(?string $filter = null): array|ClassReflection return $this->classReflections; } + /** + * @param class-string|string $className + * + * @return bool Whether the class reflection exists in the collection + */ + public function hasClassReflection(string $className): bool + { + return array_key_exists($className, $this->classReflections); + } + /** * @return true|ClassReflection If the class reflection already exists in the collection, it returns the existing one. * Otherwise, it returns true to indicate that the class reflection was successfully added. diff --git a/tests/Unit/Reflection/Domain/ReflectionTest.php b/tests/Unit/Reflection/Domain/ReflectionTest.php new file mode 100644 index 0000000..fdd6dd9 --- /dev/null +++ b/tests/Unit/Reflection/Domain/ReflectionTest.php @@ -0,0 +1,27 @@ +rootClass()); + } + + public function testRecreate(): void + { + $this->markTestIncomplete('This test has not been implemented yet.'); + } +} diff --git a/tools/phpstan/fpm-baseline.neon b/tools/phpstan/fpm-baseline.neon index f5aa90d..30a401b 100644 --- a/tools/phpstan/fpm-baseline.neon +++ b/tools/phpstan/fpm-baseline.neon @@ -40,6 +40,11 @@ parameters: count: 1 path: ../../src/Reflection/Domain/Entities/ClassReflection.php + - + message: "#^Argument of an invalid type array\\\\|PBaszak\\\\UltraMapper\\\\Reflection\\\\Domain\\\\Entities\\\\ParameterReflection supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: ../../src/Reflection/Domain/Entities/ClassReflection.php + - message: "#^Argument of an invalid type array\\\\|PBaszak\\\\UltraMapper\\\\Reflection\\\\Domain\\\\Entities\\\\PropertyReflection supplied for foreach, only iterables are supported\\.$#" count: 1 @@ -70,6 +75,11 @@ parameters: count: 1 path: ../../src/Reflection/Domain/Entities/ClassReflection.php + - + message: "#^Method PBaszak\\\\UltraMapper\\\\Reflection\\\\Domain\\\\Entities\\\\ClassReflection\\:\\:create\\(\\) should return PBaszak\\\\UltraMapper\\\\Reflection\\\\Domain\\\\Entities\\\\ClassReflection but returns array\\\\|PBaszak\\\\UltraMapper\\\\Reflection\\\\Domain\\\\Entities\\\\ClassReflection\\.$#" + count: 1 + path: ../../src/Reflection/Domain/Entities/ClassReflection.php + - message: "#^Method PBaszak\\\\UltraMapper\\\\Reflection\\\\Domain\\\\Entities\\\\ClassReflection\\:\\:denormalizeAttributes\\(\\) has parameter \\$attributes with no value type specified in iterable type array\\.$#" count: 1 @@ -120,11 +130,6 @@ parameters: count: 2 path: ../../src/Reflection/Domain/Entities/ClassReflection.php - - - message: "#^Parameter \\#3 \\$parentProperty of static method PBaszak\\\\UltraMapper\\\\Reflection\\\\Domain\\\\Entities\\\\PropertyReflection\\:\\:create\\(\\) expects ReflectionProperty\\|null, PBaszak\\\\UltraMapper\\\\Reflection\\\\Domain\\\\Entities\\\\PropertyReflection\\|null given\\.$#" - count: 1 - path: ../../src/Reflection/Domain/Entities/ClassReflection.php - - message: "#^Part \\$id \\(PBaszak\\\\UltraMapper\\\\Reflection\\\\Domain\\\\Identity\\\\ReflectionId\\) of encapsed string cannot be cast to string\\.$#" count: 1 @@ -286,9 +291,9 @@ parameters: path: ../../src/Reflection/Domain/Entities/PropertyReflection.php - - message: "#^Property PBaszak\\\\UltraMapper\\\\Reflection\\\\Domain\\\\Entities\\\\PropertyReflection\\:\\:\\$parentProperty \\(PBaszak\\\\UltraMapper\\\\Reflection\\\\Domain\\\\Entities\\\\PropertyReflection\\) does not accept ReflectionProperty\\|null\\.$#" + message: "#^Method PBaszak\\\\UltraMapper\\\\Reflection\\\\Domain\\\\Entities\\\\Type\\\\NamedTypeReflection\\:\\:getListOfClasses\\(\\) should return array\\ but returns array\\\\.$#" count: 1 - path: ../../src/Reflection/Domain/Entities/PropertyReflection.php + path: ../../src/Reflection/Domain/Entities/Type/NamedTypeReflection.php - message: "#^Parameter \\#1 \\$objectOrClass of class ReflectionClass constructor expects class\\-string\\\\|T of object, string given\\.$#"