Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
91.76% |
78 / 85 |
|
37.50% |
3 / 8 |
CRAP | |
0.00% |
0 / 1 |
MetadataFactory | |
91.76% |
78 / 85 |
|
37.50% |
3 / 8 |
45.08 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getMetadataStrategy | |
96.30% |
26 / 27 |
|
0.00% |
0 / 1 |
14 | |||
getScalarForType | |
80.00% |
4 / 5 |
|
0.00% |
0 / 1 |
2.03 | |||
getMetadataStrategyForType | |
92.31% |
36 / 39 |
|
0.00% |
0 / 1 |
19.16 | |||
getMethodMetadata | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getCreationMetadata | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getModificationMetadata | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getResultMetadata | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 |
1 | <?php |
2 | namespace Apie\Core\Metadata; |
3 | |
4 | use Apie\Core\ApieLib; |
5 | use Apie\Core\Context\ApieContext; |
6 | use Apie\Core\Context\MetadataFieldHashmap; |
7 | use Apie\Core\Enums\ScalarType; |
8 | use Apie\Core\Exceptions\InvalidTypeException; |
9 | use Apie\Core\Metadata\Fields\ConstructorParameter; |
10 | use Apie\Core\Metadata\Strategy\AliasStrategy; |
11 | use Apie\Core\Metadata\Strategy\BuiltInPhpClassStrategy; |
12 | use Apie\Core\Metadata\Strategy\CompositeValueObjectStrategy; |
13 | use Apie\Core\Metadata\Strategy\DtoStrategy; |
14 | use Apie\Core\Metadata\Strategy\EnumStrategy; |
15 | use Apie\Core\Metadata\Strategy\ExceptionStrategy; |
16 | use Apie\Core\Metadata\Strategy\ItemHashmapStrategy; |
17 | use Apie\Core\Metadata\Strategy\ItemListObjectStrategy; |
18 | use Apie\Core\Metadata\Strategy\PolymorphicEntityStrategy; |
19 | use Apie\Core\Metadata\Strategy\RegularObjectStrategy; |
20 | use Apie\Core\Metadata\Strategy\ScalarStrategy; |
21 | use Apie\Core\Metadata\Strategy\UnionTypeStrategy; |
22 | use Apie\Core\Metadata\Strategy\UploadedFileStrategy; |
23 | use Apie\Core\Metadata\Strategy\ValueObjectStrategy; |
24 | use Apie\TypeConverter\ReflectionTypeFactory; |
25 | use LogicException; |
26 | use ReflectionClass; |
27 | use ReflectionIntersectionType; |
28 | use ReflectionMethod; |
29 | use ReflectionNamedType; |
30 | use ReflectionType; |
31 | use ReflectionUnionType; |
32 | |
33 | final class MetadataFactory |
34 | { |
35 | private function __construct() |
36 | { |
37 | } |
38 | |
39 | /** |
40 | * @param ReflectionClass<object> $class |
41 | */ |
42 | public static function getMetadataStrategy(ReflectionClass $class): StrategyInterface |
43 | { |
44 | if (AliasStrategy::supports($class)) { |
45 | return new AliasStrategy($class->name); |
46 | } |
47 | if (BuiltInPhpClassStrategy::supports($class)) { |
48 | return new BuiltInPhpClassStrategy($class); |
49 | } |
50 | if (ScalarStrategy::supports($class)) { |
51 | return new ScalarStrategy(ScalarType::STDCLASS); |
52 | } |
53 | if (EnumStrategy::supports($class)) { |
54 | return new EnumStrategy($class); |
55 | } |
56 | if (PolymorphicEntityStrategy::supports($class)) { |
57 | return new PolymorphicEntityStrategy($class); |
58 | } |
59 | if (CompositeValueObjectStrategy::supports($class)) { |
60 | return new CompositeValueObjectStrategy($class); |
61 | } |
62 | if (ItemListObjectStrategy::supports($class)) { |
63 | return new ItemListObjectStrategy($class); |
64 | } |
65 | if (ItemHashmapStrategy::supports($class)) { |
66 | return new ItemHashmapStrategy($class); |
67 | } |
68 | if (DtoStrategy::supports($class)) { |
69 | return new DtoStrategy($class); |
70 | } |
71 | if (ValueObjectStrategy::supports($class)) { |
72 | return new ValueObjectStrategy($class); |
73 | } |
74 | if (ExceptionStrategy::supports($class)) { |
75 | return new ExceptionStrategy($class); |
76 | } |
77 | if (UploadedFileStrategy::supports($class)) { |
78 | return new UploadedFileStrategy($class); |
79 | } |
80 | if (RegularObjectStrategy::supports($class)) { |
81 | return new RegularObjectStrategy($class); |
82 | } |
83 | |
84 | throw new InvalidTypeException($class->name, 'Apie supported object'); |
85 | } |
86 | |
87 | public static function getScalarForType(?ReflectionType $typehint, bool $nullable = true): ScalarType |
88 | { |
89 | if ($typehint === null) { |
90 | return ScalarType::MIXED; |
91 | } |
92 | return self::getMetadataStrategyForType($typehint) |
93 | ->getResultMetadata(new ApieContext()) |
94 | ->toScalarType($nullable); |
95 | } |
96 | |
97 | public static function getMetadataStrategyForType(ReflectionType $typehint): StrategyInterface |
98 | { |
99 | if ($typehint instanceof ReflectionUnionType) { |
100 | $metadata = []; |
101 | foreach ($typehint->getTypes() as $type) { |
102 | $metadata[] = self::getMetadataStrategyForType($type)->getCreationMetadata(new ApieContext()); |
103 | } |
104 | return new UnionTypeStrategy(...$metadata); |
105 | } |
106 | if ($typehint instanceof ReflectionIntersectionType) { |
107 | throw new LogicException('Intersection typehints are not supported yet'); |
108 | } |
109 | assert($typehint instanceof ReflectionNamedType); |
110 | if (ApieLib::hasAlias($typehint->getName())) { |
111 | $strategy = self::getMetadataStrategyForType( |
112 | ReflectionTypeFactory::createReflectionType( |
113 | ApieLib::getAlias($typehint->getName()) |
114 | ) |
115 | ); |
116 | return $typehint->allowsNull() |
117 | ? new UnionTypeStrategy($strategy, new ScalarMetadata(ScalarType::NULLVALUE)) |
118 | : $strategy; |
119 | } |
120 | if ($typehint->isBuiltin()) { |
121 | if ($typehint->getName() === 'null') { |
122 | return new ScalarStrategy(ScalarType::NULLVALUE); |
123 | } |
124 | if ($typehint->getName() === 'mixed') { |
125 | return new ScalarStrategy(ScalarType::MIXED); |
126 | } |
127 | $strategy = new ScalarStrategy( |
128 | match ($typehint->getName()) { |
129 | 'string' => ScalarType::STRING, |
130 | 'float' => ScalarType::FLOAT, |
131 | 'int' => ScalarType::INTEGER, |
132 | 'array' => ScalarType::ARRAY, |
133 | 'mixed' => ScalarType::MIXED, |
134 | 'bool' => ScalarType::BOOLEAN, |
135 | 'true' => ScalarType::BOOLEAN, |
136 | 'false' => ScalarType::BOOLEAN, |
137 | default => throw new InvalidTypeException($typehint->getName(), 'string|float|int|null|array|mixed|bool') |
138 | } |
139 | ); |
140 | } else { |
141 | $strategy = self::getMetadataStrategy(new ReflectionClass($typehint->getName())); |
142 | } |
143 | if ($typehint->allowsNull()) { |
144 | return new UnionTypeStrategy($strategy, new ScalarMetadata(ScalarType::NULLVALUE)); |
145 | } |
146 | |
147 | return $strategy; |
148 | } |
149 | |
150 | public static function getMethodMetadata(ReflectionMethod $method, ApieContext $context): MetadataInterface |
151 | { |
152 | $fields = []; |
153 | foreach ($method->getParameters() as $parameter) { |
154 | $fields[$parameter->name] = new ConstructorParameter($parameter); |
155 | } |
156 | return new CompositeMetadata(new MetadataFieldHashmap($fields)); |
157 | } |
158 | |
159 | /** |
160 | * @param ReflectionClass<object>|ReflectionType $typehint |
161 | */ |
162 | public static function getCreationMetadata(ReflectionClass|ReflectionType $typehint, ApieContext $context): MetadataInterface |
163 | { |
164 | if ($typehint instanceof ReflectionType) { |
165 | return self::getMetadataStrategyForType($typehint)->getCreationMetadata($context); |
166 | } |
167 | return self::getMetadataStrategy($typehint)->getCreationMetadata($context); |
168 | } |
169 | |
170 | /** |
171 | * @param ReflectionClass<object>|ReflectionType $typehint |
172 | */ |
173 | public static function getModificationMetadata(ReflectionClass|ReflectionType $typehint, ApieContext $context): MetadataInterface |
174 | { |
175 | if ($typehint instanceof ReflectionType) { |
176 | return self::getMetadataStrategyForType($typehint)->getModificationMetadata($context); |
177 | } |
178 | return self::getMetadataStrategy($typehint)->getModificationMetadata($context); |
179 | } |
180 | |
181 | /** |
182 | * @param ReflectionClass<object>|ReflectionType $typehint |
183 | */ |
184 | public static function getResultMetadata(ReflectionClass|ReflectionType $typehint, ApieContext $context): MetadataInterface |
185 | { |
186 | if ($typehint instanceof ReflectionType) { |
187 | return self::getMetadataStrategyForType($typehint)->getResultMetadata($context); |
188 | } |
189 | return self::getMetadataStrategy($typehint)->getResultMetadata($context); |
190 | } |
191 | } |