From 73b62241b63ed365a90fb5e44876fafa63680177 Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Fri, 5 Aug 2022 23:11:50 +0200 Subject: [PATCH] fix: handle inherited private constructor in class definition --- .../ReflectionClassDefinitionRepository.php | 36 +++++++++++++++---- ...eflectionClassDefinitionRepositoryTest.php | 20 +++++++++++ 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/src/Definition/Repository/Reflection/ReflectionClassDefinitionRepository.php b/src/Definition/Repository/Reflection/ReflectionClassDefinitionRepository.php index 38a1c75..1b7709c 100644 --- a/src/Definition/Repository/Reflection/ReflectionClassDefinitionRepository.php +++ b/src/Definition/Repository/Reflection/ReflectionClassDefinitionRepository.php @@ -9,6 +9,7 @@ use CuyZ\Valinor\Definition\Exception\ClassTypeAliasesDuplication; use CuyZ\Valinor\Definition\Exception\InvalidTypeAliasImportClass; use CuyZ\Valinor\Definition\Exception\InvalidTypeAliasImportClassType; use CuyZ\Valinor\Definition\Exception\UnknownTypeAliasImport; +use CuyZ\Valinor\Definition\MethodDefinition; use CuyZ\Valinor\Definition\Methods; use CuyZ\Valinor\Definition\Properties; use CuyZ\Valinor\Definition\Repository\AttributesRepository; @@ -24,7 +25,7 @@ use CuyZ\Valinor\Type\Type; use CuyZ\Valinor\Type\Types\ClassType; use CuyZ\Valinor\Type\Types\UnresolvableType; use CuyZ\Valinor\Utility\Reflection\Reflection; -use ReflectionMethod; +use ReflectionClass; use ReflectionProperty; use function array_filter; @@ -61,19 +62,40 @@ final class ReflectionClassDefinitionRepository implements ClassDefinitionReposi $reflection->getProperties() ); - $methods = array_map( - fn (ReflectionMethod $method) => $this->methodBuilder->for($method, $typeResolver), - $reflection->getMethods() - ); - return new ClassDefinition( $type, $this->attributesFactory->for($reflection), new Properties(...$properties), - new Methods(...$methods), + new Methods(...$this->methods($reflection, $typeResolver)), ); } + /** + * @param ReflectionClass $reflection + * @return list + */ + private function methods(ReflectionClass $reflection, ReflectionTypeResolver $typeResolver): array + { + $methods = []; + + // Because `ReflectionMethod::getMethods()` wont list the constructor if + // it comes from a parent class AND is not public, we need to manually + // fetch it and add it to the list. + if ($reflection->hasMethod('__construct')) { + $methods[] = $this->methodBuilder->for($reflection->getMethod('__construct'), $typeResolver); + } + + foreach ($reflection->getMethods() as $method) { + if ($method->name === '__construct') { + continue; + } + + $methods[] = $this->methodBuilder->for($method, $typeResolver); + } + + return $methods; + } + private function typeResolver(ClassType $type): ReflectionTypeResolver { $generics = $type->generics(); diff --git a/tests/Unit/Definition/Repository/Reflection/ReflectionClassDefinitionRepositoryTest.php b/tests/Unit/Definition/Repository/Reflection/ReflectionClassDefinitionRepositoryTest.php index c2cbfd3..69260ee 100644 --- a/tests/Unit/Definition/Repository/Reflection/ReflectionClassDefinitionRepositoryTest.php +++ b/tests/Unit/Definition/Repository/Reflection/ReflectionClassDefinitionRepositoryTest.php @@ -150,6 +150,15 @@ final class ReflectionClassDefinitionRepositoryTest extends TestCase self::assertSame('Optional parameter value', $optionalParameter->defaultValue()); } + public function test_private_parent_constructor_is_listed_in_methods(): void + { + $type = new ClassType(ClassWithInheritedPrivateConstructor::class); + $methods = $this->repository->for($type)->methods(); + + self::assertTrue($methods->hasConstructor()); + self::assertFalse($methods->constructor()->isPublic()); + } + public function test_invalid_property_type_throws_exception(): void { $class = get_class(new class () { @@ -363,3 +372,14 @@ final class ReflectionClassDefinitionRepositoryTest extends TestCase $this->repository->for(new ClassType($class)); } } + +abstract class AbstractClassWithPrivateConstructor +{ + private function __construct() + { + } +} + +final class ClassWithInheritedPrivateConstructor extends AbstractClassWithPrivateConstructor +{ +}