Failed Conditions
Push — master ( fa7802...d60694 )
by Guilherme
09:27
created

convertSequenceGeneratorElementToSequenceGeneratorAnnotation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 4
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 9
ccs 5
cts 5
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ORM\Mapping\Driver;
6
7
use Doctrine\Common\Collections\Criteria;
8
use Doctrine\DBAL\DBALException;
9
use Doctrine\ORM\Annotation;
10
use Doctrine\ORM\Events;
11
use Doctrine\ORM\Mapping;
12
use Doctrine\ORM\Mapping\Builder;
13
use InvalidArgumentException;
14
use SimpleXMLElement;
15
use function class_exists;
16
use function constant;
17
use function explode;
18
use function file_get_contents;
19
use function get_class;
20
use function in_array;
21
use function simplexml_load_string;
22
use function sprintf;
23
use function str_replace;
24
use function strtoupper;
25
use function var_export;
0 ignored issues
show
introduced by
Type var_export is not used in this file.
Loading history...
26
27
/**
28
 * XmlDriver is a metadata driver that enables mapping through XML files.
29
 */
30
class XmlDriver extends FileDriver
31
{
32
    public const DEFAULT_FILE_EXTENSION = '.dcm.xml';
33
34
    /**
35
     * {@inheritDoc}
36
     */
37 41
    public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION)
38
    {
39 41
        parent::__construct($locator, $fileExtension);
40 41
    }
41
42
    /**
43
     * {@inheritDoc}
44
     *
45
     * @throws DBALException
46
     */
47 36
    public function loadMetadataForClass(
48
        string $className,
49
        ?Mapping\ComponentMetadata $parent,
50
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
51
    ) : Mapping\ComponentMetadata {
52 36
        $metadata = new Mapping\ClassMetadata($className, $parent, $metadataBuildingContext);
53
54
        /** @var SimpleXMLElement $xmlRoot */
55 36
        $xmlRoot = $this->getElement($className);
56
57 34
        if ($xmlRoot->getName() === 'entity') {
58 34
            if (isset($xmlRoot['repository-class'])) {
59
                $metadata->setCustomRepositoryClassName((string) $xmlRoot['repository-class']);
60
            }
61
62 34
            if (isset($xmlRoot['read-only']) && $this->evaluateBoolean($xmlRoot['read-only'])) {
63 34
                $metadata->asReadOnly();
64
            }
65 5
        } elseif ($xmlRoot->getName() === 'mapped-superclass') {
66 5
            if (isset($xmlRoot['repository-class'])) {
67 1
                $metadata->setCustomRepositoryClassName((string) $xmlRoot['repository-class']);
68
            }
69
70 5
            $metadata->isMappedSuperclass = true;
71
        } elseif ($xmlRoot->getName() === 'embeddable') {
72
            $metadata->isEmbeddedClass = true;
73
        } else {
74
            throw Mapping\MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
75
        }
76
77
        // Process table information
78 34
        $parent = $metadata->getParent();
79
80 34
        if ($parent && $parent->inheritanceType === Mapping\InheritanceType::SINGLE_TABLE) {
81
            // Handle the case where a middle mapped super class inherits from a single table inheritance tree.
82
            do {
83 2
                if (! $parent->isMappedSuperclass) {
84 2
                    $metadata->setTable($parent->table);
85
86 2
                    break;
87
                }
88
89
                $parent = $parent->getParent();
90 2
            } while ($parent !== null);
91
        } else {
92 34
            $tableAnnotation = new Annotation\Table();
93
94
            // Evaluate <entity...> attributes
95 34
            if (isset($xmlRoot['table'])) {
96 12
                $tableAnnotation->name = (string) $xmlRoot['table'];
97
            }
98
99 34
            if (isset($xmlRoot['schema'])) {
100 2
                $tableAnnotation->schema = (string) $xmlRoot['schema'];
101
            }
102
103
            // Evaluate <indexes...>
104 34
            if (isset($xmlRoot->indexes)) {
105 4
                $tableAnnotation->indexes = $this->parseIndexes($xmlRoot->indexes->children());
106
            }
107
108
            // Evaluate <unique-constraints..>
109 34
            if (isset($xmlRoot->{'unique-constraints'})) {
110 3
                $tableAnnotation->uniqueConstraints = $this->parseUniqueConstraints($xmlRoot->{'unique-constraints'}->children());
111
            }
112
113 34
            if (isset($xmlRoot->options)) {
114 3
                $tableAnnotation->options = $this->parseOptions($xmlRoot->options->children());
115
            }
116
117 34
            $tableBuilder = new Builder\TableMetadataBuilder($metadataBuildingContext);
118
119
            $tableBuilder
120 34
                ->withEntityClassMetadata($metadata)
121 34
                ->withTableAnnotation($tableAnnotation);
122
123 34
            $metadata->setTable($tableBuilder->build());
124
        }
125
126
        // Evaluate second level cache
127 34
        if (isset($xmlRoot->cache)) {
128 2
            $cacheBuilder = new Builder\CacheMetadataBuilder($metadataBuildingContext);
129
130
            $cacheBuilder
131 2
                ->withComponentMetadata($metadata)
132 2
                ->withCacheAnnotation($this->convertCacheElementToCacheAnnotation($xmlRoot->cache));
133
134 2
            $metadata->setCache($cacheBuilder->build());
135
        }
136
137 34
        if (isset($xmlRoot['inheritance-type'])) {
138 10
            $inheritanceType = strtoupper((string) $xmlRoot['inheritance-type']);
139
140 10
            $metadata->setInheritanceType(
141 10
                constant(sprintf('%s::%s', Mapping\InheritanceType::class, $inheritanceType))
142
            );
143
144 10
            if ($metadata->inheritanceType !== Mapping\InheritanceType::NONE) {
145 10
                $discriminatorColumnBuilder = new Builder\DiscriminatorColumnMetadataBuilder($metadataBuildingContext);
146
147
                $discriminatorColumnBuilder
148 10
                    ->withComponentMetadata($metadata)
149 10
                    ->withDiscriminatorColumnAnnotation(
150 10
                        isset($xmlRoot->{'discriminator-column'})
151 8
                            ? $this->convertDiscriminiatorColumnElementToDiscriminatorColumnAnnotation($xmlRoot->{'discriminator-column'})
152 10
                            : null
153
                    );
154
155 10
                $metadata->setDiscriminatorColumn($discriminatorColumnBuilder->build());
156
157
                // Evaluate <discriminator-map...>
158 10
                if (isset($xmlRoot->{'discriminator-map'})) {
159 10
                    $map = [];
160
161 10
                    foreach ($xmlRoot->{'discriminator-map'}->{'discriminator-mapping'} as $discrMapElement) {
162 10
                        $map[(string) $discrMapElement['value']] = (string) $discrMapElement['class'];
163
                    }
164
165 10
                    $metadata->setDiscriminatorMap($map);
166
                }
167
            }
168
        }
169
170
        // Evaluate <change-tracking-policy...>
171 34
        if (isset($xmlRoot['change-tracking-policy'])) {
172
            $changeTrackingPolicy = strtoupper((string) $xmlRoot['change-tracking-policy']);
173
174
            $metadata->setChangeTrackingPolicy(
175
                constant(sprintf('%s::%s', Mapping\ChangeTrackingPolicy::class, $changeTrackingPolicy))
176
            );
177
        }
178
179
        // Evaluate <field ...> mappings
180 34
        if (isset($xmlRoot->field)) {
181 20
            $fieldBuilder = new Builder\FieldMetadataBuilder($metadataBuildingContext);
182
183
            $fieldBuilder
184 20
                ->withComponentMetadata($metadata);
185
186 20
            foreach ($xmlRoot->field as $fieldElement) {
187 20
                $versionAnnotation = isset($fieldElement['version']) && $this->evaluateBoolean($fieldElement['version'])
188 3
                    ? new Annotation\Version()
189 20
                    : null;
190
191
                $fieldBuilder
192 20
                    ->withFieldName((string) $fieldElement['name'])
193 20
                    ->withColumnAnnotation($this->convertFieldElementToColumnAnnotation($fieldElement))
194 20
                    ->withIdAnnotation(null)
195 20
                    ->withVersionAnnotation($versionAnnotation);
196
197 20
                $fieldMetadata = $fieldBuilder->build();
198
199
                // Prevent column duplication
200 20
                if ($metadata->checkPropertyDuplication($fieldMetadata->getColumnName())) {
0 ignored issues
show
Bug introduced by
It seems like $fieldMetadata->getColumnName() can also be of type null; however, parameter $columnName of Doctrine\ORM\Mapping\Cla...ckPropertyDuplication() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

200
                if ($metadata->checkPropertyDuplication(/** @scrutinizer ignore-type */ $fieldMetadata->getColumnName())) {
Loading history...
201
                    throw Mapping\MappingException::duplicateColumnName(
202
                        $metadata->getClassName(),
203
                        $fieldMetadata->getColumnName()
204
                    );
205
                }
206
207 20
                $metadata->addProperty($fieldMetadata);
208
            }
209
        }
210
211 34
        if (isset($xmlRoot->embedded)) {
212
            foreach ($xmlRoot->embedded as $embeddedMapping) {
213
                $columnPrefix = isset($embeddedMapping['column-prefix'])
214
                    ? (string) $embeddedMapping['column-prefix']
215
                    : null;
216
217
                $useColumnPrefix = isset($embeddedMapping['use-column-prefix'])
218
                    ? $this->evaluateBoolean($embeddedMapping['use-column-prefix'])
219
                    : true;
220
221
                $mapping = [
222
                    'fieldName'    => (string) $embeddedMapping['name'],
223
                    'class'        => (string) $embeddedMapping['class'],
224
                    'columnPrefix' => $useColumnPrefix ? $columnPrefix : false,
225
                ];
226
227
                $metadata->mapEmbedded($mapping);
228
            }
229
        }
230
231
        // Evaluate <id ...> mappings
232 34
        $associationIds = [];
233
234 34
        $fieldBuilder = new Builder\FieldMetadataBuilder($metadataBuildingContext);
235
236
        $fieldBuilder
237 34
            ->withComponentMetadata($metadata);
238
239 34
        foreach ($xmlRoot->id as $idElement) {
240 29
            $fieldName = (string) $idElement['name'];
241
242 29
            if (isset($idElement['association-key']) && $this->evaluateBoolean($idElement['association-key'])) {
243 2
                $associationIds[$fieldName] = true;
244
245 2
                continue;
246
            }
247
248 28
            $versionAnnotation = isset($idElement['version']) && $this->evaluateBoolean($idElement['version'])
249
                ? new Annotation\Version()
250 28
                : null;
251
252
            $fieldMetadata = $fieldBuilder
253 28
                ->withFieldName($fieldName)
254 28
                ->withColumnAnnotation($this->convertFieldElementToColumnAnnotation($idElement))
255 28
                ->withIdAnnotation(new Annotation\Id())
256 28
                ->withVersionAnnotation($versionAnnotation)
257 28
                ->withGeneratedValueAnnotation(
258 28
                    isset($idElement->generator)
259 27
                        ? $this->convertGeneratorElementToGeneratedValueAnnotation($idElement->generator)
260 28
                        : null
261
                )
262 28
                ->withSequenceGeneratorAnnotation(
263 28
                    isset($idElement->{'sequence-generator'})
264 3
                        ? $this->convertSequenceGeneratorElementToSequenceGeneratorAnnotation($idElement->{'sequence-generator'})
265 28
                        : null
266
                )
267 28
                ->withCustomIdGeneratorAnnotation(
268 28
                    isset($idElement->{'custom-id-generator'})
269 2
                        ? $this->convertCustomIdGeneratorElementToCustomIdGeneratorAnnotation($idElement->{'custom-id-generator'})
270 28
                        : null
271
                )
272 28
                ->build();
273
274
            // Prevent column duplication
275 28
            if ($metadata->checkPropertyDuplication($fieldMetadata->getColumnName())) {
276
                throw Mapping\MappingException::duplicateColumnName(
277
                    $metadata->getClassName(),
278
                    $fieldMetadata->getColumnName()
279
                );
280
            }
281
282 28
            $metadata->addProperty($fieldMetadata);
283
        }
284
285
        // Evaluate <one-to-one ...> mappings
286 34
        if (isset($xmlRoot->{'one-to-one'})) {
287 7
            foreach ($xmlRoot->{'one-to-one'} as $oneToOneElement) {
288 7
                $association  = new Mapping\OneToOneAssociationMetadata((string) $oneToOneElement['field']);
289 7
                $targetEntity = (string) $oneToOneElement['target-entity'];
290
291 7
                $association->setTargetEntity($targetEntity);
292
293 7
                if (isset($associationIds[$association->getName()])) {
294
                    $association->setPrimaryKey(true);
295
                }
296
297 7
                if (isset($oneToOneElement->cascade)) {
298 7
                    $association->setCascade($this->getCascadeMappings($oneToOneElement->cascade));
299
                }
300
301 7
                if (isset($oneToOneElement['orphan-removal'])) {
302
                    $association->setOrphanRemoval($this->evaluateBoolean($oneToOneElement['orphan-removal']));
303
                }
304
305 7
                if (isset($oneToOneElement['fetch'])) {
306 3
                    $association->setFetchMode(
307 3
                        constant(sprintf('%s::%s', Mapping\FetchMode::class, (string) $oneToOneElement['fetch']))
308
                    );
309
                }
310
311 7
                if (isset($oneToOneElement['mapped-by'])) {
312 3
                    $association->setMappedBy((string) $oneToOneElement['mapped-by']);
313 3
                    $association->setOwningSide(false);
314
                }
315
316 7
                if (isset($oneToOneElement['inversed-by'])) {
317 4
                    $association->setInversedBy((string) $oneToOneElement['inversed-by']);
318 4
                    $association->setOwningSide(true);
319
                }
320
321
                // Evaluate second level cache
322 7
                if (isset($oneToOneElement->cache)) {
323
                    $cacheBuilder = new Builder\CacheMetadataBuilder($metadataBuildingContext);
324
325
                    $cacheBuilder
326
                        ->withComponentMetadata($metadata)
327
                        ->withFieldName($association->getName())
328
                        ->withCacheAnnotation($this->convertCacheElementToCacheAnnotation($oneToOneElement->cache));
329
330
                    $association->setCache($cacheBuilder->build());
331
                }
332
333
                // Check for owning side to consider join column
334 7
                if (! $association->isOwningSide()) {
335 3
                    $metadata->addProperty($association);
336
337 3
                    continue;
338
                }
339
340
                // Check for JoinColumn/JoinColumns annotations
341 5
                $joinColumnBuilder = new Builder\JoinColumnMetadataBuilder($metadataBuildingContext);
342
343
                $joinColumnBuilder
344 5
                    ->withComponentMetadata($metadata)
345 5
                    ->withFieldName($association->getName());
346
347
                switch (true) {
348 5
                    case isset($oneToOneElement->{'join-column'}):
349 5
                        $joinColumnAnnotation = $this->convertJoinColumnElementToJoinColumnAnnotation($oneToOneElement->{'join-column'});
350
351 5
                        $joinColumnBuilder->withJoinColumnAnnotation($joinColumnAnnotation);
352
353 5
                        $association->addJoinColumn($joinColumnBuilder->build());
354 5
                        break;
355
356
                    case isset($oneToOneElement->{'join-columns'}):
357
                        foreach ($oneToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
358
                            $joinColumnAnnotation = $this->convertJoinColumnElementToJoinColumnAnnotation($joinColumnElement);
359
360
                            $joinColumnBuilder->withJoinColumnAnnotation($joinColumnAnnotation);
361
362
                            $association->addJoinColumn($joinColumnBuilder->build());
363
                        }
364
365
                        break;
366
367
                    default:
368
                        $association->addJoinColumn($joinColumnBuilder->build());
369
                        break;
370
                }
371
372 5
                $metadata->addProperty($association);
373
            }
374
        }
375
376
        // Evaluate <one-to-many ...> mappings
377 34
        if (isset($xmlRoot->{'one-to-many'})) {
378 9
            foreach ($xmlRoot->{'one-to-many'} as $oneToManyElement) {
379 9
                $association  = new Mapping\OneToManyAssociationMetadata((string) $oneToManyElement['field']);
380 9
                $targetEntity = (string) $oneToManyElement['target-entity'];
381
382 9
                $association->setTargetEntity($targetEntity);
383 9
                $association->setOwningSide(false);
384 9
                $association->setMappedBy((string) $oneToManyElement['mapped-by']);
385
386 9
                if (isset($associationIds[$association->getName()])) {
387
                    throw Mapping\MappingException::illegalToManyIdentifierAssociation($className, $association->getName());
388
                }
389
390 9
                if (isset($oneToManyElement['fetch'])) {
391
                    $association->setFetchMode(
392
                        constant(sprintf('%s::%s', Mapping\FetchMode::class, (string) $oneToManyElement['fetch']))
393
                    );
394
                }
395
396 9
                if (isset($oneToManyElement->cascade)) {
397 6
                    $association->setCascade($this->getCascadeMappings($oneToManyElement->cascade));
398
                }
399
400 9
                if (isset($oneToManyElement['orphan-removal'])) {
401 3
                    $association->setOrphanRemoval($this->evaluateBoolean($oneToManyElement['orphan-removal']));
402
                }
403
404 9
                if (isset($oneToManyElement->{'order-by'})) {
405 5
                    $orderBy = [];
406
407 5
                    foreach ($oneToManyElement->{'order-by'}->{'order-by-field'} as $orderByField) {
408 5
                        $orderBy[(string) $orderByField['name']] = isset($orderByField['direction'])
409 4
                            ? (string) $orderByField['direction']
410 1
                            : Criteria::ASC;
411
                    }
412
413 5
                    $association->setOrderBy($orderBy);
414
                }
415
416 9
                if (isset($oneToManyElement['index-by'])) {
417 3
                    $association->setIndexedBy((string) $oneToManyElement['index-by']);
418 6
                } elseif (isset($oneToManyElement->{'index-by'})) {
419
                    throw new InvalidArgumentException('<index-by /> is not a valid tag');
420
                }
421
422
                // Evaluate second level cache
423 9
                if (isset($oneToManyElement->cache)) {
424 1
                    $cacheBuilder = new Builder\CacheMetadataBuilder($metadataBuildingContext);
425
426
                    $cacheBuilder
427 1
                        ->withComponentMetadata($metadata)
428 1
                        ->withFieldName($association->getName())
429 1
                        ->withCacheAnnotation($this->convertCacheElementToCacheAnnotation($oneToManyElement->cache));
430
431 1
                    $association->setCache($cacheBuilder->build());
432
                }
433
434 9
                $metadata->addProperty($association);
435
            }
436
        }
437
438
        // Evaluate <many-to-one ...> mappings
439 34
        if (isset($xmlRoot->{'many-to-one'})) {
440 8
            foreach ($xmlRoot->{'many-to-one'} as $manyToOneElement) {
441 8
                $association  = new Mapping\ManyToOneAssociationMetadata((string) $manyToOneElement['field']);
442 8
                $targetEntity = (string) $manyToOneElement['target-entity'];
443
444 8
                $association->setTargetEntity($targetEntity);
445
446 8
                if (isset($associationIds[$association->getName()])) {
447 2
                    $association->setPrimaryKey(true);
448
                }
449
450 8
                if (isset($manyToOneElement['fetch'])) {
451
                    $association->setFetchMode(
452
                        constant('Doctrine\ORM\Mapping\FetchMode::' . (string) $manyToOneElement['fetch'])
453
                    );
454
                }
455
456 8
                if (isset($manyToOneElement->cascade)) {
457 4
                    $association->setCascade($this->getCascadeMappings($manyToOneElement->cascade));
458
                }
459
460 8
                if (isset($manyToOneElement['inversed-by'])) {
461 2
                    $association->setInversedBy((string) $manyToOneElement['inversed-by']);
462
                }
463
464
                // Evaluate second level cache
465 8
                if (isset($manyToOneElement->cache)) {
466 1
                    $cacheBuilder = new Builder\CacheMetadataBuilder($metadataBuildingContext);
467
468
                    $cacheBuilder
469 1
                        ->withComponentMetadata($metadata)
470 1
                        ->withFieldName($association->getName())
471 1
                        ->withCacheAnnotation($this->convertCacheElementToCacheAnnotation($manyToOneElement->cache));
472
473 1
                    $association->setCache($cacheBuilder->build());
474
                }
475
476
                // Check for JoinColumn/JoinColumns annotations
477 8
                $joinColumnBuilder = new Builder\JoinColumnMetadataBuilder($metadataBuildingContext);
478
479
                $joinColumnBuilder
480 8
                    ->withComponentMetadata($metadata)
481 8
                    ->withFieldName($association->getName());
482
483
                switch (true) {
484 8
                    case isset($oneToOneElement->{'join-column'}):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $oneToOneElement does not seem to be defined for all execution paths leading up to this point.
Loading history...
485
                        $joinColumnAnnotation = $this->convertJoinColumnElementToJoinColumnAnnotation($oneToOneElement->{'join-column'});
486
487
                        $joinColumnBuilder->withJoinColumnAnnotation($joinColumnAnnotation);
488
489
                        $association->addJoinColumn($joinColumnBuilder->build());
490
                        break;
491
492 8
                    case isset($oneToOneElement->{'join-columns'}):
493
                        foreach ($oneToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
494
                            $joinColumnAnnotation = $this->convertJoinColumnElementToJoinColumnAnnotation($joinColumnElement);
495
496
                            $joinColumnBuilder->withJoinColumnAnnotation($joinColumnAnnotation);
497
498
                            $association->addJoinColumn($joinColumnBuilder->build());
499
                        }
500
501
                        break;
502
503
                    default:
504 8
                        $association->addJoinColumn($joinColumnBuilder->build());
505 8
                        break;
506
                }
507
508 8
                $metadata->addProperty($association);
509
            }
510
        }
511
512
        // Evaluate <many-to-many ...> mappings
513 33
        if (isset($xmlRoot->{'many-to-many'})) {
514 14
            foreach ($xmlRoot->{'many-to-many'} as $manyToManyElement) {
515 14
                $association  = new Mapping\ManyToManyAssociationMetadata((string) $manyToManyElement['field']);
516 14
                $targetEntity = (string) $manyToManyElement['target-entity'];
517
518 14
                $association->setTargetEntity($targetEntity);
519
520 14
                if (isset($associationIds[$association->getName()])) {
521
                    throw Mapping\MappingException::illegalToManyIdentifierAssociation($className, $association->getName());
522
                }
523
524 14
                if (isset($manyToManyElement['fetch'])) {
525 4
                    $association->setFetchMode(
526 4
                        constant(sprintf('%s::%s', Mapping\FetchMode::class, (string) $manyToManyElement['fetch']))
527
                    );
528
                }
529
530 14
                if (isset($manyToManyElement->cascade)) {
531 8
                    $association->setCascade($this->getCascadeMappings($manyToManyElement->cascade));
532
                }
533
534 14
                if (isset($manyToManyElement['orphan-removal'])) {
535
                    $association->setOrphanRemoval($this->evaluateBoolean($manyToManyElement['orphan-removal']));
536
                }
537
538 14
                if (isset($manyToManyElement['mapped-by'])) {
539 5
                    $association->setMappedBy((string) $manyToManyElement['mapped-by']);
540 5
                    $association->setOwningSide(false);
541
                }
542
543 14
                if (isset($manyToManyElement['inversed-by'])) {
544 6
                    $association->setInversedBy((string) $manyToManyElement['inversed-by']);
545
                }
546
547 14
                if (isset($manyToManyElement['index-by'])) {
548
                    $association->setIndexedBy((string) $manyToManyElement['index-by']);
549 14
                } elseif (isset($manyToManyElement->{'index-by'})) {
550
                    throw new InvalidArgumentException('<index-by /> is not a valid tag');
551
                }
552
553 14
                if (isset($manyToManyElement->{'order-by'})) {
554 1
                    $orderBy = [];
555
556 1
                    foreach ($manyToManyElement->{'order-by'}->{'order-by-field'} as $orderByField) {
557 1
                        $orderBy[(string) $orderByField['name']] = isset($orderByField['direction'])
558
                            ? (string) $orderByField['direction']
559 1
                            : Criteria::ASC;
560
                    }
561
562 1
                    $association->setOrderBy($orderBy);
563
                }
564
565
                // Check for cache
566 14
                if (isset($manyToManyElement->cache)) {
567
                    $cacheBuilder = new Builder\CacheMetadataBuilder($metadataBuildingContext);
568
569
                    $cacheBuilder
570
                        ->withComponentMetadata($metadata)
571
                        ->withFieldName($association->getName())
572
                        ->withCacheAnnotation($this->convertCacheElementToCacheAnnotation($manyToManyElement->cache));
573
574
                    $association->setCache($cacheBuilder->build());
575
                }
576
577
                // Check for owning side to consider join column
578 14
                if (! $association->isOwningSide()) {
579 5
                    $metadata->addProperty($association);
580
581 5
                    continue;
582
                }
583
584 10
                $joinTableBuilder = new Builder\JoinTableMetadataBuilder($metadataBuildingContext);
585
586
                $joinTableBuilder
587 10
                    ->withComponentMetadata($metadata)
588 10
                    ->withFieldName($association->getName())
589 10
                    ->withTargetEntity($targetEntity);
590
591 10
                if (isset($manyToManyElement->{'join-table'})) {
592 8
                    $joinTableElement    = $manyToManyElement->{'join-table'};
593 8
                    $joinTableAnnotation = $this->convertJoinTableElementToJoinTableAnnotation($joinTableElement);
594
595 8
                    $joinTableBuilder->withJoinTableAnnotation($joinTableAnnotation);
596
                }
597
598 10
                $association->setJoinTable($joinTableBuilder->build());
599
600 10
                $metadata->addProperty($association);
601
            }
602
        }
603
604
        // Evaluate association-overrides
605 33
        if (isset($xmlRoot->{'attribute-overrides'})) {
606 2
            $fieldBuilder = new Builder\FieldMetadataBuilder($metadataBuildingContext);
607
608
            $fieldBuilder
609 2
                ->withComponentMetadata($metadata);
610
611 2
            foreach ($xmlRoot->{'attribute-overrides'}->{'attribute-override'} as $overrideElement) {
612 2
                $fieldName = (string) $overrideElement['name'];
613
614 2
                foreach ($overrideElement->field as $fieldElement) {
615 2
                    $versionAnnotation = isset($fieldElement['version']) && $this->evaluateBoolean($fieldElement['version'])
616
                        ? new Annotation\Version()
617 2
                        : null;
618
619
                    $fieldBuilder
620 2
                        ->withFieldName($fieldName)
621 2
                        ->withColumnAnnotation($this->convertFieldElementToColumnAnnotation($fieldElement))
622 2
                        ->withIdAnnotation(null)
623 2
                        ->withVersionAnnotation($versionAnnotation);
624
625 2
                    $fieldMetadata = $fieldBuilder->build();
626
627
                    // Prevent column duplication
628 2
                    if ($metadata->checkPropertyDuplication($fieldMetadata->getColumnName())) {
629
                        throw Mapping\MappingException::duplicateColumnName(
630
                            $metadata->getClassName(),
631
                            $fieldMetadata->getColumnName()
632
                        );
633
                    }
634
635 2
                    $metadata->setPropertyOverride($fieldMetadata);
636
                }
637
            }
638
        }
639
640
        // Evaluate association-overrides
641 33
        if (isset($xmlRoot->{'association-overrides'})) {
642 4
            foreach ($xmlRoot->{'association-overrides'}->{'association-override'} as $overrideElement) {
643 4
                $fieldName = (string) $overrideElement['name'];
644 4
                $property  = $metadata->getProperty($fieldName);
645
646 4
                if (! $property) {
647
                    throw Mapping\MappingException::invalidOverrideFieldName($metadata->getClassName(), $fieldName);
648
                }
649
650 4
                $existingClass = get_class($property);
651 4
                $override      = new $existingClass($fieldName);
652
653 4
                $override->setTargetEntity($property->getTargetEntity());
0 ignored issues
show
Bug introduced by
The method getTargetEntity() does not exist on Doctrine\ORM\Mapping\Property. It seems like you code against a sub-type of Doctrine\ORM\Mapping\Property such as Doctrine\ORM\Mapping\AssociationMetadata. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

653
                $override->setTargetEntity($property->/** @scrutinizer ignore-call */ getTargetEntity());
Loading history...
654
655
                // Check for join-columns
656 4
                if (isset($overrideElement->{'join-columns'})) {
657 2
                    $joinColumnBuilder = new Builder\JoinColumnMetadataBuilder($metadataBuildingContext);
658
659
                    $joinColumnBuilder
660 2
                        ->withComponentMetadata($metadata)
661 2
                        ->withFieldName($override->getName());
662
663 2
                    foreach ($overrideElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
664 2
                        $joinColumnAnnotation = $this->convertJoinColumnElementToJoinColumnAnnotation($joinColumnElement);
665
666 2
                        $joinColumnBuilder->withJoinColumnAnnotation($joinColumnAnnotation);
667
668 2
                        $override->addJoinColumn($joinColumnBuilder->build());
669
                    }
670
                }
671
672
                // Check for join-table
673 4
                if ($overrideElement->{'join-table'}) {
674 2
                    $joinTableElement    = $overrideElement->{'join-table'};
675 2
                    $joinTableAnnotation = $this->convertJoinTableElementToJoinTableAnnotation($joinTableElement);
676 2
                    $joinTableBuilder    = new Builder\JoinTableMetadataBuilder($metadataBuildingContext);
677
678
                    $joinTableBuilder
679 2
                        ->withComponentMetadata($metadata)
680 2
                        ->withFieldName($property->getName())
681 2
                        ->withTargetEntity($property->getTargetEntity())
682 2
                        ->withJoinTableAnnotation($joinTableAnnotation);
683
684 2
                    $override->setJoinTable($joinTableBuilder->build());
685
                }
686
687
                // Check for inversed-by
688 4
                if (isset($overrideElement->{'inversed-by'})) {
689 1
                    $override->setInversedBy((string) $overrideElement->{'inversed-by'}['name']);
690
                }
691
692
                // Check for fetch
693 4
                if (isset($overrideElement['fetch'])) {
694 1
                    $override->setFetchMode(
695 1
                        constant('Doctrine\ORM\Mapping\FetchMode::' . (string) $overrideElement['fetch'])
696
                    );
697
                }
698
699 4
                $metadata->setPropertyOverride($override);
700
            }
701
        }
702
703
        // Evaluate <lifecycle-callbacks...>
704 33
        if (isset($xmlRoot->{'lifecycle-callbacks'})) {
705 3
            foreach ($xmlRoot->{'lifecycle-callbacks'}->{'lifecycle-callback'} as $lifecycleCallback) {
706 3
                $eventName  = constant(Events::class . '::' . (string) $lifecycleCallback['type']);
707 3
                $methodName = (string) $lifecycleCallback['method'];
708
709 3
                $metadata->addLifecycleCallback($eventName, $methodName);
710
            }
711
        }
712
713
        // Evaluate entity listener
714 33
        if (isset($xmlRoot->{'entity-listeners'})) {
715 2
            foreach ($xmlRoot->{'entity-listeners'}->{'entity-listener'} as $listenerElement) {
716 2
                $listenerClassName = (string) $listenerElement['class'];
717
718 2
                if (! class_exists($listenerClassName)) {
719
                    throw Mapping\MappingException::entityListenerClassNotFound(
720
                        $listenerClassName,
721
                        $metadata->getClassName()
722
                    );
723
                }
724
725 2
                foreach ($listenerElement as $callbackElement) {
726 2
                    $eventName  = (string) $callbackElement['type'];
727 2
                    $methodName = (string) $callbackElement['method'];
728
729 2
                    $metadata->addEntityListener($eventName, $listenerClassName, $methodName);
730
                }
731
            }
732
        }
733
734 33
        return $metadata;
735
    }
736
737
    /**
738
     * Parses (nested) index elements.
739
     *
740
     * @param SimpleXMLElement $indexes The XML element.
741
     *
742
     * @return Annotation\Index[] The indexes array.
743
     */
744 4
    private function parseIndexes(SimpleXMLElement $indexes) : array
745
    {
746 4
        $array = [];
747
748
        /** @var SimpleXMLElement $index */
749 4
        foreach ($indexes as $index) {
750 4
            $indexAnnotation = new Annotation\Index();
751
752 4
            $indexAnnotation->columns = explode(',', (string) $index['columns']);
753 4
            $indexAnnotation->options = isset($index->options) ? $this->parseOptions($index->options->children()) : [];
754 4
            $indexAnnotation->flags   = isset($index['flags']) ? explode(',', (string) $index['flags']) : [];
755
756 4
            if (isset($index['name'])) {
757 3
                $indexAnnotation->name = (string) $index['name'];
758
            }
759
760 4
            if (isset($index['unique'])) {
761
                $indexAnnotation->unique = $this->evaluateBoolean($index['unique']);
762
            }
763
764 4
            $array[] = $indexAnnotation;
765
        }
766
767 4
        return $array;
768
    }
769
770
    /**
771
     * Parses (nested) unique constraint elements.
772
     *
773
     * @param SimpleXMLElement $uniqueConstraints The XML element.
774
     *
775
     * @return Annotation\UniqueConstraint[] The unique constraints array.
776
     */
777 3
    private function parseUniqueConstraints(SimpleXMLElement $uniqueConstraints) : array
778
    {
779 3
        $array = [];
780
781
        /** @var SimpleXMLElement $uniqueConstraint */
782 3
        foreach ($uniqueConstraints as $uniqueConstraint) {
783 3
            $uniqueConstraintAnnotation = new Annotation\UniqueConstraint();
784
785 3
            $uniqueConstraintAnnotation->columns = explode(',', (string) $uniqueConstraint['columns']);
786 3
            $uniqueConstraintAnnotation->options = isset($uniqueConstraint->options) ? $this->parseOptions($uniqueConstraint->options->children()) : [];
787 3
            $uniqueConstraintAnnotation->flags   = isset($uniqueConstraint['flags']) ? explode(',', (string) $uniqueConstraint['flags']) : [];
788
789 3
            if (isset($uniqueConstraint['name'])) {
790 3
                $uniqueConstraintAnnotation->name = (string) $uniqueConstraint['name'];
791
            }
792
793 3
            $array[] = $uniqueConstraintAnnotation;
794
        }
795
796 3
        return $array;
797
    }
798
799
    /**
800
     * Parses (nested) option elements.
801
     *
802
     * @param SimpleXMLElement $options The XML element.
803
     *
804
     * @return mixed[] The options array.
805
     */
806 4
    private function parseOptions(SimpleXMLElement $options) : array
807
    {
808 4
        $array = [];
809
810
        /** @var SimpleXMLElement $option */
811 4
        foreach ($options as $option) {
812 4
            if ($option->count()) {
813 3
                $value = $this->parseOptions($option->children());
814
            } else {
815 4
                $value = (string) $option;
816
            }
817
818 4
            $attributes = $option->attributes();
819
820 4
            if (isset($attributes->name)) {
821 4
                $nameAttribute = (string) $attributes->name;
822
823 4
                $array[$nameAttribute] = in_array($nameAttribute, ['unsigned', 'fixed'], true)
824 3
                    ? $this->evaluateBoolean($value)
825 4
                    : $value;
826
            } else {
827
                $array[] = $value;
828
            }
829
        }
830
831 4
        return $array;
832
    }
833
834 30
    private function convertFieldElementToColumnAnnotation(
835
        SimpleXMLElement $fieldElement
836
    ) : Annotation\Column {
837 30
        $columnAnnotation = new Annotation\Column();
838
839 30
        $columnAnnotation->type = isset($fieldElement['type']) ? (string) $fieldElement['type'] : 'string';
840
841 30
        if (isset($fieldElement['column'])) {
842 20
            $columnAnnotation->name = (string) $fieldElement['column'];
843
        }
844
845 30
        if (isset($fieldElement['length'])) {
846 6
            $columnAnnotation->length = (int) $fieldElement['length'];
847
        }
848
849 30
        if (isset($fieldElement['precision'])) {
850 1
            $columnAnnotation->precision = (int) $fieldElement['precision'];
851
        }
852
853 30
        if (isset($fieldElement['scale'])) {
854 1
            $columnAnnotation->scale = (int) $fieldElement['scale'];
855
        }
856
857 30
        if (isset($fieldElement['unique'])) {
858 7
            $columnAnnotation->unique = $this->evaluateBoolean($fieldElement['unique']);
859
        }
860
861 30
        if (isset($fieldElement['nullable'])) {
862 7
            $columnAnnotation->nullable = $this->evaluateBoolean($fieldElement['nullable']);
863
        }
864
865 30
        if (isset($fieldElement['column-definition'])) {
866 4
            $columnAnnotation->columnDefinition = (string) $fieldElement['column-definition'];
867
        }
868
869 30
        if (isset($fieldElement->options)) {
870 3
            $columnAnnotation->options = $this->parseOptions($fieldElement->options->children());
871
        }
872
873 30
        return $columnAnnotation;
874
    }
875
876 27
    private function convertGeneratorElementToGeneratedValueAnnotation(
877
        SimpleXMLElement $generatorElement
878
    ) : Annotation\GeneratedValue {
879 27
        $generatedValueAnnotation = new Annotation\GeneratedValue();
880
881 27
        $generatedValueAnnotation->strategy = (string) ($generatorElement['strategy'] ?? 'AUTO');
882
883 27
        return $generatedValueAnnotation;
884
    }
885
886 3
    private function convertSequenceGeneratorElementToSequenceGeneratorAnnotation(
887
        SimpleXMLElement $sequenceGeneratorElement
888
    ) : Annotation\SequenceGenerator {
889 3
        $sequenceGeneratorAnnotation = new Annotation\SequenceGenerator();
890
891 3
        $sequenceGeneratorAnnotation->sequenceName   = (string) ($sequenceGeneratorElement['sequence-name'] ?? null);
892 3
        $sequenceGeneratorAnnotation->allocationSize = (int) ($sequenceGeneratorElement['allocation-size'] ?? 1);
893
894 3
        return $sequenceGeneratorAnnotation;
895
    }
896
897 2
    private function convertCustomIdGeneratorElementToCustomIdGeneratorAnnotation(
898
        SimpleXMLElement $customIdGeneratorElement
899
    ) : Annotation\CustomIdGenerator {
900 2
        $customIdGeneratorAnnotation = new Annotation\CustomIdGenerator();
901
902 2
        $customIdGeneratorAnnotation->class     = (string) $customIdGeneratorElement['class'];
903 2
        $customIdGeneratorAnnotation->arguments = [];
904
905 2
        return $customIdGeneratorAnnotation;
906
    }
907
908
    /**
909
     * Constructs a JoinTable annotation based on the information
910
     * found in the given SimpleXMLElement.
911
     *
912
     * @param SimpleXMLElement $joinTableElement The XML element.
913
     */
914 8
    private function convertJoinTableElementToJoinTableAnnotation(
915
        SimpleXMLElement $joinTableElement
916
    ) : Annotation\JoinTable {
917 8
        $joinTableAnnotation = new Annotation\JoinTable();
918
919 8
        if (isset($joinTableElement['name'])) {
920 8
            $joinTableAnnotation->name = (string) $joinTableElement['name'];
921
        }
922
923 8
        if (isset($joinTableElement['schema'])) {
924
            $joinTableAnnotation->schema = (string) $joinTableElement['schema'];
925
        }
926
927 8
        if (isset($joinTableElement->{'join-columns'})) {
928 8
            $joinColumns = [];
929
930 8
            foreach ($joinTableElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
931 8
                $joinColumns[] = $this->convertJoinColumnElementToJoinColumnAnnotation($joinColumnElement);
932
            }
933
934 8
            $joinTableAnnotation->joinColumns = $joinColumns;
935
        }
936
937 8
        if (isset($joinTableElement->{'inverse-join-columns'})) {
938 8
            $joinColumns = [];
939
940 8
            foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} as $joinColumnElement) {
941 8
                $joinColumns[] = $this->convertJoinColumnElementToJoinColumnAnnotation($joinColumnElement);
942
            }
943
944 8
            $joinTableAnnotation->inverseJoinColumns = $joinColumns;
945
        }
946
947 8
        return $joinTableAnnotation;
948
    }
949
950
    /**
951
     * Constructs a JoinColumn annotation based on the information
952
     * found in the given SimpleXMLElement.
953
     *
954
     * @param SimpleXMLElement $joinColumnElement The XML element.
955
     */
956 9
    private function convertJoinColumnElementToJoinColumnAnnotation(
957
        SimpleXMLElement $joinColumnElement
958
    ) : Annotation\JoinColumn {
959 9
        $joinColumnAnnotation = new Annotation\JoinColumn();
960
961 9
        $joinColumnAnnotation->name                 = (string) $joinColumnElement['name'];
962 9
        $joinColumnAnnotation->referencedColumnName = (string) $joinColumnElement['referenced-column-name'];
963
964 9
        if (isset($joinColumnElement['column-definition'])) {
965 3
            $joinColumnAnnotation->columnDefinition = (string) $joinColumnElement['column-definition'];
966
        }
967
968 9
        if (isset($joinColumnElement['field-name'])) {
969
            $joinColumnAnnotation->fieldName = (string) $joinColumnElement['field-name'];
970
        }
971
972 9
        if (isset($joinColumnElement['nullable'])) {
973 3
            $joinColumnAnnotation->nullable = $this->evaluateBoolean($joinColumnElement['nullable']);
974
        }
975
976 9
        if (isset($joinColumnElement['unique'])) {
977 3
            $joinColumnAnnotation->unique = $this->evaluateBoolean($joinColumnElement['unique']);
978
        }
979
980 9
        if (isset($joinColumnElement['on-delete'])) {
981 3
            $joinColumnAnnotation->onDelete = strtoupper((string) $joinColumnElement['on-delete']);
982
        }
983
984 9
        return $joinColumnAnnotation;
985
    }
986
987
    /**
988
     * Parse the given Cache as CacheMetadata
989
     */
990 2
    private function convertCacheElementToCacheAnnotation(SimpleXMLElement $cacheMapping) : Annotation\Cache
991
    {
992 2
        $cacheAnnotation = new Annotation\Cache();
993
994 2
        if (isset($cacheMapping['region'])) {
995
            $cacheAnnotation->region = (string) $cacheMapping['region'];
996
        }
997
998 2
        if (isset($cacheMapping['usage'])) {
999 2
            $cacheAnnotation->usage = strtoupper((string) $cacheMapping['usage']);
1000
        }
1001
1002 2
        return $cacheAnnotation;
1003
    }
1004
1005 8
    private function convertDiscriminiatorColumnElementToDiscriminatorColumnAnnotation(
1006
        SimpleXMLElement $discriminatorColumnMapping
1007
    ) : Annotation\DiscriminatorColumn {
1008 8
        $discriminatorColumnAnnotation = new Annotation\DiscriminatorColumn();
1009
1010 8
        $discriminatorColumnAnnotation->type = (string) ($discriminatorColumnMapping['type'] ?? 'string');
1011 8
        $discriminatorColumnAnnotation->name = (string) $discriminatorColumnMapping['name'];
1012
1013 8
        if (isset($discriminatorColumnMapping['column-definition'])) {
1014 1
            $discriminatorColumnAnnotation->columnDefinition = (string) $discriminatorColumnMapping['column-definition'];
1015
        }
1016
1017 8
        if (isset($discriminatorColumnMapping['length'])) {
1018 3
            $discriminatorColumnAnnotation->length = (int) $discriminatorColumnMapping['length'];
1019
        }
1020
1021 8
        return $discriminatorColumnAnnotation;
1022
    }
1023
1024
    /**
1025
     * Gathers a list of cascade options found in the given cascade element.
1026
     *
1027
     * @param SimpleXMLElement $cascadeElement The cascade element.
1028
     *
1029
     * @return string[] The list of cascade options.
1030
     */
1031 10
    private function getCascadeMappings(SimpleXMLElement $cascadeElement) : array
1032
    {
1033 10
        $cascades = [];
1034
1035
        /** @var SimpleXMLElement $action */
1036 10
        foreach ($cascadeElement->children() as $action) {
1037
            // According to the JPA specifications, XML uses "cascade-persist"
1038
            // instead of "persist". Here, both variations are supported
1039
            // because Annotation use "persist" and we want to make sure that
1040
            // this driver doesn't need to know anything about the supported
1041
            // cascading actions
1042 10
            $cascades[] = str_replace('cascade-', '', $action->getName());
1043
        }
1044
1045 10
        return $cascades;
1046
    }
1047
1048
    /**
1049
     * {@inheritDoc}
1050
     */
1051 36
    protected function loadMappingFile($file)
1052
    {
1053 36
        $result = [];
1054
        // Note: we do not use `simplexml_load_file()` because of https://bugs.php.net/bug.php?id=62577
1055 36
        $xmlElement = simplexml_load_string(file_get_contents($file));
1056
1057 36
        if (isset($xmlElement->entity)) {
1058 35
            foreach ($xmlElement->entity as $entityElement) {
1059 35
                $entityName          = (string) $entityElement['name'];
1060 35
                $result[$entityName] = $entityElement;
1061
            }
1062 6
        } elseif (isset($xmlElement->{'mapped-superclass'})) {
1063 5
            foreach ($xmlElement->{'mapped-superclass'} as $mappedSuperClass) {
1064 5
                $className          = (string) $mappedSuperClass['name'];
1065 5
                $result[$className] = $mappedSuperClass;
1066
            }
1067 1
        } elseif (isset($xmlElement->embeddable)) {
1068
            foreach ($xmlElement->embeddable as $embeddableElement) {
1069
                $embeddableName          = (string) $embeddableElement['name'];
1070
                $result[$embeddableName] = $embeddableElement;
1071
            }
1072
        }
1073
1074 36
        return $result;
1075
    }
1076
1077
    /**
1078
     * @param mixed $element
1079
     *
1080
     * @return bool
1081
     */
1082 9
    protected function evaluateBoolean($element)
1083
    {
1084 9
        $flag = (string) $element;
1085
1086 9
        return $flag === 'true' || $flag === '1';
1087
    }
1088
}
1089