JFIF x x C C " } !1AQa "q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w !1AQ aq"2B #3Rbr{
File "ReflectionClassDefinitionRepository.php"
Full Path: /home/palsarh/web/palsarh.in/public_html/vendor/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionClassDefinitionRepository.php
File size: 7.88 KB
MIME-type: text/x-php
Charset: utf-8
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Definition\Repository\Reflection;
use CuyZ\Valinor\Definition\Attributes;
use CuyZ\Valinor\Definition\ClassDefinition;
use CuyZ\Valinor\Definition\MethodDefinition;
use CuyZ\Valinor\Definition\Methods;
use CuyZ\Valinor\Definition\Properties;
use CuyZ\Valinor\Definition\PropertyDefinition;
use CuyZ\Valinor\Definition\Repository\AttributesRepository;
use CuyZ\Valinor\Definition\Repository\ClassDefinitionRepository;
use CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\ClassImportedTypeAliasResolver;
use CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\ClassLocalTypeAliasResolver;
use CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\ClassParentTypeResolver;
use CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\ClassGenericResolver;
use CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\ReflectionTypeResolver;
use CuyZ\Valinor\Mapper\Object\Constructor;
use CuyZ\Valinor\Type\ObjectType;
use CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
use CuyZ\Valinor\Type\Parser\UnresolvableTypeFinderParser;
use CuyZ\Valinor\Type\Parser\VacantTypeAssignerParser;
use CuyZ\Valinor\Type\Type;
use CuyZ\Valinor\Type\Types\InterfaceType;
use CuyZ\Valinor\Type\Types\NativeClassType;
use CuyZ\Valinor\Type\Types\UnresolvableType;
use CuyZ\Valinor\Utility\Reflection\Reflection;
use ReflectionMethod;
use ReflectionProperty;
use function array_filter;
use function array_keys;
use function array_map;
/** @internal */
final class ReflectionClassDefinitionRepository implements ClassDefinitionRepository
{
private TypeParserFactory $typeParserFactory;
private AttributesRepository $attributesRepository;
private ReflectionPropertyDefinitionBuilder $propertyBuilder;
private ReflectionMethodDefinitionBuilder $methodBuilder;
private ClassParentTypeResolver $parentTypeResolver;
private ClassGenericResolver $genericResolver;
private ClassLocalTypeAliasResolver $localTypeAliasResolver;
private ClassImportedTypeAliasResolver $importedTypeAliasResolver;
/**
* @param list<class-string> $allowedAttributes
*/
public function __construct(
TypeParserFactory $typeParserFactory,
array $allowedAttributes,
) {
$this->typeParserFactory = $typeParserFactory;
$this->attributesRepository = new ReflectionAttributesRepository($this, $allowedAttributes);
$this->propertyBuilder = new ReflectionPropertyDefinitionBuilder($this->attributesRepository);
$this->methodBuilder = new ReflectionMethodDefinitionBuilder($this->attributesRepository);
$this->parentTypeResolver = new ClassParentTypeResolver($this->typeParserFactory);
$this->genericResolver = new ClassGenericResolver($this->typeParserFactory);
$this->localTypeAliasResolver = new ClassLocalTypeAliasResolver($this->typeParserFactory);
$this->importedTypeAliasResolver = new ClassImportedTypeAliasResolver($this->typeParserFactory);
}
public function for(ObjectType $type): ClassDefinition
{
$reflection = Reflection::class($type->className());
$vacantTypes = $this->vacantTypes($type);
$nativeTypeParser = $this->typeParserFactory->buildNativeTypeParserForClass($type->className());
$advancedTypeParser = $this->typeParserFactory->buildAdvancedTypeParserForClass($type->className());
$advancedTypeParser = new VacantTypeAssignerParser($advancedTypeParser, $vacantTypes);
$advancedTypeParser = new UnresolvableTypeFinderParser($advancedTypeParser);
$typeResolver = new ReflectionTypeResolver($nativeTypeParser, $advancedTypeParser);
return new ClassDefinition(
$reflection->name,
$type,
new Attributes(...$this->attributesRepository->for($reflection)),
new Properties(...$this->properties($type, $typeResolver)),
new Methods(...$this->methods($type, $typeResolver)),
$reflection->isFinal(),
$reflection->isAbstract(),
);
}
/**
* @return array<non-empty-string, Type>
*/
private function vacantTypes(ObjectType $type): array
{
$generics = [];
if ($type instanceof NativeClassType || $type instanceof InterfaceType) {
$generics = $this->genericResolver->resolveGenerics($type);
}
$localTypes = $this->localTypeAliasResolver->resolveLocalTypeAliases($type);
$importedTypes = $this->importedTypeAliasResolver->resolveImportedTypeAliases($type);
$vacantTypes = [...$generics, ...$localTypes, ...$importedTypes];
$keys = [...array_keys($generics), ...array_keys($localTypes), ...array_keys($importedTypes)];
// PHP8.5 use pipes
$aliasCollision = array_filter(
array_count_values($keys),
fn (int $count) => $count > 1
);
foreach ($aliasCollision as $alias => $numberOfCollisions) {
/** @var non-empty-string $alias */
$vacantTypes[$alias] = UnresolvableType::forClassTypeAliasesCollision($alias, $numberOfCollisions);
}
return $vacantTypes;
}
/**
* @return list<PropertyDefinition>
*/
private function properties(ObjectType $type, ReflectionTypeResolver $typeResolver): array
{
$reflection = Reflection::class($type->className());
$properties = [];
foreach ($reflection->getProperties() as $property) {
$declaringClass = $property->getDeclaringClass();
if ($declaringClass->name === $type->className()) {
$properties[$property->name] = $this->propertyBuilder->for($property, $typeResolver);
} else {
$parentClass = $this->parentTypeResolver->resolveParentTypeFor($type);
$properties[$property->name] = $this->for($parentClass)->properties->get($property->name);
}
}
// Properties will be sorted by inheritance order, from parent to child.
$sortedProperties = [];
while ($reflection) {
$currentProperties = array_map(
fn (ReflectionProperty $property) => $properties[$property->name],
array_filter(
$reflection->getProperties(),
fn (ReflectionProperty $property) => isset($properties[$property->name]),
),
);
$sortedProperties = [...$currentProperties, ...$sortedProperties];
$reflection = $reflection->getParentClass();
}
return $sortedProperties;
}
/**
* @return array<MethodDefinition>
*/
private function methods(ObjectType $type, ReflectionTypeResolver $typeResolver): array
{
$reflection = Reflection::class($type->className());
$methods = array_filter($reflection->getMethods(), $this->shouldMethodBeIncluded(...));
// 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[] = $reflection->getMethod('__construct');
}
return array_map(function (ReflectionMethod $method) use ($type, $typeResolver) {
$declaringClass = $method->getDeclaringClass();
if ($declaringClass->name === $type->className()) {
return $this->methodBuilder->for($method, $typeResolver);
}
$parentClass = $this->parentTypeResolver->resolveParentTypeFor($type);
return $this->for($parentClass)->methods->get($method->name);
}, $methods);
}
private function shouldMethodBeIncluded(ReflectionMethod $method): bool
{
return $method->name === 'map'
|| $method->name === 'normalize'
|| $method->name === 'normalizeKey'
|| $method->getAttributes(Constructor::class) !== [];
}
}