fix: handle inherited private constructor in class definition

This commit is contained in:
Romain Canon 2022-08-05 23:11:50 +02:00
parent 2b46a60f37
commit 73b62241b6
2 changed files with 49 additions and 7 deletions

View File

@ -9,6 +9,7 @@ use CuyZ\Valinor\Definition\Exception\ClassTypeAliasesDuplication;
use CuyZ\Valinor\Definition\Exception\InvalidTypeAliasImportClass; use CuyZ\Valinor\Definition\Exception\InvalidTypeAliasImportClass;
use CuyZ\Valinor\Definition\Exception\InvalidTypeAliasImportClassType; use CuyZ\Valinor\Definition\Exception\InvalidTypeAliasImportClassType;
use CuyZ\Valinor\Definition\Exception\UnknownTypeAliasImport; use CuyZ\Valinor\Definition\Exception\UnknownTypeAliasImport;
use CuyZ\Valinor\Definition\MethodDefinition;
use CuyZ\Valinor\Definition\Methods; use CuyZ\Valinor\Definition\Methods;
use CuyZ\Valinor\Definition\Properties; use CuyZ\Valinor\Definition\Properties;
use CuyZ\Valinor\Definition\Repository\AttributesRepository; 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\ClassType;
use CuyZ\Valinor\Type\Types\UnresolvableType; use CuyZ\Valinor\Type\Types\UnresolvableType;
use CuyZ\Valinor\Utility\Reflection\Reflection; use CuyZ\Valinor\Utility\Reflection\Reflection;
use ReflectionMethod; use ReflectionClass;
use ReflectionProperty; use ReflectionProperty;
use function array_filter; use function array_filter;
@ -61,19 +62,40 @@ final class ReflectionClassDefinitionRepository implements ClassDefinitionReposi
$reflection->getProperties() $reflection->getProperties()
); );
$methods = array_map(
fn (ReflectionMethod $method) => $this->methodBuilder->for($method, $typeResolver),
$reflection->getMethods()
);
return new ClassDefinition( return new ClassDefinition(
$type, $type,
$this->attributesFactory->for($reflection), $this->attributesFactory->for($reflection),
new Properties(...$properties), new Properties(...$properties),
new Methods(...$methods), new Methods(...$this->methods($reflection, $typeResolver)),
); );
} }
/**
* @param ReflectionClass<object> $reflection
* @return list<MethodDefinition>
*/
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 private function typeResolver(ClassType $type): ReflectionTypeResolver
{ {
$generics = $type->generics(); $generics = $type->generics();

View File

@ -150,6 +150,15 @@ final class ReflectionClassDefinitionRepositoryTest extends TestCase
self::assertSame('Optional parameter value', $optionalParameter->defaultValue()); 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 public function test_invalid_property_type_throws_exception(): void
{ {
$class = get_class(new class () { $class = get_class(new class () {
@ -363,3 +372,14 @@ final class ReflectionClassDefinitionRepositoryTest extends TestCase
$this->repository->for(new ClassType($class)); $this->repository->for(new ClassType($class));
} }
} }
abstract class AbstractClassWithPrivateConstructor
{
private function __construct()
{
}
}
final class ClassWithInheritedPrivateConstructor extends AbstractClassWithPrivateConstructor
{
}