Failed Conditions
Pull Request — develop (#6873)
by
unknown
112:44 queued 47:41
created

XmlDriver   F

Complexity

Total Complexity 187

Size/Duplication

Total Lines 970
Duplicated Lines 9.69 %

Coupling/Cohesion

Components 1
Dependencies 18

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 187
lcom 1
cbo 18
dl 94
loc 970
ccs 368
cts 368
cp 1
rs 1.0434
c 0
b 0
f 0

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like XmlDriver often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use XmlDriver, and based on these observations, apply Extract Interface, too.

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 30 and the first side effect is on line 23.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ORM\Mapping\Driver;
6
7
use Doctrine\DBAL\Types\Type;
8
use Doctrine\ORM\Events;
9
use Doctrine\ORM\Mapping;
10
use SimpleXMLElement;
11
12
/**
13
 * XmlDriver is a metadata driver that enables mapping through XML files.
14
 *
15
 * @license 	http://www.opensource.org/licenses/mit-license.php MIT
16
 * @link    	www.doctrine-project.org
17
 * @since   	2.0
18
 * @author		Benjamin Eberlei <[email protected]>
19
 * @author		Guilherme Blanco <[email protected]>
20
 * @author      Jonathan H. Wage <[email protected]>
21
 * @author      Roman Borschel <[email protected]>
22
 */
23
class XmlDriver extends FileDriver
0 ignored issues
show
Bug introduced by
Possible parse error: class missing opening or closing brace
Loading history...
24
{
25
    const DEFAULT_FILE_EXTENSION = '.dcm.xml';
26
27
    /**
28
     * {@inheritDoc}
29
     */
30
    public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION)
31
    {
32
        parent::__construct($locator, $fileExtension);
33
    }
34
35
    /**
36
     * {@inheritDoc}
37
     */
38
    public function loadMetadataForClass(
39
        string $className,
40
        Mapping\ClassMetadata $metadata,
41
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
42
    )
43
    {
44
        /* @var \SimpleXMLElement $xmlRoot */
45
        $xmlRoot = $this->getElement($className);
46
47
        if ($xmlRoot->getName() === 'entity') {
48 41
            if (isset($xmlRoot['repository-class'])) {
49
                $metadata->setCustomRepositoryClassName(
50 41
                    $metadata->fullyQualifiedClassName((string) $xmlRoot['repository-class'])
51 41
                );
52
            }
53
54
            if (isset($xmlRoot['read-only']) && $this->evaluateBoolean($xmlRoot['read-only'])) {
55
                $metadata->asReadOnly();
56 36
            }
57
        } else if ($xmlRoot->getName() === 'mapped-superclass') {
58
            if (isset($xmlRoot['repository-class'])) {
59
                $metadata->setCustomRepositoryClassName(
60 36
                    $metadata->fullyQualifiedClassName((string) $xmlRoot['repository-class'])
61
                );
62 34
            }
63 33
64
            $metadata->isMappedSuperclass = true;
65
        } else if ($xmlRoot->getName() === 'embeddable') {
66 33
            $metadata->isEmbeddedClass = true;
67 33
        } else {
68
            throw Mapping\MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
69 6
        }
70 5
71 5
        // Process table information
72
        $parent = $metadata->getParent();
73 5
74 1
        if ($parent && $parent->inheritanceType === Mapping\InheritanceType::SINGLE_TABLE) {
75 1
            $metadata->setTable($parent->table);
76
        } else {
77
            $namingStrategy = $metadataBuildingContext->getNamingStrategy();
78
            $tableMetadata  = new Mapping\TableMetadata();
79
80
            $tableMetadata->setName($namingStrategy->classToTableName($metadata->getClassName()));
81 34
82
            // Evaluate <entity...> attributes
83 34
            if (isset($xmlRoot['table'])) {
84 14
                $tableMetadata->setName((string) $xmlRoot['table']);
85
            }
86
87 34
            if (isset($xmlRoot['schema'])) {
88 1
                $tableMetadata->setSchema((string) $xmlRoot['schema']);
89
            }
90
91 34
            if (isset($xmlRoot->options)) {
92
                $options = $this->parseOptions($xmlRoot->options->children());
93
94 34
                foreach ($options as $optionName => $optionValue) {
95 2
                    $tableMetadata->addOption($optionName, $optionValue);
96
                }
97
            }
98
99 34
            // Evaluate <indexes...>
100 3
            if (isset($xmlRoot->indexes)) {
101 3
                foreach ($xmlRoot->indexes->index as $indexXml) {
102 3
                    $indexName = isset($indexXml['name']) ? (string) $indexXml['name'] : null;
103 3
                    $columns   = explode(',', (string) $indexXml['columns']);
104
                    $isUnique  = isset($indexXml['unique']) && $indexXml['unique'];
105
                    $options   = isset($indexXml->options) ? $this->parseOptions($indexXml->options->children()) : [];
106
                    $flags     = isset($indexXml['flags']) ? explode(',', (string) $indexXml['flags']) : [];
107
108
                    $tableMetadata->addIndex([
109 34
                        'name'    => $indexName,
110 3
                        'columns' => $columns,
111 3
                        'unique'  => $isUnique,
112 3
                        'options' => $options,
113 3
                        'flags'   => $flags,
114 3
                    ]);
115 3
                }
116
            }
117
118
            // Evaluate <unique-constraints..>
119
120
            if (isset($xmlRoot->{'unique-constraints'})) {
121 34
                foreach ($xmlRoot->{'unique-constraints'}->{'unique-constraint'} as $uniqueXml) {
122 3
                    $indexName = isset($uniqueXml['name']) ? (string) $uniqueXml['name'] : null;
123 3
                    $columns   = explode(',', (string) $uniqueXml['columns']);
124 3
                    $options   = isset($uniqueXml->options) ? $this->parseOptions($uniqueXml->options->children()) : [];
125
                    $flags     = isset($uniqueXml['flags']) ? explode(',', (string) $uniqueXml['flags']) : [];
126 3
127
                    $tableMetadata->addUniqueConstraint([
128 3
                        'name'    => $indexName,
129
                        'columns' => $columns,
130 3
                        'options' => $options,
131 3
                        'flags'   => $flags,
132 3
                    ]);
133
                }
134
            }
135 3
136 3
            $metadata->setTable($tableMetadata);
137 3
        }
138 3
139
        // Evaluate second level cache
140
        if (isset($xmlRoot->cache)) {
141
            $cache = $this->convertCacheElementToCacheMetadata($xmlRoot->cache, $metadata);
142 3
143
            $metadata->setCache($cache);
144
        }
145
146 3
        // Evaluate named queries
147 3
        if (isset($xmlRoot->{'named-queries'})) {
148 3
            foreach ($xmlRoot->{'named-queries'}->{'named-query'} as $namedQueryElement) {
149
                $metadata->addNamedQuery((string) $namedQueryElement['name'], (string) $namedQueryElement['query']);
150
            }
151
        }
152
153 3
        // Evaluate native named queries
154 3
        if (isset($xmlRoot->{'named-native-queries'})) {
155 3
            foreach ($xmlRoot->{'named-native-queries'}->{'named-native-query'} as $nativeQueryElement) {
156 3
                $metadata->addNamedNativeQuery(
157
                    isset($nativeQueryElement['name']) ? (string) $nativeQueryElement['name'] : null,
158
                    isset($nativeQueryElement->query) ? (string) $nativeQueryElement->query : null,
159
                    [
160
                        'resultClass'      => isset($nativeQueryElement['result-class']) ? (string) $nativeQueryElement['result-class'] : null,
161 34
                        'resultSetMapping' => isset($nativeQueryElement['result-set-mapping']) ? (string) $nativeQueryElement['result-set-mapping'] : null,
162 10
                    ]
163 10
                );
164
            }
165 10
        }
166 10
167
        // Evaluate sql result set mapping
168 10
        if (isset($xmlRoot->{'sql-result-set-mappings'})) {
169
            foreach ($xmlRoot->{'sql-result-set-mappings'}->{'sql-result-set-mapping'} as $rsmElement) {
170
                $entities   = [];
171 10
                $columns    = [];
172 7
173
                foreach ($rsmElement as $entityElement) {
174 7
                    //<entity-result/>
175 7
                    if (isset($entityElement['entity-class'])) {
176 7
                        $entityResult = [
177 7
                            'fields'                => [],
178
                            'entityClass'           => (string) $entityElement['entity-class'],
179 6
                            'discriminatorColumn'   => isset($entityElement['discriminator-column']) ? (string) $entityElement['discriminator-column'] : null,
180 6
                        ];
181 6
182
                        foreach ($entityElement as $fieldElement) {
183
                            $entityResult['fields'][] = [
184 10
                                'name'      => isset($fieldElement['name']) ? (string) $fieldElement['name'] : null,
185
                                'column'    => isset($fieldElement['column']) ? (string) $fieldElement['column'] : null,
186
                            ];
187 10
                        }
188 10
189
                        $entities[] = $entityResult;
190 10
                    }
191 10
192
                    //<column-result/>
193
                    if (isset($entityElement['name'])) {
194 10
                        $columns[] = [
195
                            'name' => (string) $entityElement['name'],
196
                        ];
197
                    }
198
                }
199
200
                $metadata->addSqlResultSetMapping(
201 34
                    [
202
                        'name'          => (string) $rsmElement['name'],
203
                        'entities'      => $entities,
204
                        'columns'       => $columns
205
                    ]
206
                );
207
            }
208 34
        }
209 3
210
        if (isset($xmlRoot['inheritance-type'])) {
211 3
            $inheritanceType = strtoupper((string) $xmlRoot['inheritance-type']);
212 3
213
            $metadata->setInheritanceType(
214 3
                constant(sprintf('%s::%s', Mapping\InheritanceType::class, $inheritanceType))
215 1
            );
216
217
            if ($metadata->inheritanceType !== Mapping\InheritanceType::NONE) {
218 3
                $discriminatorColumn = new Mapping\DiscriminatorColumnMetadata();
219 1
220
                $discriminatorColumn->setTableName($metadata->getTableName());
221
                $discriminatorColumn->setColumnName('dtype');
222 3
                $discriminatorColumn->setType(Type::getType('string'));
223 2
                $discriminatorColumn->setLength(255);
224
225 3
                // Evaluate <discriminator-column...>
226
                if (isset($xmlRoot->{'discriminator-column'})) {
227
                    $discrColumn = $xmlRoot->{'discriminator-column'};
228
                    $metadata->setDiscriminatorColumn(
229
                        [
230
                            'name' => isset($discrColumn['name']) ? (string) $discrColumn['name'] : null,
231 34
                            'type' => (string) ($discrColumn['type'] ?? 'string'),
232 2
                            'length' => isset($discrColumn['length']) ? (string) $discrColumn['length'] : 255,
233
                            'columnDefinition' => isset($discrColumn['column-definition']) ? (string) $discrColumn['column-definition'] : null
234 2
                        ]
235 2
                    );
236
                } else {
237
                    $metadata->setDiscriminatorColumn(['name' => 'dtype', 'type' => 'string', 'length' => 255]);
238 2
                }
239 2
240
                $metadata->setDiscriminatorColumn($discriminatorColumn);
241
242 2
                // Evaluate <discriminator-map...>
243 2
                if (isset($xmlRoot->{'discriminator-map'})) {
244
                    $map = [];
245 2
246
                    foreach ($xmlRoot->{'discriminator-map'}->{'discriminator-mapping'} as $discrMapElement) {
247
                        $map[(string) $discrMapElement['value']] = $metadata->fullyQualifiedClassName((string) $discrMapElement['class']);
248
                    }
249
250 34
                    $metadata->setDiscriminatorMap($map);
251 4
                }
252
            }
253
        }
254
255 34
256 21
        // Evaluate <change-tracking-policy...>
257 21
        if (isset($xmlRoot['change-tracking-policy'])) {
258 21
            $changeTrackingPolicy = strtoupper((string) $xmlRoot['change-tracking-policy']);
259 21
260 21
            $metadata->setChangeTrackingPolicy(
261
                constant(sprintf('%s::%s', Mapping\ChangeTrackingPolicy::class, $changeTrackingPolicy))
262 21
            );
263 2
        }
264
265 21
        // Evaluate <field ...> mappings
266
        if (isset($xmlRoot->field)) {
267
            foreach ($xmlRoot->field as $fieldElement) {
268
                $fieldName        = (string) $fieldElement['name'];
269
                $isFieldVersioned = isset($fieldElement['version']) && $fieldElement['version'];
270 34
                $fieldMetadata    = $this->convertFieldElementToFieldMetadata($fieldElement, $fieldName, $isFieldVersioned);
271 3
272 3
                $metadata->addProperty($fieldMetadata);
273 3
            }
274 3
        }
275
276 3
        if (isset($xmlRoot->embedded)) {
277 2
            foreach ($xmlRoot->embedded as $embeddedMapping) {
278 3
                $columnPrefix = isset($embeddedMapping['column-prefix'])
279
                    ? (string) $embeddedMapping['column-prefix']
280
                    : null;
281 3
282 3
                $useColumnPrefix = isset($embeddedMapping['use-column-prefix'])
283 3
                    ? $this->evaluateBoolean($embeddedMapping['use-column-prefix'])
284
                    : true;
285
286 3
                $mapping = [
287
                    'fieldName' => (string) $embeddedMapping['name'],
288
                    'class' => (string) $embeddedMapping['class'],
289
                    'columnPrefix' => $useColumnPrefix ? $columnPrefix : false
290
                ];
291 34
292
                $metadata->mapEmbedded($mapping);
293 34
            }
294 31
        }
295 2
296
        // Evaluate <id ...> mappings
297 2
        $associationIds = [];
298
299
        foreach ($xmlRoot->id as $idElement) {
300 30
            $fieldName = (string) $idElement['name'];
301 30
302 30
            if (isset($idElement['association-key']) && $this->evaluateBoolean($idElement['association-key'])) {
303
                $associationIds[$fieldName] = true;
304 30
305
                continue;
306 30
            }
307
308 30
            $fieldMetadata = $this->convertFieldElementToFieldMetadata($idElement, $fieldName, false);
309 29
310 29
            $fieldMetadata->setPrimaryKey(true);
311 29
312
            if (isset($idElement->generator)) {
313 29
                $strategy = (string) ($idElement->generator['strategy'] ?? 'AUTO');
314 29
315
                $idGeneratorType = constant(sprintf('%s::%s', Mapping\GeneratorType::class, strtoupper($strategy)));
316
317
                if ($idGeneratorType !== Mapping\GeneratorType::NONE) {
318
                    $idGeneratorDefinition = [];
319 30
320 2
                    // Check for SequenceGenerator/TableGenerator definition
321
                    if (isset($idElement->{'sequence-generator'})) {
322 2
                        $seqGenerator = $idElement->{'sequence-generator'};
323 2
            $idGeneratorDefinition = [
324 2
                            'sequenceName' => (string) $seqGenerator['sequence-name'],
325 2
                            'allocationSize' => (string) $seqGenerator['allocation-size'],
326
                        ];
327 28
                    } elseif (isset($idElement->{'custom-id-generator'})) {
328 2
                        $customGenerator = $idElement->{'custom-id-generator'};
329
330 2
                        $idGeneratorDefinition = [
331 2
                            'class' => (string) $customGenerator['class'],
332
                            'arguments' => [],
333 26
                        ];
334 30
                    } elseif (isset($idElement->{'table-generator'})) {
335
                        throw Mapping\MappingException::tableIdGeneratorNotImplemented($className);
336
                    }
337
338
            if (isset($idElement->generator)) {
339 34
                $strategy = (string) ($idElement->generator['strategy'] ?? 'AUTO');
340 7
                $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_'
341
                    . $strategy));
342 7
            }
343 7
344
            $metadata->addProperty($fieldMetadata);
345
        }
346 7
347
        // Evaluate <one-to-one ...> mappings
348
        if (isset($xmlRoot->{'one-to-one'})) {
349
            foreach ($xmlRoot->{'one-to-one'} as $oneToOneElement) {
350 7
                $association  = new Mapping\OneToOneAssociationMetadata((string) $oneToOneElement['field']);
351 2
                $targetEntity = $metadata->fullyQualifiedClassName((string) $oneToOneElement['target-entity']);
352
353
                $association->setTargetEntity($targetEntity);
354 7
355 2
                if (isset($associationIds[$association->getName()])) {
356
                    $association->setPrimaryKey(true);
357 7
                }
358 7
359
                if (isset($oneToOneElement['fetch'])) {
360
                    $association->setFetchMode(
361 7
                        constant(sprintf('%s::%s', Mapping\FetchMode::class, (string) $oneToOneElement['fetch']))
362
                    );
363 7
                }
364 6
365 1
                if (isset($oneToOneElement['mapped-by'])) {
366 1
                    $association->setMappedBy((string) $oneToOneElement['mapped-by']);
367 1
                } else {
368
                    if (isset($oneToOneElement['inversed-by'])) {
369
                        $association->setInversedBy((string) $oneToOneElement['inversed-by']);
370
                    }
371 7
372
                    $joinColumns = [];
373
374 7
                    if (isset($oneToOneElement->{'join-column'})) {
375 5
                        $joinColumns[] = $this->convertJoinColumnElementToJoinColumnMetadata($oneToOneElement->{'join-column'});
376
                    } else if (isset($oneToOneElement->{'join-columns'})) {
377
                        foreach ($oneToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
378 7
                            $joinColumns[] = $this->convertJoinColumnElementToJoinColumnMetadata($joinColumnElement);
379 3
                        }
380
                    }
381
382
                    $association->setJoinColumns($joinColumns);
383 7
                }
384
385
                if (isset($oneToOneElement->cascade)) {
386
                    $association->setCascade($this->getCascadeMappings($oneToOneElement->cascade));
387 7
                }
388
389
                if (isset($oneToOneElement['orphan-removal'])) {
390
                    $association->setOrphanRemoval($this->evaluateBoolean($oneToOneElement['orphan-removal']));
391
                }
392 34
393 7
                // Evaluate second level cache
394
                if (isset($oneToOneElement->cache)) {
395 7
                    $association->setCache(
396 7
                        $this->convertCacheElementToCacheMetadata(
397 7
                            $oneToOneElement->cache,
398
                            $metadata,
399
                            $association->getName()
400 7
                        )
401 2
                    );
402
                }
403
404 7
                $metadata->addProperty($association);
405 5
            }
406
        }
407
408 7
        // Evaluate <one-to-many ...> mappings
409 5
        if (isset($xmlRoot->{'one-to-many'})) {
410
            foreach ($xmlRoot->{'one-to-many'} as $oneToManyElement) {
411
                $association  = new Mapping\OneToManyAssociationMetadata((string) $oneToManyElement['field']);
412 7
                $targetEntity = $metadata->fullyQualifiedClassName((string) $oneToManyElement['target-entity']);
413 5
414 5
                $association->setTargetEntity($targetEntity);
415 5
                $association->setMappedBy((string) $oneToManyElement['mapped-by']);
416
417 5
                if (isset($associationIds[$association->getName()])) {
418
                    throw Mapping\MappingException::illegalToManyIdentifierAssociation($className, $association->getName());
419
                }
420 7
421 2
                if (isset($oneToManyElement['fetch'])) {
422 5
                    $association->setFetchMode(
423
                        constant(sprintf('%s::%s', Mapping\FetchMode::class, (string) $oneToManyElement['fetch']))
424
                    );
425
                }
426
427 7
                if (isset($oneToManyElement->cascade)) {
428 1
                    $association->setCascade($this->getCascadeMappings($oneToManyElement->cascade));
429
                }
430
431 7
                if (isset($oneToManyElement['orphan-removal'])) {
432
                    $association->setOrphanRemoval($this->evaluateBoolean($oneToManyElement['orphan-removal']));
433
                }
434
435
                if (isset($oneToManyElement->{'order-by'})) {
436 34
                    $orderBy = [];
437 7
438
                    foreach ($oneToManyElement->{'order-by'}->{'order-by-field'} as $orderByField) {
439 7
                        $orderBy[(string) $orderByField['name']] = (string) $orderByField['direction'];
440 7
                    }
441
442
                    $association->setOrderBy($orderBy);
443 7
                }
444 2
445
                if (isset($oneToManyElement['index-by'])) {
446
                    $association->setIndexedBy((string) $oneToManyElement['index-by']);
447 7
                } else if (isset($oneToManyElement->{'index-by'})) {
448 1
                    throw new \InvalidArgumentException("<index-by /> is not a valid tag");
449
                }
450
451 7
                // Evaluate second level cache
452 1
                if (isset($oneToManyElement->cache)) {
453
                    $association->setCache(
454
                        $this->convertCacheElementToCacheMetadata(
455 7
                            $oneToManyElement->cache,
456
                            $metadata,
457 7
                            $association->getName()
458 4
                        )
459 3
                    );
460 2
                }
461 2
462
                $metadata->addProperty($association);
463
            }
464
        }
465 7
466
        // Evaluate <many-to-one ...> mappings
467 7
        if (isset($xmlRoot->{'many-to-one'})) {
468 2
            foreach ($xmlRoot->{'many-to-one'} as $manyToOneElement) {
469
                $association  = new Mapping\ManyToOneAssociationMetadata((string) $manyToOneElement['field']);
470
                $targetEntity = $metadata->fullyQualifiedClassName((string) $manyToOneElement['target-entity']);
471
472 7
                $association->setTargetEntity($targetEntity);
473 1
474
                if (isset($associationIds[$association->getName()])) {
475
                    $association->setPrimaryKey(true);
476 7
                }
477
478
                if (isset($manyToOneElement['fetch'])) {
479
                    $association->setFetchMode(
480
                        constant('Doctrine\ORM\Mapping\FetchMode::' . (string) $manyToOneElement['fetch'])
481
                    );
482 33
                }
483 10
484
                if (isset($manyToOneElement['inversed-by'])) {
485 10
                    $association->setInversedBy((string) $manyToOneElement['inversed-by']);
486 10
                }
487
488
                $joinColumns = [];
489 10
490 2
                if (isset($manyToOneElement->{'join-column'})) {
491
                    $joinColumns[] = $this->convertJoinColumnElementToJoinColumnMetadata($manyToOneElement->{'join-column'});
492
                } else if (isset($manyToOneElement->{'join-columns'})) {
493 10
                    foreach ($manyToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
494
                        $joinColumns[] = $this->convertJoinColumnElementToJoinColumnMetadata($joinColumnElement);
495
                    }
496
                }
497 10
498 2
                $association->setJoinColumns($joinColumns);
499 8
500 7
                if (isset($manyToOneElement->cascade)) {
501 2
                    $association->setCascade($this->getCascadeMappings($manyToOneElement->cascade));
502
                }
503
504 7
                // Evaluate second level cache
505
                if (isset($manyToOneElement->cache)) {
506 7
                    $association->setCache(
507
                        $this->convertCacheElementToCacheMetadata(
508
                            $manyToOneElement->cache,
509 7
                            $metadata,
510
                            $association->getName()
511
                        )
512
                    );
513 7
                }
514 7
515
                $metadata->addProperty($association);
516
            }
517 7
        }
518 7
519
        // Evaluate <many-to-many ...> mappings
520
        if (isset($xmlRoot->{'many-to-many'})) {
521 7
            foreach ($xmlRoot->{'many-to-many'} as $manyToManyElement) {
522
                $association  = new Mapping\ManyToManyAssociationMetadata((string) $manyToManyElement['field']);
523
                $targetEntity = $metadata->fullyQualifiedClassName((string) $manyToManyElement['target-entity']);
524 10
525 7
                $association->setTargetEntity($targetEntity);
526
527
                if (isset($associationIds[$association->getName()])) {
528 10
                    throw Mapping\MappingException::illegalToManyIdentifierAssociation($className, $association->getName());
529
                }
530
531
                if (isset($manyToManyElement['fetch'])) {
532
                    $association->setFetchMode(
533
                        constant(sprintf('%s::%s', Mapping\FetchMode::class, (string) $manyToManyElement['fetch']))
534
                    );
535
                }
536
537
                if (isset($manyToManyElement['orphan-removal'])) {
538 10
                    $association->setOrphanRemoval($this->evaluateBoolean($manyToManyElement['orphan-removal']));
539
                }
540 10
541
                if (isset($manyToManyElement['mapped-by'])) {
542
                    $association->setMappedBy((string) $manyToManyElement['mapped-by']);
543
                } else if (isset($manyToManyElement->{'join-table'})) {
544
                    if (isset($manyToManyElement['inversed-by'])) {
545 10
                        $association->setInversedBy((string) $manyToManyElement['inversed-by']);
546
                    }
547
548
                    $joinTableElement = $manyToManyElement->{'join-table'};
549 10
                    $joinTable        = new Mapping\JoinTableMetadata();
550
551
                    if (isset($joinTableElement['name'])) {
552
                        $joinTable->setName((string) $joinTableElement['name']);
553
                    }
554 33
555 2
                    if (isset($joinTableElement['schema'])) {
556 2
                        $joinTable->setSchema((string) $joinTableElement['schema']);
557
                    }
558 2
559 2
                    if (isset($joinTableElement->{'join-columns'})) {
560
                        foreach ($joinTableElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
561 2
                            $joinColumn = $this->convertJoinColumnElementToJoinColumnMetadata($joinColumnElement);
562
563
                            $joinTable->addJoinColumn($joinColumn);
564
                        }
565
                    }
566
567 33
                    if (isset($joinTableElement->{'inverse-join-columns'})) {
568 3
                        foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} as $joinColumnElement) {
569 3
                            $joinColumn = $this->convertJoinColumnElementToJoinColumnMetadata($joinColumnElement);
570 3
571
                            $joinTable->addInverseJoinColumn($joinColumn);
572
                        }
573 3
                    }
574 2
575
                    $association->setJoinTable($joinTable);
576 2
                }
577 2
578
                if (isset($manyToManyElement->cascade)) {
579
                    $association->setCascade($this->getCascadeMappings($manyToManyElement->cascade));
580 2
                }
581
582
                if (isset($manyToManyElement->{'order-by'})) {
583
                    $orderBy = [];
584 3
585 2
                    foreach ($manyToManyElement->{'order-by'}->{'order-by-field'} as $orderByField) {
586 2
                        $orderBy[(string) $orderByField['name']] = (string) $orderByField['direction'];
587
                    }
588
589 2
                    $association->setOrderBy($orderBy);
590 2
                }
591
592
                if (isset($manyToManyElement['index-by'])) {
593 2
                    $association->setIndexedBy((string) $manyToManyElement['index-by']);
594 2
                } else if (isset($manyToManyElement->{'index-by'})) {
595 2
                    throw new \InvalidArgumentException("<index-by /> is not a valid tag");
596
                }
597
598
                // Evaluate second level cache
599 2
                if (isset($manyToManyElement->cache)) {
600 2
                    $association->setCache(
601 2
                        $this->convertCacheElementToCacheMetadata(
602
                            $manyToManyElement->cache,
603
                            $metadata,
604
                            $association->getName()
605 2
                        )
606
                    );
607
                }
608
609 3
                $metadata->addProperty($association);
610 1
            }
611
        }
612
613 3
        // Evaluate association-overrides
614
        if (isset($xmlRoot->{'attribute-overrides'})) {
615
            foreach ($xmlRoot->{'attribute-overrides'}->{'attribute-override'} as $overrideElement) {
616
                $fieldName = (string) $overrideElement['name'];
617
618 33
                foreach ($overrideElement->field as $fieldElement) {
619 4
                    $fieldMetadata = $this->convertFieldElementToFieldMetadata($fieldElement, $fieldName, false);
620 4
621
                    $metadata->setPropertyOverride($fieldMetadata);
622
                }
623
            }
624
        }
625 33
626 4
        // Evaluate association-overrides
627 4
        if (isset($xmlRoot->{'association-overrides'})) {
628
            foreach ($xmlRoot->{'association-overrides'}->{'association-override'} as $overrideElement) {
629
                $fieldName = (string) $overrideElement['name'];
630 4
                $property  = $metadata->getProperty($fieldName);
631 2
632
                if (! $property) {
633 2
                    throw Mapping\MappingException::invalidOverrideFieldName($metadata->getClassName(), $fieldName);
634
                }
635
636 2
                $existingClass = get_class($property);
637 2
                $override      = new $existingClass($fieldName);
638 2
639
                // Check for join-columns
640 2
                if (isset($overrideElement->{'join-columns'})) {
641
                    $joinColumns = [];
642
643
                    foreach ($overrideElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
644 33
                        $joinColumns[] = $this->convertJoinColumnElementToJoinColumnMetadata($joinColumnElement);
645
                    }
646
647
                    $override->setJoinColumns($joinColumns);
648
                }
649
650
                // Check for join-table
651
                if ($overrideElement->{'join-table'}) {
652
                    $joinTableElement   = $overrideElement->{'join-table'};
653 5
                    $joinTable          = new Mapping\JoinTableMetadata();
654
655 5
                    if (isset($joinTableElement['name'])) {
656
                        $joinTable->setName((string) $joinTableElement['name']);
657
                    }
658 5
659 5
                    if (isset($joinTableElement['schema'])) {
660 4
                        $joinTable->setSchema((string) $joinTableElement['schema']);
661
                    }
662 5
663
                    if (isset($joinTableElement->{'join-columns'})) {
664
                        foreach ($joinTableElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
665 5
                            $joinColumn = $this->convertJoinColumnElementToJoinColumnMetadata($joinColumnElement);
666
667 5
                            $joinTable->addJoinColumn($joinColumn);
668 5
                        }
669
                    }
670 5
671
                    if (isset($joinTableElement->{'inverse-join-columns'})) {
672
                        foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} as $joinColumnElement) {
673
                            $joinColumn = $this->convertJoinColumnElementToJoinColumnMetadata($joinColumnElement);
674 5
675
                            $joinTable->addInverseJoinColumn($joinColumn);
676
                        }
677
                    }
678
679
                    $override->setJoinTable($joinTable);
680
                }
681
682
                // Check for inversed-by
683
                if (isset($overrideElement->{'inversed-by'})) {
684
                    $override->setInversedBy((string) $overrideElement->{'inversed-by'}['name']);
685 12
                }
686
687
                // Check for fetch
688 12
                if (isset($overrideElement['fetch'])) {
689 12
                    $override->setFetchMode(
690
                        constant('Doctrine\ORM\Mapping\FetchMode::' . (string) $overrideElement['fetch'])
691
                    );
692 12
                }
693 3
694
                $metadata->setPropertyOverride($override);
695
            }
696 12
        }
697 5
698
        // Evaluate <lifecycle-callbacks...>
699
        if (isset($xmlRoot->{'lifecycle-callbacks'})) {
700 12
            foreach ($xmlRoot->{'lifecycle-callbacks'}->{'lifecycle-callback'} as $lifecycleCallback) {
701 4
                $eventName   = constant(Events::class . '::' . (string) $lifecycleCallback['type']);
702
                $methodName  = (string) $lifecycleCallback['method'];
703
704 12
                $metadata->addLifecycleCallback($methodName, $eventName);
705 4
            }
706
        }
707
708 12
        // Evaluate entity listener
709
        if (isset($xmlRoot->{'entity-listeners'})) {
710
            foreach ($xmlRoot->{'entity-listeners'}->{'entity-listener'} as $listenerElement) {
711
                $listenerClassName = $metadata->fullyQualifiedClassName((string) $listenerElement['class']);
712
713
                if (! class_exists($listenerClassName)) {
714
                    throw Mapping\MappingException::entityListenerClassNotFound(
715
                        $listenerClassName,
716
                        $metadata->getClassName()
717
                    );
718 33
                }
719
720
                $listenerClass = new \ReflectionClass($listenerClassName);
721 33
722
                // Evaluate the listener using naming convention.
723
                if ($listenerElement->count() === 0) {
724 33
                    /* @var $method \ReflectionMethod */
725 21
                    foreach ($listenerClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
726
                        foreach ($this->getMethodCallbacks($method) as $callback) {
727 21
                            $metadata->addEntityListener($callback, $listenerClassName, $method->getName());
728
                        }
729
                    }
730
731 21
                    continue;
732
                }
733
734 33
                foreach ($listenerElement as $callbackElement) {
735 23
                    $eventName   = (string) $callbackElement['type'];
736
                    $methodName  = (string) $callbackElement['method'];
737
738 33
                    $metadata->addEntityListener($eventName, $listenerClassName, $methodName);
739 10
                }
740
            }
741
        }
742 33
    }
743 1
744
    /**
745
     * Parses (nested) option elements.
746 33
     *
747 1
     * @param SimpleXMLElement $options The XML element.
748
     *
749
     * @return array The options array.
750 33
     */
751 9
    private function parseOptions(SimpleXMLElement $options)
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected T_PRIVATE on line 751 at column 4
Loading history...
752
    {
753
        $array = [];
754 33
755 8
        /* @var $option SimpleXMLElement */
756
        foreach ($options as $option) {
757
            if ($option->count()) {
758 33
                $value = $this->parseOptions($option->children());
759 2
            } else {
760
                $value = (string) $option;
761
            }
762 33
763 5
            $attributes = $option->attributes();
764
765
            if (isset($attributes->name)) {
766 33
                $nameAttribute = (string) $attributes->name;
767 4
                $array[$nameAttribute] = in_array($nameAttribute, ['unsigned', 'fixed'])
768
                    ? $this->evaluateBoolean($value)
769
                    : $value;
770 33
            } else {
771
                $array[] = $value;
772
            }
773
774
        }
775
776
        return $array;
777
    }
778
779
    /**
780 2
     * @param SimpleXMLElement $fieldElement
781
     * @param string           $fieldName
782 2
     * @param bool             $isVersioned
783 2
     *
784
     * @return Mapping\FieldMetadata
785 2
     */
786
    private function convertFieldElementToFieldMetadata(SimpleXMLElement $fieldElement, string $fieldName, bool $isVersioned)
787
    {
788
        $fieldMetadata = $isVersioned
789 2
            ? new Mapping\VersionFieldMetadata($fieldName)
790 2
            : new Mapping\FieldMetadata($fieldName)
791
        ;
792
793
        $fieldMetadata->setType(Type::getType('string'));
794 2
795 2
        if (isset($fieldElement['type'])) {
796
            $fieldMetadata->setType(Type::getType((string) $fieldElement['type']));
797
        }
798
799
        if (isset($fieldElement['column'])) {
800
            $fieldMetadata->setColumnName((string) $fieldElement['column']);
801
        }
802
803
        if (isset($fieldElement['length'])) {
804
            $fieldMetadata->setLength((int) $fieldElement['length']);
805
        }
806 7
807
        if (isset($fieldElement['precision'])) {
808 7
            $fieldMetadata->setPrecision((int) $fieldElement['precision']);
809
        }
810
811 7
        if (isset($fieldElement['scale'])) {
812
            $fieldMetadata->setScale((int) $fieldElement['scale']);
813
        }
814
815
        if (isset($fieldElement['unique'])) {
816
            $fieldMetadata->setUnique($this->evaluateBoolean($fieldElement['unique']));
817 7
        }
818
819
        if (isset($fieldElement['nullable'])) {
820 7
            $fieldMetadata->setNullable($this->evaluateBoolean($fieldElement['nullable']));
821
        }
822
823
        if (isset($fieldElement['column-definition'])) {
824
            $fieldMetadata->setColumnDefinition((string) $fieldElement['column-definition']);
825
        }
826 36
827
        if (isset($fieldElement->options)) {
828 36
            $fieldMetadata->setOptions($this->parseOptions($fieldElement->options->children()));
829 36
        }
830
831 36
        return $fieldMetadata;
832 34
    }
833 34
834 34
    /**
835
     * Constructs a joinColumn mapping array based on the information
836 7
     * found in the given SimpleXMLElement.
837 5
     *
838 5
     * @param SimpleXMLElement $joinColumnElement The XML element.
839 5
     *
840
     * @return Mapping\JoinColumnMetadata
841 2
     */
842 1
    private function convertJoinColumnElementToJoinColumnMetadata(SimpleXMLElement $joinColumnElement)
843 1
    {
844 1
        $joinColumnMetadata = new Mapping\JoinColumnMetadata();
845
846
        $joinColumnMetadata->setColumnName((string) $joinColumnElement['name']);
847
        $joinColumnMetadata->setReferencedColumnName((string) $joinColumnElement['referenced-column-name']);
848 36
849
        if (isset($joinColumnElement['column-definition'])) {
850
            $joinColumnMetadata->setColumnDefinition((string) $joinColumnElement['column-definition']);
851
        }
852
853
        if (isset($joinColumnElement['field-name'])) {
854
            $joinColumnMetadata->setAliasedName((string) $joinColumnElement['field-name']);
855
        }
856 13
857
        if (isset($joinColumnElement['nullable'])) {
858 13
            $joinColumnMetadata->setNullable($this->evaluateBoolean($joinColumnElement['nullable']));
859
        }
860 13
861
        if (isset($joinColumnElement['unique'])) {
862
            $joinColumnMetadata->setUnique($this->evaluateBoolean($joinColumnElement['unique']));
863
        }
864
865
        if (isset($joinColumnElement['on-delete'])) {
866
            $joinColumnMetadata->setOnDelete(strtoupper((string) $joinColumnElement['on-delete']));
867
        }
868
869
        return $joinColumnMetadata;
870
    }
871
872
    /**
873
     * Parse the given Cache as CacheMetadata
874
     *
875
     * @param \SimpleXMLElement     $cacheMapping
876
     * @param Mapping\ClassMetadata $metadata
877
     * @param null|string           $fieldName
878
     *
879
     * @return Mapping\CacheMetadata
880
     */
881
    private function convertCacheElementToCacheMetadata(
882
        SimpleXMLElement $cacheMapping,
883
        Mapping\ClassMetadata $metadata,
884
        $fieldName = null
885
    )
886
    {
887
        $baseRegion    = strtolower(str_replace('\\', '_', $metadata->getRootClassName()));
888
        $defaultRegion = $baseRegion . ($fieldName ? '__' . $fieldName : '');
889
890
        $region = (string) ($cacheMapping['region'] ?? $defaultRegion);
891
        $usage  = isset($cacheMapping['usage'])
892
            ? constant(sprintf('%s::%s', Mapping\CacheUsage::class, strtoupper((string) $cacheMapping['usage'])))
893
            : Mapping\CacheUsage::READ_ONLY
894
        ;
895
896
        return new Mapping\CacheMetadata($usage, $region);
897
    }
898
899
    /**
900
     * Parses the given method.
901
     *
902
     * @param \ReflectionMethod $method
903
     *
904
     * @return array
905
     */
906
    private function getMethodCallbacks(\ReflectionMethod $method)
907
    {
908
        $events = [
909
            Events::prePersist,
910
            Events::postPersist,
911
            Events::preUpdate,
912
            Events::postUpdate,
913
            Events::preRemove,
914
            Events::postRemove,
915
            Events::postLoad,
916
            Events::preFlush,
917
        ];
918
919
        return array_filter($events, function ($eventName) use ($method) {
920
            return $eventName === $method->getName();
921
        });
922
    }
923
924
    /**
925
     * Gathers a list of cascade options found in the given cascade element.
926
     *
927
     * @param SimpleXMLElement $cascadeElement The cascade element.
928
     *
929
     * @return array The list of cascade options.
930
     */
931
    private function getCascadeMappings(SimpleXMLElement $cascadeElement)
932
    {
933
        $cascades = [];
934
935
        /* @var $action SimpleXmlElement */
936
        foreach ($cascadeElement->children() as $action) {
937
            // According to the JPA specifications, XML uses "cascade-persist"
938
            // instead of "persist". Here, both variations are supported
939
            // because Annotation use "persist" and we want to make sure that
940
            // this driver doesn't need to know anything about the supported
941
            // cascading actions
942
            $cascades[] = str_replace('cascade-', '', $action->getName());
943
        }
944
945
        return $cascades;
946
    }
947
948
    /**
949
     * {@inheritDoc}
950
     */
951
    protected function loadMappingFile($file)
952
    {
953
        $result = [];
954
        // Note: we do not use `simplexml_load_file()` because of https://bugs.php.net/bug.php?id=62577
955
        $xmlElement = simplexml_load_string(file_get_contents($file));
956
957
        if (isset($xmlElement->entity)) {
958
            foreach ($xmlElement->entity as $entityElement) {
959
                $entityName = (string) $entityElement['name'];
960
                $result[$entityName] = $entityElement;
961
            }
962
        } else if (isset($xmlElement->{'mapped-superclass'})) {
963
            foreach ($xmlElement->{'mapped-superclass'} as $mappedSuperClass) {
964
                $className = (string) $mappedSuperClass['name'];
965
                $result[$className] = $mappedSuperClass;
966
            }
967
        } else if (isset($xmlElement->embeddable)) {
968
            foreach ($xmlElement->embeddable as $embeddableElement) {
969
                $embeddableName = (string) $embeddableElement['name'];
970
                $result[$embeddableName] = $embeddableElement;
971
            }
972
        }
973
974
        return $result;
975
    }
976
977
    /**
978
     * @param mixed $element
979
     *
980
     * @return bool
981
     */
982
    protected function evaluateBoolean($element)
983
    {
984
        $flag = (string) $element;
985
986
        return ($flag == "true" || $flag == "1");
987
    }
988
}
989