1 | <?php /** @noinspection ALL */ |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | namespace Doctrine\ORM\Mapping\Driver; |
||
6 | |||
7 | use Doctrine\Common\Annotations\AnnotationReader; |
||
8 | use Doctrine\Common\Annotations\Reader; |
||
9 | use Doctrine\ORM\Annotation; |
||
10 | use Doctrine\ORM\Cache\Exception\CacheException; |
||
11 | use Doctrine\ORM\Events; |
||
12 | use Doctrine\ORM\Mapping; |
||
13 | use Doctrine\ORM\Mapping\Builder; |
||
14 | use FilesystemIterator; |
||
15 | use RecursiveDirectoryIterator; |
||
16 | use RecursiveIteratorIterator; |
||
17 | use RecursiveRegexIterator; |
||
18 | use ReflectionClass; |
||
19 | use ReflectionException; |
||
20 | use ReflectionMethod; |
||
21 | use ReflectionProperty; |
||
22 | use RegexIterator; |
||
23 | use RuntimeException; |
||
24 | use UnexpectedValueException; |
||
25 | use function array_merge; |
||
26 | use function array_unique; |
||
27 | use function class_exists; |
||
28 | use function constant; |
||
29 | use function get_class; |
||
30 | use function get_declared_classes; |
||
31 | use function in_array; |
||
32 | use function is_dir; |
||
33 | use function is_numeric; |
||
34 | use function preg_match; |
||
35 | use function preg_quote; |
||
36 | use function realpath; |
||
37 | use function str_replace; |
||
38 | use function strpos; |
||
39 | |||
40 | /** |
||
41 | * The AnnotationDriver reads the mapping metadata from docblock annotations. |
||
42 | */ |
||
43 | class AnnotationDriver implements MappingDriver |
||
44 | { |
||
45 | /** @var int[] */ |
||
46 | protected $entityAnnotationClasses = [ |
||
47 | Annotation\Entity::class => 1, |
||
48 | Annotation\MappedSuperclass::class => 2, |
||
49 | ]; |
||
50 | |||
51 | /** |
||
52 | * The AnnotationReader. |
||
53 | * |
||
54 | * @var AnnotationReader |
||
55 | */ |
||
56 | protected $reader; |
||
57 | |||
58 | /** |
||
59 | * The paths where to look for mapping files. |
||
60 | * |
||
61 | * @var string[] |
||
62 | */ |
||
63 | protected $paths = []; |
||
64 | |||
65 | /** |
||
66 | * The paths excluded from path where to look for mapping files. |
||
67 | * |
||
68 | * @var string[] |
||
69 | */ |
||
70 | protected $excludePaths = []; |
||
71 | |||
72 | /** |
||
73 | * The file extension of mapping documents. |
||
74 | * |
||
75 | * @var string |
||
76 | */ |
||
77 | protected $fileExtension = '.php'; |
||
78 | |||
79 | /** |
||
80 | * Cache for AnnotationDriver#getAllClassNames(). |
||
81 | * |
||
82 | * @var string[]|null |
||
83 | */ |
||
84 | protected $classNames; |
||
85 | |||
86 | /** |
||
87 | * Initializes a new AnnotationDriver that uses the given AnnotationReader for reading |
||
88 | * docblock annotations. |
||
89 | * |
||
90 | * @param Reader $reader The AnnotationReader to use, duck-typed. |
||
91 | * @param string|string[]|null $paths One or multiple paths where mapping classes can be found. |
||
92 | */ |
||
93 | 2302 | public function __construct(Reader $reader, $paths = null) |
|
94 | { |
||
95 | 2302 | $this->reader = $reader; |
|
96 | |||
97 | 2302 | if ($paths) { |
|
98 | 2216 | $this->addPaths((array) $paths); |
|
99 | } |
||
100 | 2302 | } |
|
101 | |||
102 | /** |
||
103 | * Appends lookup paths to metadata driver. |
||
104 | * |
||
105 | * @param string[] $paths |
||
106 | */ |
||
107 | 2220 | public function addPaths(array $paths) |
|
108 | { |
||
109 | 2220 | $this->paths = array_unique(array_merge($this->paths, $paths)); |
|
110 | 2220 | } |
|
111 | |||
112 | /** |
||
113 | * Retrieves the defined metadata lookup paths. |
||
114 | * |
||
115 | * @return string[] |
||
116 | */ |
||
117 | public function getPaths() |
||
118 | { |
||
119 | return $this->paths; |
||
120 | } |
||
121 | |||
122 | /** |
||
123 | * Append exclude lookup paths to metadata driver. |
||
124 | * |
||
125 | * @param string[] $paths |
||
126 | */ |
||
127 | public function addExcludePaths(array $paths) |
||
128 | { |
||
129 | $this->excludePaths = array_unique(array_merge($this->excludePaths, $paths)); |
||
130 | } |
||
131 | |||
132 | /** |
||
133 | * Retrieve the defined metadata lookup exclude paths. |
||
134 | * |
||
135 | * @return string[] |
||
136 | */ |
||
137 | public function getExcludePaths() |
||
138 | { |
||
139 | return $this->excludePaths; |
||
140 | } |
||
141 | |||
142 | /** |
||
143 | * Retrieve the current annotation reader |
||
144 | * |
||
145 | * @return Reader |
||
146 | */ |
||
147 | 1 | public function getReader() |
|
148 | { |
||
149 | 1 | return $this->reader; |
|
150 | } |
||
151 | |||
152 | /** |
||
153 | * Gets the file extension used to look for mapping files under. |
||
154 | * |
||
155 | * @return string |
||
156 | */ |
||
157 | public function getFileExtension() |
||
158 | { |
||
159 | return $this->fileExtension; |
||
160 | } |
||
161 | |||
162 | /** |
||
163 | * Sets the file extension used to look for mapping files under. |
||
164 | * |
||
165 | * @param string $fileExtension The file extension to set. |
||
166 | */ |
||
167 | public function setFileExtension($fileExtension) |
||
168 | { |
||
169 | $this->fileExtension = $fileExtension; |
||
170 | } |
||
171 | |||
172 | /** |
||
173 | * Returns whether the class with the specified name is transient. Only non-transient |
||
174 | * classes, that is entities and mapped superclasses, should have their metadata loaded. |
||
175 | * |
||
176 | * A class is non-transient if it is annotated with an annotation |
||
177 | * from the {@see AnnotationDriver::entityAnnotationClasses}. |
||
178 | * |
||
179 | * @param string $className |
||
180 | * |
||
181 | * @throws ReflectionException |
||
182 | */ |
||
183 | 194 | public function isTransient($className) : bool |
|
184 | { |
||
185 | 194 | $classAnnotations = $this->reader->getClassAnnotations(new ReflectionClass($className)); |
|
186 | |||
187 | 194 | foreach ($classAnnotations as $annotation) { |
|
188 | 189 | if (isset($this->entityAnnotationClasses[get_class($annotation)])) { |
|
189 | 189 | return false; |
|
190 | } |
||
191 | } |
||
192 | |||
193 | 12 | return true; |
|
194 | } |
||
195 | |||
196 | /** |
||
197 | * {@inheritdoc} |
||
198 | * |
||
199 | * @throws ReflectionException |
||
200 | */ |
||
201 | 60 | public function getAllClassNames() : array |
|
202 | { |
||
203 | 60 | if ($this->classNames !== null) { |
|
204 | 45 | return $this->classNames; |
|
205 | } |
||
206 | |||
207 | 60 | if (! $this->paths) { |
|
208 | throw Mapping\MappingException::pathRequired(); |
||
209 | } |
||
210 | |||
211 | 60 | $classes = []; |
|
212 | 60 | $includedFiles = []; |
|
213 | |||
214 | 60 | foreach ($this->paths as $path) { |
|
215 | 60 | if (! is_dir($path)) { |
|
216 | throw Mapping\MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); |
||
217 | } |
||
218 | |||
219 | 60 | $iterator = new RegexIterator( |
|
220 | 60 | new RecursiveIteratorIterator( |
|
221 | 60 | new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS), |
|
222 | 60 | RecursiveIteratorIterator::LEAVES_ONLY |
|
223 | ), |
||
224 | 60 | '/^.+' . preg_quote($this->fileExtension) . '$/i', |
|
225 | 60 | RecursiveRegexIterator::GET_MATCH |
|
226 | ); |
||
227 | |||
228 | 60 | foreach ($iterator as $file) { |
|
229 | 60 | $sourceFile = $file[0]; |
|
230 | |||
231 | 60 | if (! preg_match('(^phar:)i', $sourceFile)) { |
|
232 | 60 | $sourceFile = realpath($sourceFile); |
|
233 | } |
||
234 | |||
235 | 60 | foreach ($this->excludePaths as $excludePath) { |
|
236 | $exclude = str_replace('\\', '/', realpath($excludePath)); |
||
237 | $current = str_replace('\\', '/', $sourceFile); |
||
238 | |||
239 | if (strpos($current, $exclude) !== false) { |
||
240 | continue 2; |
||
241 | } |
||
242 | } |
||
243 | |||
244 | 60 | require_once $sourceFile; |
|
245 | |||
246 | 60 | $includedFiles[] = $sourceFile; |
|
247 | } |
||
248 | } |
||
249 | |||
250 | 60 | $declared = get_declared_classes(); |
|
251 | |||
252 | 60 | foreach ($declared as $className) { |
|
253 | 60 | $reflectionClass = new ReflectionClass($className); |
|
254 | 60 | $sourceFile = $reflectionClass->getFileName(); |
|
255 | |||
256 | 60 | if (in_array($sourceFile, $includedFiles, true) && ! $this->isTransient($className)) { |
|
257 | 60 | $classes[] = $className; |
|
258 | } |
||
259 | } |
||
260 | |||
261 | 60 | $this->classNames = $classes; |
|
262 | |||
263 | 60 | return $classes; |
|
264 | } |
||
265 | |||
266 | /** |
||
267 | * {@inheritDoc} |
||
268 | * |
||
269 | * @throws CacheException |
||
270 | * @throws Mapping\MappingException |
||
271 | * @throws ReflectionException |
||
272 | * @throws RuntimeException |
||
273 | * @throws UnexpectedValueException |
||
274 | */ |
||
275 | 380 | public function loadMetadataForClass( |
|
276 | string $className, |
||
277 | ?Mapping\ComponentMetadata $parent, |
||
278 | Mapping\ClassMetadataBuildingContext $metadataBuildingContext |
||
279 | ) : Mapping\ComponentMetadata { |
||
280 | 380 | $reflectionClass = new ReflectionClass($className); |
|
281 | 380 | $classAnnotations = $this->getClassAnnotations($reflectionClass); |
|
282 | 380 | $classBuilder = new Builder\ClassMetadataBuilder($metadataBuildingContext); |
|
283 | $classMetadata = $classBuilder |
||
284 | 380 | ->withClassName($reflectionClass->getName()) |
|
285 | 380 | ->withParentMetadata($parent) |
|
286 | 380 | ->withEntityAnnotation($classAnnotations[Annotation\Entity::class] ?? null) |
|
287 | 380 | ->withMappedSuperclassAnnotation($classAnnotations[Annotation\MappedSuperclass::class] ?? null) |
|
288 | 380 | ->withEmbeddableAnnotation($classAnnotations[Annotation\Embeddable::class] ?? null) |
|
289 | 380 | ->withTableAnnotation($classAnnotations[Annotation\Table::class] ?? null) |
|
290 | 380 | ->withInheritanceTypeAnnotation($classAnnotations[Annotation\InheritanceType::class] ?? null) |
|
291 | 380 | ->withDiscriminatorColumnAnnotation($classAnnotations[Annotation\DiscriminatorColumn::class] ?? null) |
|
292 | 380 | ->withDiscriminatorMapAnnotation($classAnnotations[Annotation\DiscriminatorMap::class] ?? null) |
|
293 | 380 | ->withChangeTrackingPolicyAnnotation($classAnnotations[Annotation\ChangeTrackingPolicy::class] ?? null) |
|
294 | 380 | ->withCacheAnnotation($classAnnotations[Annotation\Cache::class] ?? null) |
|
295 | 380 | ->build(); |
|
296 | |||
297 | 374 | if (! $classMetadata->isEmbeddedClass) { |
|
298 | 374 | $this->attachLifecycleCallbacks($classAnnotations, $reflectionClass, $classMetadata); |
|
299 | 374 | $this->attachEntityListeners($classAnnotations, $classMetadata); |
|
300 | } |
||
301 | |||
302 | 374 | $this->attachProperties($classAnnotations, $reflectionClass, $classMetadata, $metadataBuildingContext); |
|
303 | 371 | $this->attachPropertyOverrides($classAnnotations, $reflectionClass, $classMetadata, $metadataBuildingContext); |
|
304 | |||
305 | 371 | return $classMetadata; |
|
306 | } |
||
307 | |||
308 | /** |
||
309 | * @param Annotation\Annotation[] $classAnnotations |
||
310 | */ |
||
311 | 374 | private function attachLifecycleCallbacks( |
|
312 | array $classAnnotations, |
||
313 | ReflectionClass $reflectionClass, |
||
314 | Mapping\ClassMetadata $metadata |
||
315 | ) : void { |
||
316 | // Evaluate @HasLifecycleCallbacks annotation |
||
317 | 374 | if (isset($classAnnotations[Annotation\HasLifecycleCallbacks::class])) { |
|
318 | $eventMap = [ |
||
319 | 14 | Events::prePersist => Annotation\PrePersist::class, |
|
320 | 14 | Events::postPersist => Annotation\PostPersist::class, |
|
321 | 14 | Events::preUpdate => Annotation\PreUpdate::class, |
|
322 | 14 | Events::postUpdate => Annotation\PostUpdate::class, |
|
323 | 14 | Events::preRemove => Annotation\PreRemove::class, |
|
324 | 14 | Events::postRemove => Annotation\PostRemove::class, |
|
325 | 14 | Events::postLoad => Annotation\PostLoad::class, |
|
326 | 14 | Events::preFlush => Annotation\PreFlush::class, |
|
327 | ]; |
||
328 | |||
329 | /** @var ReflectionMethod $reflectionMethod */ |
||
330 | 14 | foreach ($reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $reflectionMethod) { |
|
331 | 13 | $annotations = $this->getMethodAnnotations($reflectionMethod); |
|
332 | |||
333 | 13 | foreach ($eventMap as $eventName => $annotationClassName) { |
|
334 | 13 | if (isset($annotations[$annotationClassName])) { |
|
335 | 12 | $metadata->addLifecycleCallback($eventName, $reflectionMethod->getName()); |
|
336 | } |
||
337 | } |
||
338 | } |
||
339 | } |
||
340 | 374 | } |
|
341 | |||
342 | /** |
||
343 | * @param Annotation\Annotation[] $classAnnotations |
||
344 | * |
||
345 | * @throws ReflectionException |
||
346 | * @throws Mapping\MappingException |
||
347 | */ |
||
348 | 374 | private function attachEntityListeners( |
|
349 | array $classAnnotations, |
||
350 | Mapping\ClassMetadata $metadata |
||
351 | ) : void { |
||
352 | // Evaluate @EntityListeners annotation |
||
353 | 374 | if (isset($classAnnotations[Annotation\EntityListeners::class])) { |
|
354 | /** @var Annotation\EntityListeners $entityListenersAnnot */ |
||
355 | 8 | $entityListenersAnnot = $classAnnotations[Annotation\EntityListeners::class]; |
|
356 | $eventMap = [ |
||
357 | 8 | Events::prePersist => Annotation\PrePersist::class, |
|
358 | 8 | Events::postPersist => Annotation\PostPersist::class, |
|
359 | 8 | Events::preUpdate => Annotation\PreUpdate::class, |
|
360 | 8 | Events::postUpdate => Annotation\PostUpdate::class, |
|
361 | 8 | Events::preRemove => Annotation\PreRemove::class, |
|
362 | 8 | Events::postRemove => Annotation\PostRemove::class, |
|
363 | 8 | Events::postLoad => Annotation\PostLoad::class, |
|
364 | 8 | Events::preFlush => Annotation\PreFlush::class, |
|
365 | ]; |
||
366 | |||
367 | 8 | foreach ($entityListenersAnnot->value as $listenerClassName) { |
|
368 | 8 | if (! class_exists($listenerClassName)) { |
|
369 | throw Mapping\MappingException::entityListenerClassNotFound( |
||
370 | $listenerClassName, |
||
371 | $metadata->getClassName() |
||
372 | ); |
||
373 | } |
||
374 | |||
375 | 8 | $listenerClass = new ReflectionClass($listenerClassName); |
|
376 | |||
377 | /** @var ReflectionMethod $reflectionMethod */ |
||
378 | 8 | foreach ($listenerClass->getMethods(ReflectionMethod::IS_PUBLIC) as $reflectionMethod) { |
|
379 | 8 | $annotations = $this->getMethodAnnotations($reflectionMethod); |
|
380 | |||
381 | 8 | foreach ($eventMap as $eventName => $annotationClassName) { |
|
382 | 8 | if (isset($annotations[$annotationClassName])) { |
|
383 | 6 | $metadata->addEntityListener($eventName, $listenerClassName, $reflectionMethod->getName()); |
|
384 | } |
||
385 | } |
||
386 | } |
||
387 | } |
||
388 | } |
||
389 | 374 | } |
|
390 | |||
391 | /** |
||
392 | * @param Annotation\Annotation[] $classAnnotations |
||
393 | * |
||
394 | * @throws Mapping\MappingException |
||
395 | */ |
||
396 | 374 | private function attachProperties( |
|
397 | array $classAnnotations, |
||
398 | ReflectionClass $reflectionClass, |
||
399 | Mapping\ClassMetadata $metadata, |
||
400 | Mapping\ClassMetadataBuildingContext $metadataBuildingContext |
||
401 | ) : void { |
||
402 | // Evaluate annotations on properties/fields |
||
403 | 374 | $propertyBuilder = new Builder\PropertyMetadataBuilder($metadataBuildingContext); |
|
404 | |||
405 | /** @var ReflectionProperty $reflProperty */ |
||
406 | 374 | foreach ($reflectionClass->getProperties() as $reflectionProperty) { |
|
407 | 374 | if ($reflectionProperty->getDeclaringClass()->getName() !== $reflectionClass->getName()) { |
|
408 | 75 | continue; |
|
409 | } |
||
410 | |||
411 | 374 | $propertyAnnotations = $this->getPropertyAnnotations($reflectionProperty); |
|
412 | $propertyMetadata = $propertyBuilder |
||
413 | 373 | ->withComponentMetadata($metadata) |
|
414 | 373 | ->withFieldName($reflectionProperty->getName()) |
|
415 | 373 | ->withIdAnnotation($propertyAnnotations[Annotation\Id::class] ?? null) |
|
416 | 373 | ->withCacheAnnotation($propertyAnnotations[Annotation\Cache::class] ?? null) |
|
417 | 373 | ->withColumnAnnotation($propertyAnnotations[Annotation\Column::class] ?? null) |
|
418 | 373 | ->withEmbeddedAnnotation($propertyAnnotations[Annotation\Embedded::class] ?? null) |
|
419 | 373 | ->withOneToOneAnnotation($propertyAnnotations[Annotation\OneToOne::class] ?? null) |
|
420 | 373 | ->withManyToOneAnnotation($propertyAnnotations[Annotation\ManyToOne::class] ?? null) |
|
421 | 373 | ->withOneToManyAnnotation($propertyAnnotations[Annotation\OneToMany::class] ?? null) |
|
422 | 373 | ->withManyToManyAnnotation($propertyAnnotations[Annotation\ManyToMany::class] ?? null) |
|
423 | 373 | ->withJoinTableAnnotation($propertyAnnotations[Annotation\JoinTable::class] ?? null) |
|
424 | 373 | ->withJoinColumnsAnnotation($propertyAnnotations[Annotation\JoinColumns::class] ?? null) |
|
425 | 373 | ->withJoinColumnAnnotation($propertyAnnotations[Annotation\JoinColumn::class] ?? null) |
|
426 | 373 | ->withOrderByAnnotation($propertyAnnotations[Annotation\OrderBy::class] ?? null) |
|
427 | 373 | ->withVersionAnnotation($propertyAnnotations[Annotation\Version::class] ?? null) |
|
428 | 373 | ->withGeneratedValueAnnotation($propertyAnnotations[Annotation\GeneratedValue::class] ?? null) |
|
429 | 373 | ->withSequenceGeneratorAnnotation($propertyAnnotations[Annotation\SequenceGenerator::class] ?? null) |
|
430 | 373 | ->withCustomIdGeneratorAnnotation($propertyAnnotations[Annotation\CustomIdGenerator::class] ?? null) |
|
431 | 373 | ->build(); |
|
432 | |||
433 | 371 | $metadata->addProperty($propertyMetadata); |
|
434 | } |
||
435 | 371 | } |
|
436 | |||
437 | /** |
||
438 | * @param Annotation\Annotation[] $classAnnotations |
||
439 | * |
||
440 | * @throws Mapping\MappingException |
||
441 | */ |
||
442 | 371 | private function attachPropertyOverrides( |
|
443 | array $classAnnotations, |
||
444 | ReflectionClass $reflectionClass, |
||
445 | Mapping\ClassMetadata $metadata, |
||
446 | Mapping\ClassMetadataBuildingContext $metadataBuildingContext |
||
447 | ) : void { |
||
448 | // Evaluate AssociationOverrides annotation |
||
449 | 371 | if (isset($classAnnotations[Annotation\AssociationOverrides::class])) { |
|
450 | 5 | $associationOverridesAnnot = $classAnnotations[Annotation\AssociationOverrides::class]; |
|
451 | |||
452 | 5 | foreach ($associationOverridesAnnot->value as $associationOverrideAnnotation) { |
|
453 | 5 | $fieldName = $associationOverrideAnnotation->name; |
|
454 | 5 | $property = $metadata->getProperty($fieldName); |
|
455 | |||
456 | 5 | if (! $property) { |
|
457 | throw Mapping\MappingException::invalidOverrideFieldName($metadata->getClassName(), $fieldName); |
||
458 | } |
||
459 | |||
460 | 5 | $override = clone $property; |
|
461 | |||
462 | // Check for JoinColumn/JoinColumns annotations |
||
463 | 5 | if ($associationOverrideAnnotation->joinColumns) { |
|
464 | 3 | $joinColumnBuilder = new Builder\JoinColumnMetadataBuilder($metadataBuildingContext); |
|
465 | |||
466 | $joinColumnBuilder |
||
467 | 3 | ->withComponentMetadata($metadata) |
|
468 | 3 | ->withFieldName($fieldName); |
|
469 | |||
470 | 3 | $joinColumns = []; |
|
471 | |||
472 | 3 | foreach ($associationOverrideAnnotation->joinColumns as $joinColumnAnnotation) { |
|
473 | 3 | $joinColumnBuilder->withJoinColumnAnnotation($joinColumnAnnotation); |
|
474 | |||
475 | 3 | $joinColumnMetadata = $joinColumnBuilder->build(); |
|
476 | 3 | $columnName = $joinColumnMetadata->getColumnName(); |
|
477 | |||
478 | // @todo guilhermeblanco Open an issue to discuss making this scenario impossible. |
||
479 | //if ($metadata->checkPropertyDuplication($columnName)) { |
||
480 | // throw Mapping\MappingException::duplicateColumnName($metadata->getClassName(), $columnName); |
||
481 | //} |
||
482 | |||
483 | 3 | $joinColumns[] = $joinColumnMetadata; |
|
484 | } |
||
485 | |||
486 | 3 | $override->setJoinColumns($joinColumns); |
|
487 | } |
||
488 | |||
489 | // Check for JoinTable annotations |
||
490 | 5 | if ($associationOverrideAnnotation->joinTable) { |
|
491 | 2 | $joinTableBuilder = new Builder\JoinTableMetadataBuilder($metadataBuildingContext); |
|
492 | |||
493 | $joinTableBuilder |
||
494 | 2 | ->withComponentMetadata($metadata) |
|
495 | 2 | ->withFieldName($fieldName) |
|
496 | 2 | ->withTargetEntity($property->getTargetEntity()) |
|
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
497 | 2 | ->withJoinTableAnnotation($associationOverrideAnnotation->joinTable); |
|
498 | |||
499 | 2 | $override->setJoinTable($joinTableBuilder->build()); |
|
500 | } |
||
501 | |||
502 | // Check for inversedBy |
||
503 | 5 | if ($associationOverrideAnnotation->inversedBy) { |
|
504 | 1 | $override->setInversedBy($associationOverrideAnnotation->inversedBy); |
|
505 | } |
||
506 | |||
507 | // Check for fetch |
||
508 | 5 | if ($associationOverrideAnnotation->fetch) { |
|
509 | 1 | $override->setFetchMode(constant(Mapping\FetchMode::class . '::' . $associationOverrideAnnotation->fetch)); |
|
510 | } |
||
511 | |||
512 | 5 | $metadata->setPropertyOverride($override); |
|
513 | } |
||
514 | } |
||
515 | |||
516 | // Evaluate AttributeOverrides annotation |
||
517 | 371 | if (isset($classAnnotations[Annotation\AttributeOverrides::class])) { |
|
518 | 3 | $attributeOverridesAnnot = $classAnnotations[Annotation\AttributeOverrides::class]; |
|
519 | 3 | $fieldBuilder = new Builder\FieldMetadataBuilder($metadataBuildingContext); |
|
520 | |||
521 | $fieldBuilder |
||
522 | 3 | ->withComponentMetadata($metadata) |
|
523 | 3 | ->withIdAnnotation(null) |
|
524 | 3 | ->withVersionAnnotation(null); |
|
525 | |||
526 | 3 | foreach ($attributeOverridesAnnot->value as $attributeOverrideAnnotation) { |
|
527 | 3 | $fieldName = $attributeOverrideAnnotation->name; |
|
528 | 3 | $property = $metadata->getProperty($fieldName); |
|
529 | |||
530 | 3 | if (! $property) { |
|
531 | throw Mapping\MappingException::invalidOverrideFieldName($metadata->getClassName(), $fieldName); |
||
532 | } |
||
533 | |||
534 | $fieldBuilder |
||
535 | 3 | ->withFieldName($fieldName) |
|
536 | 3 | ->withColumnAnnotation($attributeOverrideAnnotation->column); |
|
537 | |||
538 | 3 | $fieldMetadata = $fieldBuilder->build(); |
|
539 | 3 | $columnName = $fieldMetadata->getColumnName(); |
|
540 | |||
541 | // Prevent column duplication |
||
542 | 3 | if ($metadata->checkPropertyDuplication($columnName)) { |
|
543 | throw Mapping\MappingException::duplicateColumnName($metadata->getClassName(), $columnName); |
||
544 | } |
||
545 | |||
546 | 3 | $metadata->setPropertyOverride($fieldMetadata); |
|
547 | } |
||
548 | } |
||
549 | 371 | } |
|
550 | |||
551 | /** |
||
552 | * @return Annotation\Annotation[] |
||
553 | */ |
||
554 | 380 | private function getClassAnnotations(ReflectionClass $reflectionClass) : array |
|
555 | { |
||
556 | 380 | $classAnnotations = $this->reader->getClassAnnotations($reflectionClass); |
|
557 | |||
558 | 380 | foreach ($classAnnotations as $key => $annot) { |
|
559 | 374 | if (! is_numeric($key)) { |
|
560 | continue; |
||
561 | } |
||
562 | |||
563 | 374 | $classAnnotations[get_class($annot)] = $annot; |
|
564 | } |
||
565 | |||
566 | 380 | return $classAnnotations; |
|
567 | } |
||
568 | |||
569 | /** |
||
570 | * @return Annotation\Annotation[] |
||
571 | */ |
||
572 | 374 | private function getPropertyAnnotations(ReflectionProperty $reflectionProperty) : array |
|
573 | { |
||
574 | 374 | $propertyAnnotations = $this->reader->getPropertyAnnotations($reflectionProperty); |
|
575 | |||
576 | 373 | foreach ($propertyAnnotations as $key => $annot) { |
|
577 | 373 | if (! is_numeric($key)) { |
|
578 | continue; |
||
579 | } |
||
580 | |||
581 | 373 | $propertyAnnotations[get_class($annot)] = $annot; |
|
582 | } |
||
583 | |||
584 | 373 | return $propertyAnnotations; |
|
585 | } |
||
586 | |||
587 | /** |
||
588 | * @return Annotation\Annotation[] |
||
589 | */ |
||
590 | 21 | private function getMethodAnnotations(ReflectionMethod $reflectionMethod) : array |
|
591 | { |
||
592 | 21 | $methodAnnotations = $this->reader->getMethodAnnotations($reflectionMethod); |
|
593 | |||
594 | 21 | foreach ($methodAnnotations as $key => $annot) { |
|
595 | 18 | if (! is_numeric($key)) { |
|
596 | continue; |
||
597 | } |
||
598 | |||
599 | 18 | $methodAnnotations[get_class($annot)] = $annot; |
|
600 | } |
||
601 | |||
602 | 21 | return $methodAnnotations; |
|
603 | } |
||
604 | } |
||
605 |