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\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<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
{
$generics = $type->generics();

View File

@ -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
{
}