1 | <?php |
||||
2 | |||||
3 | declare(strict_types=1); |
||||
4 | |||||
5 | namespace Doctrine\ORM\Mapping\Driver; |
||||
6 | |||||
7 | use Doctrine\Common\Annotations\AnnotationReader; |
||||
8 | use Doctrine\Common\Persistence\Mapping\Driver\FileLocator; |
||||
9 | use Doctrine\DBAL\Types\Type; |
||||
10 | use Doctrine\ORM\Annotation; |
||||
11 | use Doctrine\ORM\Events; |
||||
12 | use Doctrine\ORM\Mapping; |
||||
13 | use Doctrine\ORM\Mapping\Factory; |
||||
14 | use ReflectionClass; |
||||
15 | use ReflectionMethod; |
||||
16 | use ReflectionProperty; |
||||
17 | use UnexpectedValueException; |
||||
18 | use function array_diff; |
||||
19 | use function array_filter; |
||||
20 | use function array_intersect; |
||||
21 | use function array_map; |
||||
22 | use function array_merge; |
||||
23 | use function class_exists; |
||||
24 | use function constant; |
||||
25 | use function count; |
||||
26 | use function defined; |
||||
27 | use function get_class; |
||||
28 | use function in_array; |
||||
29 | use function is_numeric; |
||||
30 | use function sprintf; |
||||
31 | use function str_replace; |
||||
32 | use function strtolower; |
||||
33 | use function strtoupper; |
||||
34 | |||||
35 | class NewAnnotationDriver implements MappingDriver |
||||
36 | { |
||||
37 | /** @var int[] */ |
||||
38 | protected static $entityAnnotationClasses = [ |
||||
39 | Annotation\Entity::class => 1, |
||||
40 | Annotation\MappedSuperclass::class => 2, |
||||
41 | ]; |
||||
42 | |||||
43 | /** |
||||
44 | * The Annotation reader. |
||||
45 | * |
||||
46 | * @var AnnotationReader |
||||
47 | */ |
||||
48 | protected $reader; |
||||
49 | |||||
50 | /** |
||||
51 | * The file locator. |
||||
52 | * |
||||
53 | * @var FileLocator |
||||
54 | */ |
||||
55 | protected $locator; |
||||
56 | |||||
57 | /** @var Factory\NamingStrategy */ |
||||
58 | protected $namingStrategy; |
||||
59 | |||||
60 | /** |
||||
61 | * Cache for AnnotationDriver#getAllClassNames(). |
||||
62 | * |
||||
63 | * @var string[]|null |
||||
64 | */ |
||||
65 | private $classNames; |
||||
66 | |||||
67 | /** |
||||
68 | * Initializes a new AnnotationDriver that uses the given AnnotationReader for reading docblock annotations. |
||||
69 | * |
||||
70 | * @param AnnotationReader $reader The AnnotationReader to use, duck-typed. |
||||
71 | * @param FileLocator $locator A FileLocator or one/multiple paths where mapping documents can be found. |
||||
72 | * @param Factory\NamingStrategy $namingStrategy The NamingStrategy to use. |
||||
73 | */ |
||||
74 | public function __construct(AnnotationReader $reader, FileLocator $locator, Factory\NamingStrategy $namingStrategy) |
||||
75 | { |
||||
76 | $this->reader = $reader; |
||||
77 | $this->locator = $locator; |
||||
78 | $this->namingStrategy = $namingStrategy; |
||||
79 | } |
||||
80 | |||||
81 | /** |
||||
82 | * {@inheritdoc} |
||||
83 | * |
||||
84 | * @return Mapping\ClassMetadata |
||||
85 | * |
||||
86 | * @throws Mapping\MappingException |
||||
87 | */ |
||||
88 | public function loadMetadataForClass( |
||||
89 | string $className, |
||||
90 | Mapping\ClassMetadata $metadata, |
||||
91 | Mapping\ClassMetadataBuildingContext $metadataBuildingContext |
||||
92 | ) { |
||||
93 | // IMPORTANT: We're handling $metadata as "parent" metadata here, while building the $className ClassMetadata. |
||||
94 | $reflectionClass = new ReflectionClass($className); |
||||
95 | |||||
96 | // Evaluate annotations on class metadata |
||||
97 | $classAnnotations = $this->getClassAnnotations($reflectionClass); |
||||
98 | $classMetadata = $this->convertClassAnnotationsToClassMetadata( |
||||
99 | $classAnnotations, |
||||
100 | $reflectionClass, |
||||
101 | $metadata |
||||
102 | ); |
||||
103 | |||||
104 | // Evaluate @Cache annotation |
||||
105 | if (isset($classAnnotations[Annotation\Cache::class])) { |
||||
106 | $cacheAnnot = $classAnnotations[Annotation\Cache::class]; |
||||
107 | $cache = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata); |
||||
108 | |||||
109 | $classMetadata->setCache($cache); |
||||
110 | } |
||||
111 | |||||
112 | // Evaluate annotations on properties/fields |
||||
113 | /** @var ReflectionProperty $reflectionProperty */ |
||||
114 | foreach ($reflectionClass->getProperties() as $reflectionProperty) { |
||||
115 | if ($reflectionProperty->getDeclaringClass()->getClassName() !== $reflectionClass->getName()) { |
||||
116 | continue; |
||||
117 | } |
||||
118 | |||||
119 | $propertyAnnotations = $this->getPropertyAnnotations($reflectionProperty); |
||||
120 | $property = $this->convertReflectionPropertyAnnotationsToProperty( |
||||
121 | $reflectionProperty, |
||||
122 | $propertyAnnotations, |
||||
123 | $classMetadata |
||||
124 | ); |
||||
125 | |||||
126 | $classMetadata->addDeclaredProperty($property); |
||||
127 | } |
||||
128 | |||||
129 | return $classMetadata; |
||||
0 ignored issues
–
show
|
|||||
130 | } |
||||
131 | |||||
132 | /** |
||||
133 | * {@inheritdoc} |
||||
134 | */ |
||||
135 | public function getAllClassNames() |
||||
136 | { |
||||
137 | if ($this->classNames !== null) { |
||||
138 | return $this->classNames; |
||||
139 | } |
||||
140 | |||||
141 | $classNames = array_filter( |
||||
142 | $this->locator->getAllClassNames(null), |
||||
143 | function ($className) { |
||||
144 | return ! $this->isTransient($className); |
||||
145 | } |
||||
146 | ); |
||||
147 | |||||
148 | $this->classNames = $classNames; |
||||
149 | |||||
150 | return $classNames; |
||||
151 | } |
||||
152 | |||||
153 | /** |
||||
154 | * {@inheritdoc} |
||||
155 | */ |
||||
156 | public function isTransient($className) |
||||
157 | { |
||||
158 | $reflectionClass = new ReflectionClass($className); |
||||
159 | $classAnnotations = $this->reader->getClassAnnotations($reflectionClass); |
||||
160 | |||||
161 | foreach ($classAnnotations as $annotation) { |
||||
162 | if (isset(self::$entityAnnotationClasses[get_class($annotation)])) { |
||||
163 | return false; |
||||
164 | } |
||||
165 | } |
||||
166 | |||||
167 | return true; |
||||
168 | } |
||||
169 | |||||
170 | /** |
||||
171 | * @param Annotation\Annotation[] $classAnnotations |
||||
172 | * |
||||
173 | * @return Mapping\ClassMetadata|Mapping\ComponentMetadata |
||||
174 | * |
||||
175 | * @throws Mapping\MappingException |
||||
176 | */ |
||||
177 | private function convertClassAnnotationsToClassMetadata( |
||||
178 | array $classAnnotations, |
||||
179 | ReflectionClass $reflectionClass, |
||||
180 | Mapping\ClassMetadata $parent |
||||
181 | ) { |
||||
182 | switch (true) { |
||||
183 | case isset($classAnnotations[Annotation\Entity::class]): |
||||
184 | return $this->convertClassAnnotationsToEntityClassMetadata( |
||||
185 | $classAnnotations, |
||||
186 | $reflectionClass, |
||||
187 | $parent |
||||
188 | ); |
||||
189 | |||||
190 | break; |
||||
191 | |||||
192 | case isset($classAnnotations[Annotation\MappedSuperclass::class]): |
||||
193 | return $this->convertClassAnnotationsToMappedSuperClassMetadata( |
||||
194 | $classAnnotations, |
||||
195 | $reflectionClass, |
||||
196 | $parent |
||||
197 | ); |
||||
198 | case isset($classAnnotations[Annotation\Embeddable::class]): |
||||
199 | return $this->convertClassAnnotationsToEntityClassMetadata( |
||||
200 | $classAnnotations, |
||||
201 | $reflectionClass, |
||||
202 | $parent |
||||
203 | ); |
||||
204 | default: |
||||
205 | throw Mapping\MappingException::classIsNotAValidEntityOrMappedSuperClass($reflectionClass->getName()); |
||||
206 | } |
||||
207 | } |
||||
208 | |||||
209 | /** |
||||
210 | * @param Annotation\Annotation[] $classAnnotations |
||||
211 | * |
||||
212 | * @return Mapping\ClassMetadata |
||||
213 | * |
||||
214 | * @throws Mapping\MappingException |
||||
215 | * @throws UnexpectedValueException |
||||
216 | */ |
||||
217 | private function convertClassAnnotationsToEntityClassMetadata( |
||||
218 | array $classAnnotations, |
||||
219 | ReflectionClass $reflectionClass, |
||||
220 | Mapping\ClassMetadata $parent |
||||
221 | ) { |
||||
222 | /** @var Annotation\Entity $entityAnnot */ |
||||
223 | $entityAnnot = $classAnnotations[Annotation\Entity::class]; |
||||
224 | $classMetadata = new Mapping\ClassMetadata($reflectionClass->getName(), $parent); |
||||
0 ignored issues
–
show
$parent of type Doctrine\ORM\Mapping\ClassMetadata is incompatible with the type Doctrine\ORM\Mapping\ClassMetadataBuildingContext expected by parameter $metadataBuildingContext of Doctrine\ORM\Mapping\ClassMetadata::__construct() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
225 | |||||
226 | if ($entityAnnot->repositoryClass !== null) { |
||||
227 | $classMetadata->setCustomRepositoryClassName($entityAnnot->repositoryClass); |
||||
228 | } |
||||
229 | |||||
230 | if ($entityAnnot->readOnly) { |
||||
231 | $classMetadata->asReadOnly(); |
||||
232 | } |
||||
233 | |||||
234 | // Evaluate @Table annotation |
||||
235 | if (isset($classAnnotations[Annotation\Table::class])) { |
||||
236 | /** @var Annotation\Table $tableAnnot */ |
||||
237 | $tableAnnot = $classAnnotations[Annotation\Table::class]; |
||||
238 | $table = $this->convertTableAnnotationToTableMetadata($tableAnnot); |
||||
239 | |||||
240 | $classMetadata->setTable($table); |
||||
241 | } |
||||
242 | |||||
243 | // Evaluate @ChangeTrackingPolicy annotation |
||||
244 | if (isset($classAnnotations[Annotation\ChangeTrackingPolicy::class])) { |
||||
245 | /** @var Annotation\ChangeTrackingPolicy $changeTrackingAnnot */ |
||||
246 | $changeTrackingAnnot = $classAnnotations[Annotation\ChangeTrackingPolicy::class]; |
||||
247 | |||||
248 | $classMetadata->setChangeTrackingPolicy( |
||||
249 | constant(sprintf('%s::%s', Mapping\ChangeTrackingPolicy::class, $changeTrackingAnnot->value)) |
||||
250 | ); |
||||
251 | } |
||||
252 | |||||
253 | // Evaluate @EntityListeners annotation |
||||
254 | if (isset($classAnnotations[Annotation\EntityListeners::class])) { |
||||
255 | /** @var Annotation\EntityListeners $entityListenersAnnot */ |
||||
256 | $entityListenersAnnot = $classAnnotations[Annotation\EntityListeners::class]; |
||||
257 | |||||
258 | foreach ($entityListenersAnnot->value as $listenerClassName) { |
||||
259 | if (! class_exists($listenerClassName)) { |
||||
260 | throw Mapping\MappingException::entityListenerClassNotFound( |
||||
261 | $listenerClassName, |
||||
262 | $reflectionClass->getName() |
||||
263 | ); |
||||
264 | } |
||||
265 | |||||
266 | $listenerClass = new ReflectionClass($listenerClassName); |
||||
267 | |||||
268 | /** @var ReflectionMethod $method */ |
||||
269 | foreach ($listenerClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { |
||||
270 | foreach ($this->getMethodCallbacks($method) as $callback) { |
||||
271 | $classMetadata->addEntityListener($callback, $listenerClassName, $method->getName()); |
||||
272 | } |
||||
273 | } |
||||
274 | } |
||||
275 | } |
||||
276 | |||||
277 | // Evaluate @HasLifecycleCallbacks annotation |
||||
278 | if (isset($classAnnotations[Annotation\HasLifecycleCallbacks::class])) { |
||||
279 | /** @var ReflectionMethod $method */ |
||||
280 | foreach ($reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { |
||||
281 | foreach ($this->getMethodCallbacks($method) as $callback) { |
||||
282 | $classMetadata->addLifecycleCallback($method->getName(), $callback); |
||||
283 | } |
||||
284 | } |
||||
285 | } |
||||
286 | |||||
287 | // Evaluate @InheritanceType annotation |
||||
288 | if (isset($classAnnotations[Annotation\InheritanceType::class])) { |
||||
289 | /** @var Annotation\InheritanceType $inheritanceTypeAnnot */ |
||||
290 | $inheritanceTypeAnnot = $classAnnotations[Annotation\InheritanceType::class]; |
||||
291 | |||||
292 | $classMetadata->setInheritanceType( |
||||
293 | constant(sprintf('%s::%s', Mapping\InheritanceType::class, $inheritanceTypeAnnot->value)) |
||||
294 | ); |
||||
295 | |||||
296 | if ($classMetadata->inheritanceType !== Mapping\InheritanceType::NONE) { |
||||
297 | $discriminatorColumn = new Mapping\DiscriminatorColumnMetadata(); |
||||
298 | |||||
299 | // Evaluate @DiscriminatorColumn annotation |
||||
300 | if (isset($classAnnotations[Annotation\DiscriminatorColumn::class])) { |
||||
301 | /** @var Annotation\DiscriminatorColumn $discriminatorColumnAnnot */ |
||||
302 | $discriminatorColumnAnnot = $classAnnotations[Annotation\DiscriminatorColumn::class]; |
||||
303 | |||||
304 | $discriminatorColumn->setColumnName($discriminatorColumnAnnot->name); |
||||
305 | |||||
306 | if (! empty($discriminatorColumnAnnot->columnDefinition)) { |
||||
307 | $discriminatorColumn->setColumnDefinition($discriminatorColumnAnnot->columnDefinition); |
||||
308 | } |
||||
309 | |||||
310 | if (! empty($discriminatorColumnAnnot->type)) { |
||||
311 | $discriminatorColumn->setType(Type::getType($discriminatorColumnAnnot->type)); |
||||
312 | } |
||||
313 | |||||
314 | if (! empty($discriminatorColumnAnnot->length)) { |
||||
315 | $discriminatorColumn->setLength($discriminatorColumnAnnot->length); |
||||
316 | } |
||||
317 | } |
||||
318 | |||||
319 | if (empty($discriminatorColumn->getColumnName())) { |
||||
320 | throw Mapping\MappingException::nameIsMandatoryForDiscriminatorColumns($reflectionClass->getName()); |
||||
321 | } |
||||
322 | |||||
323 | $classMetadata->setDiscriminatorColumn($discriminatorColumn); |
||||
324 | |||||
325 | // Evaluate @DiscriminatorMap annotation |
||||
326 | if (isset($classAnnotations[Annotation\DiscriminatorMap::class])) { |
||||
327 | /** @var Annotation\DiscriminatorMap $discriminatorMapAnnotation */ |
||||
328 | $discriminatorMapAnnotation = $classAnnotations[Annotation\DiscriminatorMap::class]; |
||||
329 | $discriminatorMap = $discriminatorMapAnnotation->value; |
||||
330 | |||||
331 | $classMetadata->setDiscriminatorMap($discriminatorMap); |
||||
332 | } |
||||
333 | } |
||||
334 | } |
||||
335 | |||||
336 | return $classMetadata; |
||||
337 | } |
||||
338 | |||||
339 | /** |
||||
340 | * @param Annotation\Annotation[] $classAnnotations |
||||
341 | * |
||||
342 | * @return Mapping\MappedSuperClassMetadata |
||||
343 | */ |
||||
344 | private function convertClassAnnotationsToMappedSuperClassMetadata( |
||||
345 | array $classAnnotations, |
||||
346 | ReflectionClass $reflectionClass, |
||||
347 | Mapping\ClassMetadata $parent |
||||
348 | ) { |
||||
349 | /** @var Annotation\MappedSuperclass $mappedSuperclassAnnot */ |
||||
350 | $mappedSuperclassAnnot = $classAnnotations[Annotation\MappedSuperclass::class]; |
||||
351 | $classMetadata = new Mapping\MappedSuperClassMetadata($reflectionClass->getName(), $parent); |
||||
352 | |||||
353 | if ($mappedSuperclassAnnot->repositoryClass !== null) { |
||||
354 | $classMetadata->setCustomRepositoryClassName($mappedSuperclassAnnot->repositoryClass); |
||||
355 | } |
||||
356 | |||||
357 | return $classMetadata; |
||||
358 | } |
||||
359 | |||||
360 | /** |
||||
361 | * Parse the given Table as TableMetadata |
||||
362 | * |
||||
363 | * @return Mapping\TableMetadata |
||||
364 | */ |
||||
365 | private function convertTableAnnotationToTableMetadata(Annotation\Table $tableAnnot) |
||||
366 | { |
||||
367 | $table = new Mapping\TableMetadata(); |
||||
368 | |||||
369 | if (! empty($tableAnnot->name)) { |
||||
370 | $table->setName($tableAnnot->name); |
||||
371 | } |
||||
372 | |||||
373 | if (! empty($tableAnnot->schema)) { |
||||
374 | $table->setSchema($tableAnnot->schema); |
||||
375 | } |
||||
376 | |||||
377 | foreach ($tableAnnot->options as $optionName => $optionValue) { |
||||
378 | $table->addOption($optionName, $optionValue); |
||||
379 | } |
||||
380 | |||||
381 | foreach ($tableAnnot->indexes as $indexAnnot) { |
||||
382 | $table->addIndex([ |
||||
383 | 'name' => $indexAnnot->name, |
||||
384 | 'columns' => $indexAnnot->columns, |
||||
385 | 'unique' => $indexAnnot->unique, |
||||
386 | 'options' => $indexAnnot->options, |
||||
387 | 'flags' => $indexAnnot->flags, |
||||
388 | ]); |
||||
389 | } |
||||
390 | |||||
391 | foreach ($tableAnnot->uniqueConstraints as $uniqueConstraintAnnot) { |
||||
392 | $table->addUniqueConstraint([ |
||||
393 | 'name' => $uniqueConstraintAnnot->name, |
||||
394 | 'columns' => $uniqueConstraintAnnot->columns, |
||||
395 | 'options' => $uniqueConstraintAnnot->options, |
||||
396 | 'flags' => $uniqueConstraintAnnot->flags, |
||||
397 | ]); |
||||
398 | } |
||||
399 | |||||
400 | return $table; |
||||
401 | } |
||||
402 | |||||
403 | /** |
||||
404 | * Parse the given Cache as CacheMetadata |
||||
405 | * |
||||
406 | * @param string|null $fieldName |
||||
407 | * |
||||
408 | * @return Mapping\CacheMetadata |
||||
409 | */ |
||||
410 | private function convertCacheAnnotationToCacheMetadata( |
||||
411 | Annotation\Cache $cacheAnnot, |
||||
412 | Mapping\ClassMetadata $metadata, |
||||
413 | $fieldName = null |
||||
414 | ) { |
||||
415 | $usage = constant(sprintf('%s::%s', Mapping\CacheUsage::class, $cacheAnnot->usage)); |
||||
416 | $baseRegion = strtolower(str_replace('\\', '_', $metadata->getRootClassName())); |
||||
417 | $defaultRegion = $baseRegion . ($fieldName ? '__' . $fieldName : ''); |
||||
418 | |||||
419 | return new Mapping\CacheMetadata($usage, $cacheAnnot->region ?: $defaultRegion); |
||||
420 | } |
||||
421 | |||||
422 | /** |
||||
423 | * @param Annotation\Annotation[] $propertyAnnotations |
||||
424 | * |
||||
425 | * @return Mapping\Property |
||||
426 | * |
||||
427 | * @throws Mapping\MappingException |
||||
428 | */ |
||||
429 | private function convertReflectionPropertyAnnotationsToProperty( |
||||
430 | ReflectionProperty $reflectionProperty, |
||||
431 | array $propertyAnnotations, |
||||
432 | Mapping\ClassMetadata $classMetadata |
||||
433 | ) { |
||||
434 | // Field can only be annotated with one of: |
||||
435 | // @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany, @Embedded |
||||
436 | switch (true) { |
||||
437 | case isset($propertyAnnotations[Annotation\Column::class]): |
||||
438 | return $this->convertReflectionPropertyToFieldMetadata( |
||||
439 | $reflectionProperty, |
||||
440 | $propertyAnnotations, |
||||
441 | $classMetadata |
||||
442 | ); |
||||
443 | case isset($propertyAnnotations[Annotation\OneToOne::class]): |
||||
444 | return $this->convertReflectionPropertyToOneToOneAssociationMetadata( |
||||
445 | $reflectionProperty, |
||||
446 | $propertyAnnotations, |
||||
447 | $classMetadata |
||||
448 | ); |
||||
449 | case isset($propertyAnnotations[Annotation\ManyToOne::class]): |
||||
450 | return $this->convertReflectionPropertyToManyToOneAssociationMetadata( |
||||
451 | $reflectionProperty, |
||||
452 | $propertyAnnotations, |
||||
453 | $classMetadata |
||||
454 | ); |
||||
455 | case isset($propertyAnnotations[Annotation\OneToMany::class]): |
||||
456 | return $this->convertReflectionPropertyToOneToManyAssociationMetadata( |
||||
457 | $reflectionProperty, |
||||
458 | $propertyAnnotations, |
||||
459 | $classMetadata |
||||
460 | ); |
||||
461 | case isset($propertyAnnotations[Annotation\ManyToMany::class]): |
||||
462 | return $this->convertReflectionPropertyToManyToManyAssociationMetadata( |
||||
463 | $reflectionProperty, |
||||
464 | $propertyAnnotations, |
||||
465 | $classMetadata |
||||
466 | ); |
||||
467 | case isset($propertyAnnotations[Annotation\Embedded::class]): |
||||
468 | // @todo guilhermeblanco Implement later... =) |
||||
469 | break; |
||||
470 | |||||
471 | default: |
||||
472 | return new Mapping\TransientMetadata($reflectionProperty->getName()); |
||||
473 | } |
||||
474 | } |
||||
475 | |||||
476 | /** |
||||
477 | * @param Annotation\Annotation[] $propertyAnnotations |
||||
478 | * |
||||
479 | * @return Mapping\FieldMetadata |
||||
480 | * |
||||
481 | * @throws Mapping\MappingException |
||||
482 | */ |
||||
483 | private function convertReflectionPropertyToFieldMetadata( |
||||
484 | ReflectionProperty $reflectionProperty, |
||||
485 | array $propertyAnnotations, |
||||
486 | Mapping\ClassMetadata $classMetadata |
||||
487 | ) { |
||||
488 | $className = $classMetadata->getClassName(); |
||||
489 | $fieldName = $reflectionProperty->getName(); |
||||
490 | $columnAnnot = $propertyAnnotations[Annotation\Column::class]; |
||||
491 | $isVersioned = isset($propertyAnnotations[Annotation\Version::class]); |
||||
492 | $isPrimaryKey = isset($propertyAnnotations[Annotation\Id::class]); |
||||
493 | |||||
494 | if ($columnAnnot->type === null) { |
||||
495 | throw Mapping\MappingException::propertyTypeIsRequired($className, $fieldName); |
||||
496 | } |
||||
497 | |||||
498 | if ($isVersioned && $isPrimaryKey) { |
||||
499 | throw Mapping\MappingException::cannotVersionIdField($className, $fieldName); |
||||
500 | } |
||||
501 | |||||
502 | $columnName = empty($columnAnnot->name) |
||||
503 | ? $this->namingStrategy->propertyToColumnName($fieldName, $className) |
||||
504 | : $columnAnnot->name; |
||||
505 | |||||
506 | $fieldMetadata = $isVersioned |
||||
507 | ? new Mapping\VersionFieldMetadata($fieldName) |
||||
508 | : new Mapping\FieldMetadata($fieldName); |
||||
509 | |||||
510 | $fieldMetadata->setType(Type::getType($columnAnnot->type)); |
||||
511 | $fieldMetadata->setColumnName($columnName); |
||||
512 | $fieldMetadata->setScale($columnAnnot->scale); |
||||
513 | $fieldMetadata->setPrecision($columnAnnot->precision); |
||||
514 | $fieldMetadata->setNullable($columnAnnot->nullable); |
||||
515 | $fieldMetadata->setUnique($columnAnnot->unique); |
||||
516 | |||||
517 | // Check for Id |
||||
518 | if ($isPrimaryKey) { |
||||
519 | if ($fieldMetadata->getType()->canRequireSQLConversion()) { |
||||
520 | throw Mapping\MappingException::sqlConversionNotAllowedForPrimaryKeyProperties($className, $fieldMetadata); |
||||
521 | } |
||||
522 | |||||
523 | $fieldMetadata->setPrimaryKey(true); |
||||
524 | } |
||||
525 | |||||
526 | if (! empty($columnAnnot->columnDefinition)) { |
||||
527 | $fieldMetadata->setColumnDefinition($columnAnnot->columnDefinition); |
||||
528 | } |
||||
529 | |||||
530 | if (! empty($columnAnnot->length)) { |
||||
531 | $fieldMetadata->setLength($columnAnnot->length); |
||||
532 | } |
||||
533 | |||||
534 | // Assign default options |
||||
535 | $customOptions = $columnAnnot->options ?? []; |
||||
536 | $defaultOptions = []; |
||||
537 | |||||
538 | if ($isVersioned) { |
||||
539 | switch ($fieldMetadata->getTypeName()) { |
||||
540 | case 'integer': |
||||
541 | case 'bigint': |
||||
542 | case 'smallint': |
||||
543 | $defaultOptions['default'] = 1; |
||||
544 | break; |
||||
545 | |||||
546 | case 'datetime': |
||||
547 | $defaultOptions['default'] = 'CURRENT_TIMESTAMP'; |
||||
548 | break; |
||||
549 | |||||
550 | default: |
||||
551 | if (! isset($customOptions['default'])) { |
||||
552 | throw Mapping\MappingException::unsupportedOptimisticLockingType($fieldMetadata->getType()); |
||||
553 | } |
||||
554 | } |
||||
555 | } |
||||
556 | |||||
557 | $fieldMetadata->setOptions(array_merge($defaultOptions, $customOptions)); |
||||
558 | |||||
559 | return $fieldMetadata; |
||||
560 | } |
||||
561 | |||||
562 | /** |
||||
563 | * @param Annotation\Annotation[] $propertyAnnotations |
||||
564 | * |
||||
565 | * @return Mapping\OneToOneAssociationMetadata |
||||
566 | */ |
||||
567 | private function convertReflectionPropertyToOneToOneAssociationMetadata( |
||||
568 | ReflectionProperty $reflectionProperty, |
||||
569 | array $propertyAnnotations, |
||||
570 | Mapping\ClassMetadata $classMetadata |
||||
571 | ) { |
||||
572 | $className = $classMetadata->getClassName(); |
||||
573 | $fieldName = $reflectionProperty->getName(); |
||||
574 | $oneToOneAnnot = $propertyAnnotations[Annotation\OneToOne::class]; |
||||
575 | |||||
576 | if ($oneToOneAnnot->targetEntity === null) { |
||||
577 | throw Mapping\MappingException::missingTargetEntity($fieldName); |
||||
578 | } |
||||
579 | |||||
580 | $assocMetadata = new Mapping\OneToOneAssociationMetadata($fieldName); |
||||
581 | $targetEntity = $oneToOneAnnot->targetEntity; |
||||
582 | |||||
583 | $assocMetadata->setSourceEntity($className); |
||||
584 | $assocMetadata->setTargetEntity($targetEntity); |
||||
585 | $assocMetadata->setCascade($this->getCascade($className, $fieldName, $oneToOneAnnot->cascade)); |
||||
586 | $assocMetadata->setOrphanRemoval($oneToOneAnnot->orphanRemoval); |
||||
587 | $assocMetadata->setFetchMode($this->getFetchMode($className, $oneToOneAnnot->fetch)); |
||||
588 | |||||
589 | if (! empty($oneToOneAnnot->mappedBy)) { |
||||
590 | $assocMetadata->setMappedBy($oneToOneAnnot->mappedBy); |
||||
591 | } |
||||
592 | |||||
593 | if (! empty($oneToOneAnnot->inversedBy)) { |
||||
594 | $assocMetadata->setInversedBy($oneToOneAnnot->inversedBy); |
||||
595 | } |
||||
596 | |||||
597 | // Check for Id |
||||
598 | if (isset($propertyAnnotations[Annotation\Id::class])) { |
||||
599 | $assocMetadata->setPrimaryKey(true); |
||||
600 | } |
||||
601 | |||||
602 | // Check for Cache |
||||
603 | if (isset($propertyAnnotations[Annotation\Cache::class])) { |
||||
604 | $cacheAnnot = $propertyAnnotations[Annotation\Cache::class]; |
||||
605 | $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata, $fieldName); |
||||
606 | |||||
607 | $assocMetadata->setCache($cacheMetadata); |
||||
608 | } |
||||
609 | |||||
610 | // Check for JoinColumn/JoinColumns annotations |
||||
611 | switch (true) { |
||||
612 | case isset($propertyAnnotations[Annotation\JoinColumn::class]): |
||||
613 | $joinColumnAnnot = $propertyAnnotations[Annotation\JoinColumn::class]; |
||||
614 | $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata( |
||||
615 | $reflectionProperty, |
||||
616 | $joinColumnAnnot, |
||||
617 | $classMetadata |
||||
618 | ); |
||||
619 | |||||
620 | $assocMetadata->addJoinColumn($joinColumn); |
||||
621 | |||||
622 | break; |
||||
623 | |||||
624 | case isset($propertyAnnotations[Annotation\JoinColumns::class]): |
||||
625 | $joinColumnsAnnot = $propertyAnnotations[Annotation\JoinColumns::class]; |
||||
626 | |||||
627 | foreach ($joinColumnsAnnot->value as $joinColumnAnnot) { |
||||
628 | $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata( |
||||
629 | $reflectionProperty, |
||||
630 | $joinColumnAnnot, |
||||
631 | $classMetadata |
||||
632 | ); |
||||
633 | |||||
634 | $assocMetadata->addJoinColumn($joinColumn); |
||||
635 | } |
||||
636 | |||||
637 | break; |
||||
638 | } |
||||
639 | |||||
640 | return $assocMetadata; |
||||
641 | } |
||||
642 | |||||
643 | /** |
||||
644 | * @param Annotation\Annotation[] $propertyAnnotations |
||||
645 | * |
||||
646 | * @return Mapping\ManyToOneAssociationMetadata |
||||
647 | */ |
||||
648 | private function convertReflectionPropertyToManyToOneAssociationMetadata( |
||||
649 | ReflectionProperty $reflectionProperty, |
||||
650 | array $propertyAnnotations, |
||||
651 | Mapping\ClassMetadata $classMetadata |
||||
652 | ) { |
||||
653 | $className = $classMetadata->getClassName(); |
||||
654 | $fieldName = $reflectionProperty->getName(); |
||||
655 | $manyToOneAnnot = $propertyAnnotations[Annotation\ManyToOne::class]; |
||||
656 | |||||
657 | if ($manyToOneAnnot->targetEntity === null) { |
||||
658 | throw Mapping\MappingException::missingTargetEntity($fieldName); |
||||
659 | } |
||||
660 | |||||
661 | $assocMetadata = new Mapping\ManyToOneAssociationMetadata($fieldName); |
||||
662 | $targetEntity = $manyToOneAnnot->targetEntity; |
||||
663 | |||||
664 | $assocMetadata->setSourceEntity($className); |
||||
665 | $assocMetadata->setTargetEntity($targetEntity); |
||||
666 | $assocMetadata->setCascade($this->getCascade($className, $fieldName, $manyToOneAnnot->cascade)); |
||||
667 | $assocMetadata->setFetchMode($this->getFetchMode($className, $manyToOneAnnot->fetch)); |
||||
668 | |||||
669 | if (! empty($manyToOneAnnot->inversedBy)) { |
||||
670 | $assocMetadata->setInversedBy($manyToOneAnnot->inversedBy); |
||||
671 | } |
||||
672 | |||||
673 | // Check for Id |
||||
674 | if (isset($propertyAnnotations[Annotation\Id::class])) { |
||||
675 | $assocMetadata->setPrimaryKey(true); |
||||
676 | } |
||||
677 | |||||
678 | // Check for Cache |
||||
679 | if (isset($propertyAnnotations[Annotation\Cache::class])) { |
||||
680 | $cacheAnnot = $propertyAnnotations[Annotation\Cache::class]; |
||||
681 | $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata, $fieldName); |
||||
682 | |||||
683 | $assocMetadata->setCache($cacheMetadata); |
||||
684 | } |
||||
685 | |||||
686 | // Check for JoinColumn/JoinColumns annotations |
||||
687 | switch (true) { |
||||
688 | case isset($propertyAnnotations[Annotation\JoinColumn::class]): |
||||
689 | $joinColumnAnnot = $propertyAnnotations[Annotation\JoinColumn::class]; |
||||
690 | $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata( |
||||
691 | $reflectionProperty, |
||||
692 | $joinColumnAnnot, |
||||
693 | $classMetadata |
||||
694 | ); |
||||
695 | |||||
696 | $assocMetadata->addJoinColumn($joinColumn); |
||||
697 | |||||
698 | break; |
||||
699 | |||||
700 | case isset($propertyAnnotations[Annotation\JoinColumns::class]): |
||||
701 | $joinColumnsAnnot = $propertyAnnotations[Annotation\JoinColumns::class]; |
||||
702 | |||||
703 | foreach ($joinColumnsAnnot->value as $joinColumnAnnot) { |
||||
704 | $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata( |
||||
705 | $reflectionProperty, |
||||
706 | $joinColumnAnnot, |
||||
707 | $classMetadata |
||||
708 | ); |
||||
709 | |||||
710 | $assocMetadata->addJoinColumn($joinColumn); |
||||
711 | } |
||||
712 | |||||
713 | break; |
||||
714 | } |
||||
715 | |||||
716 | return $assocMetadata; |
||||
717 | } |
||||
718 | |||||
719 | /** |
||||
720 | * @param Annotation\Annotation[] $propertyAnnotations |
||||
721 | * |
||||
722 | * @return Mapping\OneToManyAssociationMetadata |
||||
723 | */ |
||||
724 | private function convertReflectionPropertyToOneToManyAssociationMetadata( |
||||
725 | ReflectionProperty $reflectionProperty, |
||||
726 | array $propertyAnnotations, |
||||
727 | Mapping\ClassMetadata $classMetadata |
||||
728 | ) { |
||||
729 | $className = $classMetadata->getClassName(); |
||||
730 | $fieldName = $reflectionProperty->getName(); |
||||
731 | $oneToManyAnnot = $propertyAnnotations[Annotation\OneToMany::class]; |
||||
732 | |||||
733 | if ($oneToManyAnnot->targetEntity === null) { |
||||
734 | throw Mapping\MappingException::missingTargetEntity($fieldName); |
||||
735 | } |
||||
736 | |||||
737 | $assocMetadata = new Mapping\OneToManyAssociationMetadata($fieldName); |
||||
738 | $targetEntity = $oneToManyAnnot->targetEntity; |
||||
739 | |||||
740 | $assocMetadata->setSourceEntity($className); |
||||
741 | $assocMetadata->setTargetEntity($targetEntity); |
||||
742 | $assocMetadata->setCascade($this->getCascade($className, $fieldName, $oneToManyAnnot->cascade)); |
||||
743 | $assocMetadata->setOrphanRemoval($oneToManyAnnot->orphanRemoval); |
||||
744 | $assocMetadata->setFetchMode($this->getFetchMode($className, $oneToManyAnnot->fetch)); |
||||
745 | |||||
746 | if (! empty($oneToManyAnnot->mappedBy)) { |
||||
747 | $assocMetadata->setMappedBy($oneToManyAnnot->mappedBy); |
||||
748 | } |
||||
749 | |||||
750 | if (! empty($oneToManyAnnot->indexBy)) { |
||||
751 | $assocMetadata->setIndexedBy($oneToManyAnnot->indexBy); |
||||
752 | } |
||||
753 | |||||
754 | // Check for OrderBy |
||||
755 | if (isset($propertyAnnotations[Annotation\OrderBy::class])) { |
||||
756 | $orderByAnnot = $propertyAnnotations[Annotation\OrderBy::class]; |
||||
757 | |||||
758 | $assocMetadata->setOrderBy($orderByAnnot->value); |
||||
759 | } |
||||
760 | |||||
761 | // Check for Id |
||||
762 | if (isset($propertyAnnotations[Annotation\Id::class])) { |
||||
763 | $assocMetadata->setPrimaryKey(true); |
||||
764 | } |
||||
765 | |||||
766 | // Check for Cache |
||||
767 | if (isset($propertyAnnotations[Annotation\Cache::class])) { |
||||
768 | $cacheAnnot = $propertyAnnotations[Annotation\Cache::class]; |
||||
769 | $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata, $fieldName); |
||||
770 | |||||
771 | $assocMetadata->setCache($cacheMetadata); |
||||
772 | } |
||||
773 | |||||
774 | return $assocMetadata; |
||||
775 | } |
||||
776 | |||||
777 | /** |
||||
778 | * @param Annotation\Annotation[] $propertyAnnotations |
||||
779 | * |
||||
780 | * @return Mapping\ManyToManyAssociationMetadata |
||||
781 | */ |
||||
782 | private function convertReflectionPropertyToManyToManyAssociationMetadata( |
||||
783 | ReflectionProperty $reflectionProperty, |
||||
784 | array $propertyAnnotations, |
||||
785 | Mapping\ClassMetadata $classMetadata |
||||
786 | ) { |
||||
787 | $className = $classMetadata->getClassName(); |
||||
788 | $fieldName = $reflectionProperty->getName(); |
||||
789 | $manyToManyAnnot = $propertyAnnotations[Annotation\ManyToMany::class]; |
||||
790 | |||||
791 | if ($manyToManyAnnot->targetEntity === null) { |
||||
792 | throw Mapping\MappingException::missingTargetEntity($fieldName); |
||||
793 | } |
||||
794 | |||||
795 | $assocMetadata = new Mapping\ManyToManyAssociationMetadata($fieldName); |
||||
796 | $targetEntity = $manyToManyAnnot->targetEntity; |
||||
797 | |||||
798 | $assocMetadata->setSourceEntity($className); |
||||
799 | $assocMetadata->setTargetEntity($targetEntity); |
||||
800 | $assocMetadata->setCascade($this->getCascade($className, $fieldName, $manyToManyAnnot->cascade)); |
||||
801 | $assocMetadata->setOrphanRemoval($manyToManyAnnot->orphanRemoval); |
||||
802 | $assocMetadata->setFetchMode($this->getFetchMode($className, $manyToManyAnnot->fetch)); |
||||
803 | |||||
804 | if (! empty($manyToManyAnnot->mappedBy)) { |
||||
805 | $assocMetadata->setMappedBy($manyToManyAnnot->mappedBy); |
||||
806 | } |
||||
807 | |||||
808 | if (! empty($manyToManyAnnot->inversedBy)) { |
||||
809 | $assocMetadata->setInversedBy($manyToManyAnnot->inversedBy); |
||||
810 | } |
||||
811 | |||||
812 | if (! empty($manyToManyAnnot->indexBy)) { |
||||
813 | $assocMetadata->setIndexedBy($manyToManyAnnot->indexBy); |
||||
814 | } |
||||
815 | |||||
816 | // Check for JoinTable |
||||
817 | if (isset($propertyAnnotations[Annotation\JoinTable::class])) { |
||||
818 | $joinTableAnnot = $propertyAnnotations[Annotation\JoinTable::class]; |
||||
819 | $joinTableMetadata = $this->convertJoinTableAnnotationToJoinTableMetadata( |
||||
820 | $reflectionProperty, |
||||
821 | $joinTableAnnot, |
||||
822 | $classMetadata |
||||
823 | ); |
||||
824 | |||||
825 | $assocMetadata->setJoinTable($joinTableMetadata); |
||||
826 | } |
||||
827 | |||||
828 | // Check for OrderBy |
||||
829 | if (isset($propertyAnnotations[Annotation\OrderBy::class])) { |
||||
830 | $orderByAnnot = $propertyAnnotations[Annotation\OrderBy::class]; |
||||
831 | |||||
832 | $assocMetadata->setOrderBy($orderByAnnot->value); |
||||
833 | } |
||||
834 | |||||
835 | // Check for Id |
||||
836 | if (isset($propertyAnnotations[Annotation\Id::class])) { |
||||
837 | $assocMetadata->setPrimaryKey(true); |
||||
838 | } |
||||
839 | |||||
840 | // Check for Cache |
||||
841 | if (isset($propertyAnnotations[Annotation\Cache::class])) { |
||||
842 | $cacheAnnot = $propertyAnnotations[Annotation\Cache::class]; |
||||
843 | $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata, $fieldName); |
||||
844 | |||||
845 | $assocMetadata->setCache($cacheMetadata); |
||||
846 | } |
||||
847 | |||||
848 | return $assocMetadata; |
||||
849 | } |
||||
850 | |||||
851 | /** |
||||
852 | * Parse the given JoinTable as JoinTableMetadata |
||||
853 | * |
||||
854 | * @return Mapping\JoinTableMetadata |
||||
855 | */ |
||||
856 | private function convertJoinTableAnnotationToJoinTableMetadata( |
||||
857 | ReflectionProperty $reflectionProperty, |
||||
858 | Annotation\JoinTable $joinTableAnnot, |
||||
859 | Mapping\ClassMetadata $classMetadata |
||||
860 | ) { |
||||
861 | $joinTable = new Mapping\JoinTableMetadata(); |
||||
862 | |||||
863 | if (! empty($joinTableAnnot->name)) { |
||||
864 | $joinTable->setName($joinTableAnnot->name); |
||||
865 | } |
||||
866 | |||||
867 | if (! empty($joinTableAnnot->schema)) { |
||||
868 | $joinTable->setSchema($joinTableAnnot->schema); |
||||
869 | } |
||||
870 | |||||
871 | foreach ($joinTableAnnot->joinColumns as $joinColumnAnnot) { |
||||
872 | $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata( |
||||
873 | $reflectionProperty, |
||||
874 | $joinColumnAnnot, |
||||
875 | $classMetadata |
||||
876 | ); |
||||
877 | |||||
878 | $joinTable->addJoinColumn($joinColumn); |
||||
879 | } |
||||
880 | |||||
881 | foreach ($joinTableAnnot->inverseJoinColumns as $joinColumnAnnot) { |
||||
882 | $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata( |
||||
883 | $reflectionProperty, |
||||
884 | $joinColumnAnnot, |
||||
885 | $classMetadata |
||||
886 | ); |
||||
887 | |||||
888 | $joinTable->addInverseJoinColumn($joinColumn); |
||||
889 | } |
||||
890 | |||||
891 | return $joinTable; |
||||
892 | } |
||||
893 | |||||
894 | /** |
||||
895 | * Parse the given JoinColumn as JoinColumnMetadata |
||||
896 | * |
||||
897 | * @return Mapping\JoinColumnMetadata |
||||
898 | */ |
||||
899 | private function convertJoinColumnAnnotationToJoinColumnMetadata( |
||||
900 | ReflectionProperty $reflectionProperty, |
||||
901 | Annotation\JoinColumn $joinColumnAnnot, |
||||
902 | Mapping\ClassMetadata $classMetadata |
||||
903 | ) { |
||||
904 | $fieldName = $reflectionProperty->getName(); |
||||
905 | $joinColumn = new Mapping\JoinColumnMetadata(); |
||||
906 | $columnName = empty($joinColumnAnnot->name) |
||||
907 | ? $this->namingStrategy->propertyToColumnName($fieldName, $classMetadata->getClassName()) |
||||
908 | : $joinColumnAnnot->name; |
||||
909 | $referencedColumnName = empty($joinColumnAnnot->referencedColumnName) |
||||
910 | ? $this->namingStrategy->referenceColumnName() |
||||
911 | : $joinColumnAnnot->referencedColumnName; |
||||
912 | |||||
913 | $joinColumn->setColumnName($columnName); |
||||
914 | $joinColumn->setReferencedColumnName($referencedColumnName); |
||||
915 | $joinColumn->setNullable($joinColumnAnnot->nullable); |
||||
916 | $joinColumn->setUnique($joinColumnAnnot->unique); |
||||
917 | |||||
918 | if (! empty($joinColumnAnnot->fieldName)) { |
||||
919 | $joinColumn->setAliasedName($joinColumnAnnot->fieldName); |
||||
920 | } |
||||
921 | |||||
922 | if (! empty($joinColumnAnnot->columnDefinition)) { |
||||
923 | $joinColumn->setColumnDefinition($joinColumnAnnot->columnDefinition); |
||||
924 | } |
||||
925 | |||||
926 | if ($joinColumnAnnot->onDelete) { |
||||
927 | $joinColumn->setOnDelete(strtoupper($joinColumnAnnot->onDelete)); |
||||
928 | } |
||||
929 | |||||
930 | return $joinColumn; |
||||
931 | } |
||||
932 | |||||
933 | /** |
||||
934 | * Parses the given method. |
||||
935 | * |
||||
936 | * @return string[] |
||||
937 | */ |
||||
938 | private function getMethodCallbacks(ReflectionMethod $method) |
||||
939 | { |
||||
940 | $annotations = $this->getMethodAnnotations($method); |
||||
941 | $events = [ |
||||
942 | Events::prePersist => Annotation\PrePersist::class, |
||||
943 | Events::postPersist => Annotation\PostPersist::class, |
||||
944 | Events::preUpdate => Annotation\PreUpdate::class, |
||||
945 | Events::postUpdate => Annotation\PostUpdate::class, |
||||
946 | Events::preRemove => Annotation\PreRemove::class, |
||||
947 | Events::postRemove => Annotation\PostRemove::class, |
||||
948 | Events::postLoad => Annotation\PostLoad::class, |
||||
949 | Events::preFlush => Annotation\PreFlush::class, |
||||
950 | ]; |
||||
951 | |||||
952 | // Check for callbacks |
||||
953 | $callbacks = []; |
||||
954 | |||||
955 | foreach ($events as $eventName => $annotationClassName) { |
||||
956 | if (isset($annotations[$annotationClassName]) || $method->getName() === $eventName) { |
||||
957 | $callbacks[] = $eventName; |
||||
958 | } |
||||
959 | } |
||||
960 | |||||
961 | return $callbacks; |
||||
962 | } |
||||
963 | |||||
964 | /** |
||||
965 | * Attempts to resolve the fetch mode. |
||||
966 | * |
||||
967 | * @param string $className The class name. |
||||
968 | * @param string $fetchMode The fetch mode. |
||||
969 | * |
||||
970 | * @return int The fetch mode as defined in ClassMetadata. |
||||
971 | * |
||||
972 | * @throws Mapping\MappingException If the fetch mode is not valid. |
||||
973 | */ |
||||
974 | private function getFetchMode($className, $fetchMode) |
||||
975 | { |
||||
976 | $fetchModeConstant = sprintf('%s::%s', Mapping\FetchMode::class, $fetchMode); |
||||
977 | |||||
978 | if (! defined($fetchModeConstant)) { |
||||
979 | throw Mapping\MappingException::invalidFetchMode($className, $fetchMode); |
||||
980 | } |
||||
981 | |||||
982 | return constant($fetchModeConstant); |
||||
983 | } |
||||
984 | |||||
985 | /** |
||||
986 | * @param string $className The class name. |
||||
987 | * @param string $fieldName The field name. |
||||
988 | * @param string[] $originalCascades The original unprocessed field cascades. |
||||
989 | * |
||||
990 | * @return string[] The processed field cascades. |
||||
991 | * |
||||
992 | * @throws Mapping\MappingException If a cascade option is not valid. |
||||
993 | */ |
||||
994 | private function getCascade(string $className, string $fieldName, array $originalCascades) |
||||
995 | { |
||||
996 | $cascadeTypes = ['remove', 'persist', 'refresh']; |
||||
997 | $cascades = array_map('strtolower', $originalCascades); |
||||
998 | |||||
999 | if (in_array('all', $cascades, true)) { |
||||
1000 | $cascades = $cascadeTypes; |
||||
1001 | } |
||||
1002 | |||||
1003 | if (count($cascades) !== count(array_intersect($cascades, $cascadeTypes))) { |
||||
1004 | $diffCascades = array_diff($cascades, array_intersect($cascades, $cascadeTypes)); |
||||
1005 | |||||
1006 | throw Mapping\MappingException::invalidCascadeOption($diffCascades, $className, $fieldName); |
||||
1007 | } |
||||
1008 | |||||
1009 | return $cascades; |
||||
1010 | } |
||||
1011 | |||||
1012 | /** |
||||
1013 | * @return Annotation\Annotation[] |
||||
1014 | */ |
||||
1015 | private function getClassAnnotations(ReflectionClass $reflectionClass) |
||||
1016 | { |
||||
1017 | $classAnnotations = $this->reader->getClassAnnotations($reflectionClass); |
||||
1018 | |||||
1019 | foreach ($classAnnotations as $key => $annot) { |
||||
1020 | if (! is_numeric($key)) { |
||||
1021 | continue; |
||||
1022 | } |
||||
1023 | |||||
1024 | $classAnnotations[get_class($annot)] = $annot; |
||||
1025 | } |
||||
1026 | |||||
1027 | return $classAnnotations; |
||||
1028 | } |
||||
1029 | |||||
1030 | /** |
||||
1031 | * @return Annotation\Annotation[] |
||||
1032 | */ |
||||
1033 | private function getPropertyAnnotations(ReflectionProperty $reflectionProperty) |
||||
1034 | { |
||||
1035 | $propertyAnnotations = $this->reader->getPropertyAnnotations($reflectionProperty); |
||||
1036 | |||||
1037 | foreach ($propertyAnnotations as $key => $annot) { |
||||
1038 | if (! is_numeric($key)) { |
||||
1039 | continue; |
||||
1040 | } |
||||
1041 | |||||
1042 | $propertyAnnotations[get_class($annot)] = $annot; |
||||
1043 | } |
||||
1044 | |||||
1045 | return $propertyAnnotations; |
||||
1046 | } |
||||
1047 | |||||
1048 | /** |
||||
1049 | * @return Annotation\Annotation[] |
||||
1050 | */ |
||||
1051 | private function getMethodAnnotations(ReflectionMethod $reflectionMethod) |
||||
1052 | { |
||||
1053 | $methodAnnotations = $this->reader->getMethodAnnotations($reflectionMethod); |
||||
1054 | |||||
1055 | foreach ($methodAnnotations as $key => $annot) { |
||||
1056 | if (! is_numeric($key)) { |
||||
1057 | continue; |
||||
1058 | } |
||||
1059 | |||||
1060 | $methodAnnotations[get_class($annot)] = $annot; |
||||
1061 | } |
||||
1062 | |||||
1063 | return $methodAnnotations; |
||||
1064 | } |
||||
1065 | } |
||||
1066 |
In the issue above, the returned value is violating the contract defined by the mentioned interface.
Let's take a look at an example: