4dc72a31c81977dc4b263c3685f996a852c1ab92
[GitHub/WoltLab/WCF.git] /
1 <?php
2
3 declare(strict_types=1);
4
5 namespace CuyZ\Valinor\Definition\Repository\Reflection;
6
7 use CuyZ\Valinor\Definition\FunctionDefinition;
8 use CuyZ\Valinor\Definition\Parameters;
9 use CuyZ\Valinor\Definition\Repository\AttributesRepository;
10 use CuyZ\Valinor\Definition\Repository\FunctionDefinitionRepository;
11 use CuyZ\Valinor\Type\Parser\Factory\Specifications\AliasSpecification;
12 use CuyZ\Valinor\Type\Parser\Factory\Specifications\ClassContextSpecification;
13 use CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
14 use CuyZ\Valinor\Utility\Reflection\Reflection;
15 use ReflectionFunction;
16 use ReflectionParameter;
17
18 use function str_ends_with;
19
20 /** @internal */
21 final class ReflectionFunctionDefinitionRepository implements FunctionDefinitionRepository
22 {
23 private TypeParserFactory $typeParserFactory;
24
25 private AttributesRepository $attributesRepository;
26
27 private ReflectionParameterDefinitionBuilder $parameterBuilder;
28
29 public function __construct(TypeParserFactory $typeParserFactory, AttributesRepository $attributesRepository)
30 {
31 $this->typeParserFactory = $typeParserFactory;
32 $this->attributesRepository = $attributesRepository;
33 $this->parameterBuilder = new ReflectionParameterDefinitionBuilder($attributesRepository);
34 }
35
36 public function for(callable $function): FunctionDefinition
37 {
38 $reflection = Reflection::function($function);
39
40 $typeResolver = $this->typeResolver($reflection);
41
42 $parameters = array_map(
43 fn (ReflectionParameter $parameter) => $this->parameterBuilder->for($parameter, $typeResolver),
44 $reflection->getParameters()
45 );
46
47 $name = $reflection->getName();
48 $class = $reflection->getClosureScopeClass();
49 $returnType = $typeResolver->resolveType($reflection);
50 $isClosure = $name === '{closure}' || str_ends_with($name, '\\{closure}');
51
52 return new FunctionDefinition(
53 $name,
54 Reflection::signature($reflection),
55 $this->attributesRepository->for($reflection),
56 $reflection->getFileName() ?: null,
57 $class?->name,
58 $reflection->getClosureThis() === null,
59 $isClosure,
60 new Parameters(...$parameters),
61 $returnType
62 );
63 }
64
65 private function typeResolver(ReflectionFunction $reflection): ReflectionTypeResolver
66 {
67 $class = $reflection->getClosureScopeClass();
68
69 $nativeSpecifications = [];
70 $advancedSpecification = [new AliasSpecification($reflection)];
71
72 if ($class !== null) {
73 $nativeSpecifications[] = new ClassContextSpecification($class->name);
74 $advancedSpecification[] = new ClassContextSpecification($class->name);
75 }
76
77 $nativeParser = $this->typeParserFactory->get(...$nativeSpecifications);
78 $advancedParser = $this->typeParserFactory->get(...$advancedSpecification);
79
80 return new ReflectionTypeResolver($nativeParser, $advancedParser);
81 }
82 }