Failed Conditions
Push — master ( a0c0d3...57a950 )
by Marco
22:07
created

lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ORM\Mapping\Driver;
21
22
use SimpleXMLElement;
23
use Doctrine\Common\Persistence\Mapping\Driver\FileDriver;
24
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
25
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
26
use Doctrine\ORM\Mapping\MappingException;
27
use Doctrine\ORM\Mapping\ClassMetadata as Metadata;
28
29
/**
30
 * XmlDriver is a metadata driver that enables mapping through XML files.
31
 *
32
 * @license 	http://www.opensource.org/licenses/mit-license.php MIT
33
 * @link    	www.doctrine-project.org
34
 * @since   	2.0
35
 * @author		Benjamin Eberlei <[email protected]>
36
 * @author		Guilherme Blanco <[email protected]>
37
 * @author      Jonathan H. Wage <[email protected]>
38
 * @author      Roman Borschel <[email protected]>
39
 */
40
class XmlDriver extends FileDriver
41
{
42
    const DEFAULT_FILE_EXTENSION = '.dcm.xml';
43
44
    /**
45
     * {@inheritDoc}
46
     */
47 43
    public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION)
48
    {
49 43
        parent::__construct($locator, $fileExtension);
50 43
    }
51
52
    /**
53
     * {@inheritDoc}
54
     */
55 38
    public function loadMetadataForClass($className, ClassMetadata $metadata)
56
    {
57
        /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */
58
        /* @var $xmlRoot SimpleXMLElement */
59 38
        $xmlRoot = $this->getElement($className);
60
61 36
        if ($xmlRoot->getName() == 'entity') {
62 35
            if (isset($xmlRoot['repository-class'])) {
63
                $metadata->setCustomRepositoryClass((string) $xmlRoot['repository-class']);
64
            }
65 35
            if (isset($xmlRoot['read-only']) && $this->evaluateBoolean($xmlRoot['read-only'])) {
66 35
                $metadata->markReadOnly();
67
            }
68 8
        } else if ($xmlRoot->getName() == 'mapped-superclass') {
69 5
            $metadata->setCustomRepositoryClass(
70 5
                isset($xmlRoot['repository-class']) ? (string) $xmlRoot['repository-class'] : null
71
            );
72 5
            $metadata->isMappedSuperclass = true;
73 3 View Code Duplication
        } else if ($xmlRoot->getName() == 'embeddable') {
74 3
            $metadata->isEmbeddedClass = true;
75
        } else {
76
            throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
77
        }
78
79
        // Evaluate <entity...> attributes
80 36
        $primaryTable = [];
81
82 36
        if (isset($xmlRoot['table'])) {
83 15
            $primaryTable['name'] = (string) $xmlRoot['table'];
84
        }
85
86 36
        if (isset($xmlRoot['schema'])) {
87 1
            $primaryTable['schema'] = (string) $xmlRoot['schema'];
88
        }
89
90 36
        $metadata->setPrimaryTable($primaryTable);
91
92
        // Evaluate second level cache
93 36
        if (isset($xmlRoot->cache)) {
94 2
            $metadata->enableCache($this->cacheToArray($xmlRoot->cache));
95
        }
96
97
        // Evaluate named queries
98 36
        if (isset($xmlRoot->{'named-queries'})) {
99 4
            foreach ($xmlRoot->{'named-queries'}->{'named-query'} as $namedQueryElement) {
100 4
                $metadata->addNamedQuery(
101
                    [
102 4
                        'name'  => (string) $namedQueryElement['name'],
103 4
                        'query' => (string) $namedQueryElement['query']
104
                    ]
105
                );
106
            }
107
        }
108
109
        // Evaluate native named queries
110 36
        if (isset($xmlRoot->{'named-native-queries'})) {
111 3
            foreach ($xmlRoot->{'named-native-queries'}->{'named-native-query'} as $nativeQueryElement) {
112 3
                $metadata->addNamedNativeQuery(
113
                    [
114 3
                        'name'              => isset($nativeQueryElement['name']) ? (string) $nativeQueryElement['name'] : null,
115 3
                        'query'             => isset($nativeQueryElement->query) ? (string) $nativeQueryElement->query : null,
116 3
                        'resultClass'       => isset($nativeQueryElement['result-class']) ? (string) $nativeQueryElement['result-class'] : null,
117 3
                        'resultSetMapping'  => isset($nativeQueryElement['result-set-mapping']) ? (string) $nativeQueryElement['result-set-mapping'] : null,
118
                    ]
119
                );
120
            }
121
        }
122
123
        // Evaluate sql result set mapping
124 36
        if (isset($xmlRoot->{'sql-result-set-mappings'})) {
125 3
            foreach ($xmlRoot->{'sql-result-set-mappings'}->{'sql-result-set-mapping'} as $rsmElement) {
126 3
                $entities   = [];
127 3
                $columns    = [];
128 3
                foreach ($rsmElement as $entityElement) {
129
                    //<entity-result/>
130 3
                    if (isset($entityElement['entity-class'])) {
131
                        $entityResult = [
132 3
                            'fields'                => [],
133 3
                            'entityClass'           => (string) $entityElement['entity-class'],
134 3
                            'discriminatorColumn'   => isset($entityElement['discriminator-column']) ? (string) $entityElement['discriminator-column'] : null,
135
                        ];
136
137 3 View Code Duplication
                        foreach ($entityElement as $fieldElement) {
138 3
                            $entityResult['fields'][] = [
139 3
                                'name'      => isset($fieldElement['name']) ? (string) $fieldElement['name'] : null,
140 3
                                'column'    => isset($fieldElement['column']) ? (string) $fieldElement['column'] : null,
141
                            ];
142
                        }
143
144 3
                        $entities[] = $entityResult;
145
                    }
146
147
                    //<column-result/>
148 3
                    if (isset($entityElement['name'])) {
149 3
                        $columns[] = [
150 3
                            'name' => (string) $entityElement['name'],
151
                        ];
152
                    }
153
                }
154
155 3
                $metadata->addSqlResultSetMapping(
156
                    [
157 3
                        'name'          => (string) $rsmElement['name'],
158 3
                        'entities'      => $entities,
159 3
                        'columns'       => $columns
160
                    ]
161
                );
162
            }
163
        }
164
165 36
        if (isset($xmlRoot['inheritance-type'])) {
166 10
            $inheritanceType = (string) $xmlRoot['inheritance-type'];
167 10
            $metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceType));
168
169 10
            if ($metadata->inheritanceType != Metadata::INHERITANCE_TYPE_NONE) {
170
                // Evaluate <discriminator-column...>
171 10 View Code Duplication
                if (isset($xmlRoot->{'discriminator-column'})) {
172 7
                    $discrColumn = $xmlRoot->{'discriminator-column'};
173 7
                    $metadata->setDiscriminatorColumn(
174
                        [
175 7
                            'name' => isset($discrColumn['name']) ? (string) $discrColumn['name'] : null,
176 7
                            'type' => isset($discrColumn['type']) ? (string) $discrColumn['type'] : 'string',
177 7
                            'length' => isset($discrColumn['length']) ? (string) $discrColumn['length'] : 255,
178 7
                            'columnDefinition' => isset($discrColumn['column-definition']) ? (string) $discrColumn['column-definition'] : null
179
                        ]
180
                    );
181
                } else {
182 6
                    $metadata->setDiscriminatorColumn(['name' => 'dtype', 'type' => 'string', 'length' => 255]);
183
                }
184
185
                // Evaluate <discriminator-map...>
186 10
                if (isset($xmlRoot->{'discriminator-map'})) {
187 10
                    $map = [];
188 10
                    foreach ($xmlRoot->{'discriminator-map'}->{'discriminator-mapping'} as $discrMapElement) {
189 10
                        $map[(string) $discrMapElement['value']] = (string) $discrMapElement['class'];
190
                    }
191 10
                    $metadata->setDiscriminatorMap($map);
192
                }
193
            }
194
        }
195
196
197
        // Evaluate <change-tracking-policy...>
198 36 View Code Duplication
        if (isset($xmlRoot['change-tracking-policy'])) {
199
            $metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_'
200
                . strtoupper((string) $xmlRoot['change-tracking-policy'])));
201
        }
202
203
        // Evaluate <indexes...>
204 36
        if (isset($xmlRoot->indexes)) {
205 4
            $metadata->table['indexes'] = [];
206 4
            foreach ($xmlRoot->indexes->index as $indexXml) {
207 4
                $index = ['columns' => explode(',', (string) $indexXml['columns'])];
208
209 4
                if (isset($indexXml['flags'])) {
210 1
                    $index['flags'] = explode(',', (string) $indexXml['flags']);
211
                }
212
213 4
                if (isset($indexXml->options)) {
214 1
                    $index['options'] = $this->_parseOptions($indexXml->options->children());
215
                }
216
217 4
                if (isset($indexXml['name'])) {
218 3
                    $metadata->table['indexes'][(string) $indexXml['name']] = $index;
219
                } else {
220 4
                    $metadata->table['indexes'][] = $index;
221
                }
222
            }
223
        }
224
225
        // Evaluate <unique-constraints..>
226 36
        if (isset($xmlRoot->{'unique-constraints'})) {
227 3
            $metadata->table['uniqueConstraints'] = [];
228 3
            foreach ($xmlRoot->{'unique-constraints'}->{'unique-constraint'} as $uniqueXml) {
229 3
                $unique = ['columns' => explode(',', (string) $uniqueXml['columns'])];
230
231 3
                if (isset($uniqueXml->options)) {
232 3
                    $unique['options'] = $this->_parseOptions($uniqueXml->options->children());
233
                }
234
235 3
                if (isset($uniqueXml['name'])) {
236 3
                    $metadata->table['uniqueConstraints'][(string) $uniqueXml['name']] = $unique;
237
                } else {
238 3
                    $metadata->table['uniqueConstraints'][] = $unique;
239
                }
240
            }
241
        }
242
243 36
        if (isset($xmlRoot->options)) {
244 5
            $metadata->table['options'] = $this->_parseOptions($xmlRoot->options->children());
245
        }
246
247
        // The mapping assignment is done in 2 times as a bug might occurs on some php/xml lib versions
248
        // The internal SimpleXmlIterator get resetted, to this generate a duplicate field exception
249 36
        $mappings = [];
250
        // Evaluate <field ...> mappings
251 36
        if (isset($xmlRoot->field)) {
252 24
            foreach ($xmlRoot->field as $fieldMapping) {
253 24
                $mapping = $this->columnToArray($fieldMapping);
254
255 24
                if (isset($mapping['version'])) {
256 3
                    $metadata->setVersionMapping($mapping);
257 3
                    unset($mapping['version']);
258
                }
259
260 24
                $metadata->mapField($mapping);
261
            }
262
        }
263
264 36
        if (isset($xmlRoot->embedded)) {
265 3
            foreach ($xmlRoot->embedded as $embeddedMapping) {
266 3
                $columnPrefix = isset($embeddedMapping['column-prefix'])
267 3
                    ? (string) $embeddedMapping['column-prefix']
268 3
                    : null;
269
270 3
                $useColumnPrefix = isset($embeddedMapping['use-column-prefix'])
271 2
                    ? $this->evaluateBoolean($embeddedMapping['use-column-prefix'])
272 3
                    : true;
273
274
                $mapping = [
275 3
                    'fieldName' => (string) $embeddedMapping['name'],
276 3
                    'class' => (string) $embeddedMapping['class'],
277 3
                    'columnPrefix' => $useColumnPrefix ? $columnPrefix : false
278
                ];
279
280 3
                $metadata->mapEmbedded($mapping);
281
            }
282
        }
283
284 36
        foreach ($mappings as $mapping) {
285
            if (isset($mapping['version'])) {
286
                $metadata->setVersionMapping($mapping);
287
            }
288
289
            $metadata->mapField($mapping);
290
        }
291
292
        // Evaluate <id ...> mappings
293 36
        $associationIds = [];
294 36
        foreach ($xmlRoot->id as $idElement) {
295 33
            if (isset($idElement['association-key']) && $this->evaluateBoolean($idElement['association-key'])) {
296 2
                $associationIds[(string) $idElement['name']] = true;
297 2
                continue;
298
            }
299
300
            $mapping = [
301 32
                'id' => true,
302 32
                'fieldName' => (string) $idElement['name']
303
            ];
304
305 32
            if (isset($idElement['type'])) {
306 21
                $mapping['type'] = (string) $idElement['type'];
307
            }
308
309 32
            if (isset($idElement['length'])) {
310 3
                $mapping['length'] = (string) $idElement['length'];
311
            }
312
313 32
            if (isset($idElement['column'])) {
314 23
                $mapping['columnName'] = (string) $idElement['column'];
315
            }
316
317 32
            if (isset($idElement['column-definition'])) {
318 1
                $mapping['columnDefinition'] = (string) $idElement['column-definition'];
319
            }
320
321 32
            if (isset($idElement->options)) {
322 3
                $mapping['options'] = $this->_parseOptions($idElement->options->children());
323
            }
324
325 32
            $metadata->mapField($mapping);
326
327 32
            if (isset($idElement->generator)) {
328 31
                $strategy = isset($idElement->generator['strategy']) ?
329 31
                        (string) $idElement->generator['strategy'] : 'AUTO';
330 31
                $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_'
331 31
                    . $strategy));
332
            }
333
334
            // Check for SequenceGenerator/TableGenerator definition
335 32
            if (isset($idElement->{'sequence-generator'})) {
336 3
                $seqGenerator = $idElement->{'sequence-generator'};
337 3
                $metadata->setSequenceGeneratorDefinition(
338
                    [
339 3
                        'sequenceName' => (string) $seqGenerator['sequence-name'],
340 3
                        'allocationSize' => (string) $seqGenerator['allocation-size'],
341 3
                        'initialValue' => (string) $seqGenerator['initial-value']
342
                    ]
343
                );
344 29
            } else if (isset($idElement->{'custom-id-generator'})) {
345 2
                $customGenerator = $idElement->{'custom-id-generator'};
346 2
                $metadata->setCustomGeneratorDefinition(
347
                    [
348 2
                        'class' => (string) $customGenerator['class']
349
                    ]
350
                );
351 27
            } else if (isset($idElement->{'table-generator'})) {
352 32
                throw MappingException::tableIdGeneratorNotImplemented($className);
353
            }
354
        }
355
356
        // Evaluate <one-to-one ...> mappings
357 36
        if (isset($xmlRoot->{'one-to-one'})) {
358 8
            foreach ($xmlRoot->{'one-to-one'} as $oneToOneElement) {
359
                $mapping = [
360 8
                    'fieldName' => (string) $oneToOneElement['field'],
361 8
                    'targetEntity' => (string) $oneToOneElement['target-entity']
362
                ];
363
364 8
                if (isset($associationIds[$mapping['fieldName']])) {
365
                    $mapping['id'] = true;
366
                }
367
368 8 View Code Duplication
                if (isset($oneToOneElement['fetch'])) {
369 2
                    $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string) $oneToOneElement['fetch']);
370
                }
371
372 8
                if (isset($oneToOneElement['mapped-by'])) {
373 2
                    $mapping['mappedBy'] = (string) $oneToOneElement['mapped-by'];
374
                } else {
375 8
                    if (isset($oneToOneElement['inversed-by'])) {
376 8
                        $mapping['inversedBy'] = (string) $oneToOneElement['inversed-by'];
377
                    }
378 8
                    $joinColumns = [];
379
380 8 View Code Duplication
                    if (isset($oneToOneElement->{'join-column'})) {
381 7
                        $joinColumns[] = $this->joinColumnToArray($oneToOneElement->{'join-column'});
382 1
                    } else if (isset($oneToOneElement->{'join-columns'})) {
383 1
                        foreach ($oneToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
384 1
                            $joinColumns[] = $this->joinColumnToArray($joinColumnElement);
385
                        }
386
                    }
387
388 8
                    $mapping['joinColumns'] = $joinColumns;
389
                }
390
391 8
                if (isset($oneToOneElement->cascade)) {
392 6
                    $mapping['cascade'] = $this->_getCascadeMappings($oneToOneElement->cascade);
393
                }
394
395 8
                if (isset($oneToOneElement['orphan-removal'])) {
396 3
                    $mapping['orphanRemoval'] = $this->evaluateBoolean($oneToOneElement['orphan-removal']);
397
                }
398
399
                // Evaluate second level cache
400 8 View Code Duplication
                if (isset($oneToOneElement->cache)) {
401
                    $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($oneToOneElement->cache));
402
                }
403
404 8
                $metadata->mapOneToOne($mapping);
405
            }
406
        }
407
408
        // Evaluate <one-to-many ...> mappings
409 36
        if (isset($xmlRoot->{'one-to-many'})) {
410 8
            foreach ($xmlRoot->{'one-to-many'} as $oneToManyElement) {
411
                $mapping = [
412 8
                    'fieldName' => (string) $oneToManyElement['field'],
413 8
                    'targetEntity' => (string) $oneToManyElement['target-entity'],
414 8
                    'mappedBy' => (string) $oneToManyElement['mapped-by']
415
                ];
416
417 8 View Code Duplication
                if (isset($oneToManyElement['fetch'])) {
418 2
                    $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string) $oneToManyElement['fetch']);
419
                }
420
421 8
                if (isset($oneToManyElement->cascade)) {
422 6
                    $mapping['cascade'] = $this->_getCascadeMappings($oneToManyElement->cascade);
423
                }
424
425 8
                if (isset($oneToManyElement['orphan-removal'])) {
426 6
                    $mapping['orphanRemoval'] = $this->evaluateBoolean($oneToManyElement['orphan-removal']);
427
                }
428
429 8 View Code Duplication
                if (isset($oneToManyElement->{'order-by'})) {
430 6
                    $orderBy = [];
431 6
                    foreach ($oneToManyElement->{'order-by'}->{'order-by-field'} as $orderByField) {
432 6
                        $orderBy[(string) $orderByField['name']] = (string) $orderByField['direction'];
433
                    }
434 6
                    $mapping['orderBy'] = $orderBy;
435
                }
436
437 8 View Code Duplication
                if (isset($oneToManyElement['index-by'])) {
438 3
                    $mapping['indexBy'] = (string) $oneToManyElement['index-by'];
439 5
                } else if (isset($oneToManyElement->{'index-by'})) {
440
                    throw new \InvalidArgumentException("<index-by /> is not a valid tag");
441
                }
442
443
                // Evaluate second level cache
444 8 View Code Duplication
                if (isset($oneToManyElement->cache)) {
445 1
                    $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($oneToManyElement->cache));
446
                }
447
448 8
                $metadata->mapOneToMany($mapping);
449
            }
450
        }
451
452
        // Evaluate <many-to-one ...> mappings
453 36
        if (isset($xmlRoot->{'many-to-one'})) {
454 7
            foreach ($xmlRoot->{'many-to-one'} as $manyToOneElement) {
455
                $mapping = [
456 7
                    'fieldName' => (string) $manyToOneElement['field'],
457 7
                    'targetEntity' => (string) $manyToOneElement['target-entity']
458
                ];
459
460 7
                if (isset($associationIds[$mapping['fieldName']])) {
461 2
                    $mapping['id'] = true;
462
                }
463
464 7 View Code Duplication
                if (isset($manyToOneElement['fetch'])) {
465 1
                    $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string) $manyToOneElement['fetch']);
466
                }
467
468 7
                if (isset($manyToOneElement['inversed-by'])) {
469 1
                    $mapping['inversedBy'] = (string) $manyToOneElement['inversed-by'];
470
                }
471
472 7
                $joinColumns = [];
473
474 7 View Code Duplication
                if (isset($manyToOneElement->{'join-column'})) {
475 4
                    $joinColumns[] = $this->joinColumnToArray($manyToOneElement->{'join-column'});
476 3
                } else if (isset($manyToOneElement->{'join-columns'})) {
477 2
                    foreach ($manyToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
478 2
                        $joinColumns[] = $this->joinColumnToArray($joinColumnElement);
479
                    }
480
                }
481
482 7
                $mapping['joinColumns'] = $joinColumns;
483
484 7
                if (isset($manyToOneElement->cascade)) {
485 2
                    $mapping['cascade'] = $this->_getCascadeMappings($manyToOneElement->cascade);
486
                }
487
488
                // Evaluate second level cache
489 7 View Code Duplication
                if (isset($manyToOneElement->cache)) {
490 1
                    $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($manyToOneElement->cache));
491
                }
492
493 7
                $metadata->mapManyToOne($mapping);
494
495
            }
496
        }
497
498
        // Evaluate <many-to-many ...> mappings
499 35
        if (isset($xmlRoot->{'many-to-many'})) {
500 12
            foreach ($xmlRoot->{'many-to-many'} as $manyToManyElement) {
501
                $mapping = [
502 12
                    'fieldName' => (string) $manyToManyElement['field'],
503 12
                    'targetEntity' => (string) $manyToManyElement['target-entity']
504
                ];
505
506 12 View Code Duplication
                if (isset($manyToManyElement['fetch'])) {
507 3
                    $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string) $manyToManyElement['fetch']);
508
                }
509
510 12
                if (isset($manyToManyElement['orphan-removal'])) {
511
                    $mapping['orphanRemoval'] = $this->evaluateBoolean($manyToManyElement['orphan-removal']);
512
                }
513
514 12
                if (isset($manyToManyElement['mapped-by'])) {
515 2
                    $mapping['mappedBy'] = (string) $manyToManyElement['mapped-by'];
516 10
                } else if (isset($manyToManyElement->{'join-table'})) {
517 8
                    if (isset($manyToManyElement['inversed-by'])) {
518 2
                        $mapping['inversedBy'] = (string) $manyToManyElement['inversed-by'];
519
                    }
520
521 8
                    $joinTableElement = $manyToManyElement->{'join-table'};
522
                    $joinTable = [
523 8
                        'name' => (string) $joinTableElement['name']
524
                    ];
525
526 8
                    if (isset($joinTableElement['schema'])) {
527
                        $joinTable['schema'] = (string) $joinTableElement['schema'];
528
                    }
529
530 8
                    foreach ($joinTableElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
531 8
                        $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement);
532
                    }
533
534 8
                    foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} as $joinColumnElement) {
535 8
                        $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement);
536
                    }
537
538 8
                    $mapping['joinTable'] = $joinTable;
539
                }
540
541 12
                if (isset($manyToManyElement->cascade)) {
542 8
                    $mapping['cascade'] = $this->_getCascadeMappings($manyToManyElement->cascade);
543
                }
544
545 12 View Code Duplication
                if (isset($manyToManyElement->{'order-by'})) {
546
                    $orderBy = [];
547
                    foreach ($manyToManyElement->{'order-by'}->{'order-by-field'} as $orderByField) {
548
                        $orderBy[(string) $orderByField['name']] = (string) $orderByField['direction'];
549
                    }
550
                    $mapping['orderBy'] = $orderBy;
551
                }
552
553 12 View Code Duplication
                if (isset($manyToManyElement['index-by'])) {
554
                    $mapping['indexBy'] = (string) $manyToManyElement['index-by'];
555 12
                } else if (isset($manyToManyElement->{'index-by'})) {
556
                    throw new \InvalidArgumentException("<index-by /> is not a valid tag");
557
                }
558
559
                // Evaluate second level cache
560 12 View Code Duplication
                if (isset($manyToManyElement->cache)) {
561
                    $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($manyToManyElement->cache));
562
                }
563
564 12
                $metadata->mapManyToMany($mapping);
565
            }
566
        }
567
568
        // Evaluate association-overrides
569 35
        if (isset($xmlRoot->{'attribute-overrides'})) {
570 2
            foreach ($xmlRoot->{'attribute-overrides'}->{'attribute-override'} as $overrideElement) {
571 2
                $fieldName = (string) $overrideElement['name'];
572 2
                foreach ($overrideElement->field as $field) {
573 2
                    $mapping = $this->columnToArray($field);
574 2
                    $mapping['fieldName'] = $fieldName;
575 2
                    $metadata->setAttributeOverride($fieldName, $mapping);
576
                }
577
            }
578
        }
579
580
        // Evaluate association-overrides
581 35
        if (isset($xmlRoot->{'association-overrides'})) {
582 4
            foreach ($xmlRoot->{'association-overrides'}->{'association-override'} as $overrideElement) {
583 4
                $fieldName  = (string) $overrideElement['name'];
584 4
                $override   = [];
585
586
                // Check for join-columns
587 4
                if (isset($overrideElement->{'join-columns'})) {
588 2
                    $joinColumns = [];
589 2
                    foreach ($overrideElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
590 2
                        $joinColumns[] = $this->joinColumnToArray($joinColumnElement);
591
                    }
592 2
                    $override['joinColumns'] = $joinColumns;
593
                }
594
595
                // Check for join-table
596 4
                if ($overrideElement->{'join-table'}) {
597 2
                    $joinTable          = null;
598 2
                    $joinTableElement   = $overrideElement->{'join-table'};
599
600
                    $joinTable = [
601 2
                        'name'      => (string) $joinTableElement['name'],
602 2
                        'schema'    => (string) $joinTableElement['schema']
603
                    ];
604
605 2
                    if (isset($joinTableElement->{'join-columns'})) {
606 2
                        foreach ($joinTableElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
607 2
                            $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement);
608
                        }
609
                    }
610
611 2
                    if (isset($joinTableElement->{'inverse-join-columns'})) {
612 2
                        foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} as $joinColumnElement) {
613 2
                            $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement);
614
                        }
615
                    }
616
617 2
                    $override['joinTable'] = $joinTable;
618
                }
619
620
                // Check for inversed-by
621 4
                if (isset($overrideElement->{'inversed-by'})) {
622 1
                    $override['inversedBy'] = (string) $overrideElement->{'inversed-by'}['name'];
623
                }
624
625
                // Check for `fetch`
626 4 View Code Duplication
                if (isset($overrideElement['fetch'])) {
627 1
                    $override['fetch'] = constant(Metadata::class . '::FETCH_' . (string) $overrideElement['fetch']);
628
                }
629
630 4
                $metadata->setAssociationOverride($fieldName, $override);
631
            }
632
        }
633
634
        // Evaluate <lifecycle-callbacks...>
635 35
        if (isset($xmlRoot->{'lifecycle-callbacks'})) {
636 5
            foreach ($xmlRoot->{'lifecycle-callbacks'}->{'lifecycle-callback'} as $lifecycleCallback) {
637 5
                $metadata->addLifecycleCallback((string) $lifecycleCallback['method'], constant('Doctrine\ORM\Events::' . (string) $lifecycleCallback['type']));
638
            }
639
        }
640
641
        // Evaluate entity listener
642 35
        if (isset($xmlRoot->{'entity-listeners'})) {
643 4
            foreach ($xmlRoot->{'entity-listeners'}->{'entity-listener'} as $listenerElement) {
644 4
                $className = (string) $listenerElement['class'];
645
                // Evaluate the listener using naming convention.
646 4
                if ($listenerElement->count() === 0) {
647 2
                    EntityListenerBuilder::bindEntityListener($metadata, $className);
648
649 2
                    continue;
650
                }
651
652 2
                foreach ($listenerElement as $callbackElement) {
653 2
                    $eventName   = (string) $callbackElement['type'];
654 2
                    $methodName  = (string) $callbackElement['method'];
655
656 2
                    $metadata->addEntityListener($eventName, $className, $methodName);
657
                }
658
            }
659
        }
660 35
    }
661
662
    /**
663
     * Parses (nested) option elements.
664
     *
665
     * @param SimpleXMLElement $options The XML element.
666
     *
667
     * @return array The options array.
668
     */
669 6
    private function _parseOptions(SimpleXMLElement $options)
670
    {
671 6
        $array = [];
672
673
        /* @var $option SimpleXMLElement */
674 6
        foreach ($options as $option) {
675 6
            if ($option->count()) {
676 5
                $value = $this->_parseOptions($option->children());
677
            } else {
678 6
                $value = (string) $option;
679
            }
680
681 6
            $attributes = $option->attributes();
682
683 6
            if (isset($attributes->name)) {
684 6
                $nameAttribute = (string) $attributes->name;
685 6
                $array[$nameAttribute] = in_array($nameAttribute, ['unsigned', 'fixed'])
686 5
                    ? $this->evaluateBoolean($value)
687 6
                    : $value;
688
            } else {
689 6
                $array[] = $value;
690
            }
691
        }
692
693 6
        return $array;
694
    }
695
696
    /**
697
     * Constructs a joinColumn mapping array based on the information
698
     * found in the given SimpleXMLElement.
699
     *
700
     * @param SimpleXMLElement $joinColumnElement The XML element.
701
     *
702
     * @return array The mapping array.
703
     */
704 13
    private function joinColumnToArray(SimpleXMLElement $joinColumnElement)
705
    {
706
        $joinColumn = [
707 13
            'name' => (string) $joinColumnElement['name'],
708 13
            'referencedColumnName' => (string) $joinColumnElement['referenced-column-name']
709
        ];
710
711 13
        if (isset($joinColumnElement['unique'])) {
712 4
            $joinColumn['unique'] = $this->evaluateBoolean($joinColumnElement['unique']);
713
        }
714
715 13
        if (isset($joinColumnElement['nullable'])) {
716 6
            $joinColumn['nullable'] = $this->evaluateBoolean($joinColumnElement['nullable']);
717
        }
718
719 13
        if (isset($joinColumnElement['on-delete'])) {
720 5
            $joinColumn['onDelete'] = (string) $joinColumnElement['on-delete'];
721
        }
722
723 13
        if (isset($joinColumnElement['column-definition'])) {
724 5
            $joinColumn['columnDefinition'] = (string) $joinColumnElement['column-definition'];
725
        }
726
727 13
        return $joinColumn;
728
    }
729
730
     /**
731
     * Parses the given field as array.
732
     *
733
     * @param SimpleXMLElement $fieldMapping
734
     *
735
     * @return array
736
     */
737 24
    private function columnToArray(SimpleXMLElement $fieldMapping)
738
    {
739
        $mapping = [
740 24
            'fieldName' => (string) $fieldMapping['name'],
741
        ];
742
743 24
        if (isset($fieldMapping['type'])) {
744 20
            $mapping['type'] = (string) $fieldMapping['type'];
745
        }
746
747 24
        if (isset($fieldMapping['column'])) {
748 16
            $mapping['columnName'] = (string) $fieldMapping['column'];
749
        }
750
751 24
        if (isset($fieldMapping['length'])) {
752 11
            $mapping['length'] = (int) $fieldMapping['length'];
753
        }
754
755 24
        if (isset($fieldMapping['precision'])) {
756 1
            $mapping['precision'] = (int) $fieldMapping['precision'];
757
        }
758
759 24
        if (isset($fieldMapping['scale'])) {
760 1
            $mapping['scale'] = (int) $fieldMapping['scale'];
761
        }
762
763 24
        if (isset($fieldMapping['unique'])) {
764 10
            $mapping['unique'] = $this->evaluateBoolean($fieldMapping['unique']);
765
        }
766
767 24
        if (isset($fieldMapping['nullable'])) {
768 9
            $mapping['nullable'] = $this->evaluateBoolean($fieldMapping['nullable']);
769
        }
770
771 24
        if (isset($fieldMapping['version']) && $fieldMapping['version']) {
772 3
            $mapping['version'] = $this->evaluateBoolean($fieldMapping['version']);
773
        }
774
775 24
        if (isset($fieldMapping['column-definition'])) {
776 6
            $mapping['columnDefinition'] = (string) $fieldMapping['column-definition'];
777
        }
778
779 24
        if (isset($fieldMapping->options)) {
780 5
            $mapping['options'] = $this->_parseOptions($fieldMapping->options->children());
781
        }
782
783 24
        return $mapping;
784
    }
785
786
    /**
787
     * Parse / Normalize the cache configuration
788
     *
789
     * @param SimpleXMLElement $cacheMapping
790
     *
791
     * @return array
792
     */
793 2 View Code Duplication
    private function cacheToArray(SimpleXMLElement $cacheMapping)
794
    {
795 2
        $region = isset($cacheMapping['region']) ? (string) $cacheMapping['region'] : null;
796 2
        $usage  = isset($cacheMapping['usage']) ? strtoupper($cacheMapping['usage']) : null;
797
798 2
        if ($usage && ! defined('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $usage)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $usage of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
799
            throw new \InvalidArgumentException(sprintf('Invalid cache usage "%s"', $usage));
800
        }
801
802 2
        if ($usage) {
803 2
            $usage = constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $usage);
804
        }
805
806
        return [
807 2
            'usage'  => $usage,
808 2
            'region' => $region,
809
        ];
810
    }
811
812
    /**
813
     * Gathers a list of cascade options found in the given cascade element.
814
     *
815
     * @param SimpleXMLElement $cascadeElement The cascade element.
816
     *
817
     * @return array The list of cascade options.
818
     */
819 8
    private function _getCascadeMappings(SimpleXMLElement $cascadeElement)
820
    {
821 8
        $cascades = [];
822
        /* @var $action SimpleXmlElement */
823 8
        foreach ($cascadeElement->children() as $action) {
824
            // According to the JPA specifications, XML uses "cascade-persist"
825
            // instead of "persist". Here, both variations
826
            // are supported because both YAML and Annotation use "persist"
827
            // and we want to make sure that this driver doesn't need to know
828
            // anything about the supported cascading actions
829 8
            $cascades[] = str_replace('cascade-', '', $action->getName());
830
        }
831
832 8
        return $cascades;
833
    }
834
835
    /**
836
     * {@inheritDoc}
837
     */
838 38
    protected function loadMappingFile($file)
839
    {
840 38
        $result = [];
841 38
        $xmlElement = simplexml_load_file($file);
842
843 38
        if (isset($xmlElement->entity)) {
844 36
            foreach ($xmlElement->entity as $entityElement) {
845 36
                $entityName = (string) $entityElement['name'];
846 36
                $result[$entityName] = $entityElement;
847
            }
848 9
        } else if (isset($xmlElement->{'mapped-superclass'})) {
849 5
            foreach ($xmlElement->{'mapped-superclass'} as $mappedSuperClass) {
850 5
                $className = (string) $mappedSuperClass['name'];
851 5
                $result[$className] = $mappedSuperClass;
852
            }
853 4
        } else if (isset($xmlElement->embeddable)) {
854 3
            foreach ($xmlElement->embeddable as $embeddableElement) {
855 3
                $embeddableName = (string) $embeddableElement['name'];
856 3
                $result[$embeddableName] = $embeddableElement;
857
            }
858
        }
859
860 38
        return $result;
861
    }
862
863
    /**
864
     * @param mixed $element
865
     *
866
     * @return bool
867
     */
868 14
    protected function evaluateBoolean($element)
869
    {
870 14
        $flag = (string) $element;
871
872 14
        return ($flag == "true" || $flag == "1");
873
    }
874
}
875