Completed
Push — master ( 049ad1...c0c08d )
by Marco
19s
created

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

Labels
Severity

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 41
    public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION)
48
    {
49 41
        parent::__construct($locator, $fileExtension);
50 41
    }
51
52
    /**
53
     * {@inheritDoc}
54
     */
55 36
    public function loadMetadataForClass($className, ClassMetadata $metadata)
56
    {
57
        /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */
58
        /* @var $xmlRoot SimpleXMLElement */
59 36
        $xmlRoot = $this->getElement($className);
60
61 34
        if ($xmlRoot->getName() == 'entity') {
62 33
            if (isset($xmlRoot['repository-class'])) {
63
                $metadata->setCustomRepositoryClass((string) $xmlRoot['repository-class']);
64
            }
65 33
            if (isset($xmlRoot['read-only']) && $this->evaluateBoolean($xmlRoot['read-only'])) {
66 33
                $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
        } else if ($xmlRoot->getName() == 'embeddable') {
74 3
            $metadata->isEmbeddedClass = true;
75
        } else {
76
            throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
77
        }
78
79
        // Evaluate <entity...> attributes
80 34
        $primaryTable = [];
81
82 34
        if (isset($xmlRoot['table'])) {
83 13
            $primaryTable['name'] = (string) $xmlRoot['table'];
84
        }
85
86 34
        if (isset($xmlRoot['schema'])) {
87 1
            $primaryTable['schema'] = (string) $xmlRoot['schema'];
88
        }
89
90 34
        $metadata->setPrimaryTable($primaryTable);
91
92
        // Evaluate second level cache
93 34
        if (isset($xmlRoot->cache)) {
94 2
            $metadata->enableCache($this->cacheToArray($xmlRoot->cache));
95
        }
96
97
        // Evaluate named queries
98 34
        if (isset($xmlRoot->{'named-queries'})) {
99 2
            foreach ($xmlRoot->{'named-queries'}->{'named-query'} as $namedQueryElement) {
100 2
                $metadata->addNamedQuery(
101
                    [
102 2
                        'name'  => (string) $namedQueryElement['name'],
103 2
                        'query' => (string) $namedQueryElement['query']
104
                    ]
105
                );
106
            }
107
        }
108
109
        // Evaluate native named queries
110 34
        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 34
        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
                        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 34
        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 != \Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) {
0 ignored issues
show
Accessing inheritanceType on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
170
                // Evaluate <discriminator-column...>
171 10
                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 34
        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 34
        if (isset($xmlRoot->indexes)) {
205 2
            $metadata->table['indexes'] = [];
206 2
            foreach ($xmlRoot->indexes->index as $indexXml) {
207 2
                $index = ['columns' => explode(',', (string) $indexXml['columns'])];
208
209 2
                if (isset($indexXml['flags'])) {
210 1
                    $index['flags'] = explode(',', (string) $indexXml['flags']);
211
                }
212
213 2
                if (isset($indexXml->options)) {
214 1
                    $index['options'] = $this->_parseOptions($indexXml->options->children());
215
                }
216
217 2
                if (isset($indexXml['name'])) {
218 1
                    $metadata->table['indexes'][(string) $indexXml['name']] = $index;
219
                } else {
220 2
                    $metadata->table['indexes'][] = $index;
221
                }
222
            }
223
        }
224
225
        // Evaluate <unique-constraints..>
226 34
        if (isset($xmlRoot->{'unique-constraints'})) {
227 1
            $metadata->table['uniqueConstraints'] = [];
228 1
            foreach ($xmlRoot->{'unique-constraints'}->{'unique-constraint'} as $uniqueXml) {
229 1
                $unique = ['columns' => explode(',', (string) $uniqueXml['columns'])];
230
231 1
                if (isset($uniqueXml->options)) {
232 1
                    $unique['options'] = $this->_parseOptions($uniqueXml->options->children());
233
                }
234
235 1
                if (isset($uniqueXml['name'])) {
236 1
                    $metadata->table['uniqueConstraints'][(string) $uniqueXml['name']] = $unique;
237
                } else {
238 1
                    $metadata->table['uniqueConstraints'][] = $unique;
239
                }
240
            }
241
        }
242
243 34
        if (isset($xmlRoot->options)) {
244 3
            $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 34
        $mappings = [];
250
        // Evaluate <field ...> mappings
251 34
        if (isset($xmlRoot->field)) {
252 22
            foreach ($xmlRoot->field as $fieldMapping) {
253 22
                $mapping = $this->columnToArray($fieldMapping);
254
255 22
                if (isset($mapping['version'])) {
256 1
                    $metadata->setVersionMapping($mapping);
257 1
                    unset($mapping['version']);
258
                }
259
260 22
                $metadata->mapField($mapping);
261
            }
262
        }
263
264 34
        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 34
        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 34
        $associationIds = [];
294 34
        foreach ($xmlRoot->id as $idElement) {
295 31
            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 30
                'id' => true,
302 30
                'fieldName' => (string) $idElement['name']
303
            ];
304
305 30
            if (isset($idElement['type'])) {
306 19
                $mapping['type'] = (string) $idElement['type'];
307
            }
308
309 30
            if (isset($idElement['length'])) {
310 3
                $mapping['length'] = (string) $idElement['length'];
311
            }
312
313 30
            if (isset($idElement['column'])) {
314 21
                $mapping['columnName'] = (string) $idElement['column'];
315
            }
316
317 30
            if (isset($idElement['column-definition'])) {
318 1
                $mapping['columnDefinition'] = (string) $idElement['column-definition'];
319
            }
320
321 30
            if (isset($idElement->options)) {
322 1
                $mapping['options'] = $this->_parseOptions($idElement->options->children());
323
            }
324
325 30
            $metadata->mapField($mapping);
326
327 30
            if (isset($idElement->generator)) {
328 29
                $strategy = isset($idElement->generator['strategy']) ?
329 29
                        (string) $idElement->generator['strategy'] : 'AUTO';
330 29
                $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_'
331 29
                    . $strategy));
332
            }
333
334
            // Check for SequenceGenerator/TableGenerator definition
335 30
            if (isset($idElement->{'sequence-generator'})) {
336 1
                $seqGenerator = $idElement->{'sequence-generator'};
337 1
                $metadata->setSequenceGeneratorDefinition(
338
                    [
339 1
                        'sequenceName' => (string) $seqGenerator['sequence-name'],
340 1
                        'allocationSize' => (string) $seqGenerator['allocation-size'],
341 1
                        '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 30
                throw MappingException::tableIdGeneratorNotImplemented($className);
353
            }
354
        }
355
356
        // Evaluate <one-to-one ...> mappings
357 34
        if (isset($xmlRoot->{'one-to-one'})) {
358 6
            foreach ($xmlRoot->{'one-to-one'} as $oneToOneElement) {
359
                $mapping = [
360 6
                    'fieldName' => (string) $oneToOneElement['field'],
361 6
                    'targetEntity' => (string) $oneToOneElement['target-entity']
362
                ];
363
364 6
                if (isset($associationIds[$mapping['fieldName']])) {
365
                    $mapping['id'] = true;
366
                }
367
368 6
                if (isset($oneToOneElement['fetch'])) {
369 2
                    $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string) $oneToOneElement['fetch']);
370
                }
371
372 6
                if (isset($oneToOneElement['mapped-by'])) {
373 2
                    $mapping['mappedBy'] = (string) $oneToOneElement['mapped-by'];
374
                } else {
375 6
                    if (isset($oneToOneElement['inversed-by'])) {
376 6
                        $mapping['inversedBy'] = (string) $oneToOneElement['inversed-by'];
377
                    }
378 6
                    $joinColumns = [];
379
380 6
                    if (isset($oneToOneElement->{'join-column'})) {
381 5
                        $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 6
                    $mapping['joinColumns'] = $joinColumns;
389
                }
390
391 6
                if (isset($oneToOneElement->cascade)) {
392 4
                    $mapping['cascade'] = $this->_getCascadeMappings($oneToOneElement->cascade);
393
                }
394
395 6
                if (isset($oneToOneElement['orphan-removal'])) {
396 3
                    $mapping['orphanRemoval'] = $this->evaluateBoolean($oneToOneElement['orphan-removal']);
397
                }
398
399
                // Evaluate second level cache
400 6
                if (isset($oneToOneElement->cache)) {
401
                    $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($oneToOneElement->cache));
402
                }
403
404 6
                $metadata->mapOneToOne($mapping);
405
            }
406
        }
407
408
        // Evaluate <one-to-many ...> mappings
409 34
        if (isset($xmlRoot->{'one-to-many'})) {
410 6
            foreach ($xmlRoot->{'one-to-many'} as $oneToManyElement) {
411
                $mapping = [
412 6
                    'fieldName' => (string) $oneToManyElement['field'],
413 6
                    'targetEntity' => (string) $oneToManyElement['target-entity'],
414 6
                    'mappedBy' => (string) $oneToManyElement['mapped-by']
415
                ];
416
417 6
                if (isset($oneToManyElement['fetch'])) {
418 2
                    $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string) $oneToManyElement['fetch']);
419
                }
420
421 6
                if (isset($oneToManyElement->cascade)) {
422 4
                    $mapping['cascade'] = $this->_getCascadeMappings($oneToManyElement->cascade);
423
                }
424
425 6
                if (isset($oneToManyElement['orphan-removal'])) {
426 4
                    $mapping['orphanRemoval'] = $this->evaluateBoolean($oneToManyElement['orphan-removal']);
427
                }
428
429 6
                if (isset($oneToManyElement->{'order-by'})) {
430 4
                    $orderBy = [];
431 4
                    foreach ($oneToManyElement->{'order-by'}->{'order-by-field'} as $orderByField) {
432 4
                        $orderBy[(string) $orderByField['name']] = (string) $orderByField['direction'];
433
                    }
434 4
                    $mapping['orderBy'] = $orderBy;
435
                }
436
437 6
                if (isset($oneToManyElement['index-by'])) {
438 1
                    $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 6
                if (isset($oneToManyElement->cache)) {
445 1
                    $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($oneToManyElement->cache));
446
                }
447
448 6
                $metadata->mapOneToMany($mapping);
449
            }
450
        }
451
452
        // Evaluate <many-to-one ...> mappings
453 34
        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
                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
                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
                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 33
        if (isset($xmlRoot->{'many-to-many'})) {
500 10
            foreach ($xmlRoot->{'many-to-many'} as $manyToManyElement) {
501
                $mapping = [
502 10
                    'fieldName' => (string) $manyToManyElement['field'],
503 10
                    'targetEntity' => (string) $manyToManyElement['target-entity']
504
                ];
505
506 10
                if (isset($manyToManyElement['fetch'])) {
507 3
                    $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string) $manyToManyElement['fetch']);
508
                }
509
510 10
                if (isset($manyToManyElement['orphan-removal'])) {
511
                    $mapping['orphanRemoval'] = $this->evaluateBoolean($manyToManyElement['orphan-removal']);
512
                }
513
514 10
                if (isset($manyToManyElement['mapped-by'])) {
515 2
                    $mapping['mappedBy'] = (string) $manyToManyElement['mapped-by'];
516 8
                } else if (isset($manyToManyElement->{'join-table'})) {
517 6
                    if (isset($manyToManyElement['inversed-by'])) {
518 2
                        $mapping['inversedBy'] = (string) $manyToManyElement['inversed-by'];
519
                    }
520
521 6
                    $joinTableElement = $manyToManyElement->{'join-table'};
522
                    $joinTable = [
523 6
                        'name' => (string) $joinTableElement['name']
524
                    ];
525
526 6
                    if (isset($joinTableElement['schema'])) {
527
                        $joinTable['schema'] = (string) $joinTableElement['schema'];
528
                    }
529
530 6
                    foreach ($joinTableElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
531 6
                        $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement);
532
                    }
533
534 6
                    foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} as $joinColumnElement) {
535 6
                        $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement);
536
                    }
537
538 6
                    $mapping['joinTable'] = $joinTable;
539
                }
540
541 10
                if (isset($manyToManyElement->cascade)) {
542 6
                    $mapping['cascade'] = $this->_getCascadeMappings($manyToManyElement->cascade);
543
                }
544
545 10
                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 10
                if (isset($manyToManyElement['index-by'])) {
554
                    $mapping['indexBy'] = (string) $manyToManyElement['index-by'];
555 10
                } 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 10
                if (isset($manyToManyElement->cache)) {
561
                    $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($manyToManyElement->cache));
562
                }
563
564 10
                $metadata->mapManyToMany($mapping);
565
            }
566
        }
567
568
        // Evaluate association-overrides
569 33
        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 33
        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
                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 33
        if (isset($xmlRoot->{'lifecycle-callbacks'})) {
636 3
            foreach ($xmlRoot->{'lifecycle-callbacks'}->{'lifecycle-callback'} as $lifecycleCallback) {
637 3
                $metadata->addLifecycleCallback((string) $lifecycleCallback['method'], constant('Doctrine\ORM\Events::' . (string) $lifecycleCallback['type']));
638
            }
639
        }
640
641
        // Evaluate entity listener
642 33
        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 33
    }
661
662
    /**
663
     * Parses (nested) option elements.
664
     *
665
     * @param SimpleXMLElement $options The XML element.
666
     *
667
     * @return array The options array.
668
     */
669 4
    private function _parseOptions(SimpleXMLElement $options)
670
    {
671 4
        $array = [];
672
673
        /* @var $option SimpleXMLElement */
674 4
        foreach ($options as $option) {
675 4
            if ($option->count()) {
676 3
                $value = $this->_parseOptions($option->children());
677
            } else {
678 4
                $value = (string) $option;
679
            }
680
681 4
            $attributes = $option->attributes();
682
683 4
            if (isset($attributes->name)) {
684 4
                $nameAttribute = (string) $attributes->name;
685 4
                $array[$nameAttribute] = in_array($nameAttribute, ['unsigned', 'fixed'])
686 3
                    ? $this->evaluateBoolean($value)
687 4
                    : $value;
688
            } else {
689 4
                $array[] = $value;
690
            }
691
        }
692
693 4
        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 11
    private function joinColumnToArray(SimpleXMLElement $joinColumnElement)
705
    {
706
        $joinColumn = [
707 11
            'name' => (string) $joinColumnElement['name'],
708 11
            'referencedColumnName' => (string) $joinColumnElement['referenced-column-name']
709
        ];
710
711 11
        if (isset($joinColumnElement['unique'])) {
712 2
            $joinColumn['unique'] = $this->evaluateBoolean($joinColumnElement['unique']);
713
        }
714
715 11
        if (isset($joinColumnElement['nullable'])) {
716 4
            $joinColumn['nullable'] = $this->evaluateBoolean($joinColumnElement['nullable']);
717
        }
718
719 11
        if (isset($joinColumnElement['on-delete'])) {
720 3
            $joinColumn['onDelete'] = (string) $joinColumnElement['on-delete'];
721
        }
722
723 11
        if (isset($joinColumnElement['column-definition'])) {
724 3
            $joinColumn['columnDefinition'] = (string) $joinColumnElement['column-definition'];
725
        }
726
727 11
        return $joinColumn;
728
    }
729
730
     /**
731
     * Parses the given field as array.
732
     *
733
     * @param SimpleXMLElement $fieldMapping
734
     *
735
     * @return array
736
     */
737 22
    private function columnToArray(SimpleXMLElement $fieldMapping)
738
    {
739
        $mapping = [
740 22
            'fieldName' => (string) $fieldMapping['name'],
741
        ];
742
743 22
        if (isset($fieldMapping['type'])) {
744 18
            $mapping['type'] = (string) $fieldMapping['type'];
745
        }
746
747 22
        if (isset($fieldMapping['column'])) {
748 14
            $mapping['columnName'] = (string) $fieldMapping['column'];
749
        }
750
751 22
        if (isset($fieldMapping['length'])) {
752 9
            $mapping['length'] = (int) $fieldMapping['length'];
753
        }
754
755 22
        if (isset($fieldMapping['precision'])) {
756 1
            $mapping['precision'] = (int) $fieldMapping['precision'];
757
        }
758
759 22
        if (isset($fieldMapping['scale'])) {
760 1
            $mapping['scale'] = (int) $fieldMapping['scale'];
761
        }
762
763 22
        if (isset($fieldMapping['unique'])) {
764 8
            $mapping['unique'] = $this->evaluateBoolean($fieldMapping['unique']);
765
        }
766
767 22
        if (isset($fieldMapping['nullable'])) {
768 7
            $mapping['nullable'] = $this->evaluateBoolean($fieldMapping['nullable']);
769
        }
770
771 22
        if (isset($fieldMapping['version']) && $fieldMapping['version']) {
772 1
            $mapping['version'] = $this->evaluateBoolean($fieldMapping['version']);
773
        }
774
775 22
        if (isset($fieldMapping['column-definition'])) {
776 4
            $mapping['columnDefinition'] = (string) $fieldMapping['column-definition'];
777
        }
778
779 22
        if (isset($fieldMapping->options)) {
780 3
            $mapping['options'] = $this->_parseOptions($fieldMapping->options->children());
781
        }
782
783 22
        return $mapping;
784
    }
785
786
    /**
787
     * Parse / Normalize the cache configuration
788
     *
789
     * @param SimpleXMLElement $cacheMapping
790
     *
791
     * @return array
792
     */
793 2
    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)) {
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 6
    private function _getCascadeMappings(SimpleXMLElement $cascadeElement)
820
    {
821 6
        $cascades = [];
822
        /* @var $action SimpleXmlElement */
823 6
        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 6
            $cascades[] = str_replace('cascade-', '', $action->getName());
830
        }
831
832 6
        return $cascades;
833
    }
834
835
    /**
836
     * {@inheritDoc}
837
     */
838 36
    protected function loadMappingFile($file)
839
    {
840 36
        $result = [];
841 36
        $xmlElement = simplexml_load_file($file);
842
843 36
        if (isset($xmlElement->entity)) {
844 34
            foreach ($xmlElement->entity as $entityElement) {
845 34
                $entityName = (string) $entityElement['name'];
846 34
                $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 36
        return $result;
861
    }
862
863
    /**
864
     * @param mixed $element
865
     *
866
     * @return bool
867
     */
868 12
    protected function evaluateBoolean($element)
869
    {
870 12
        $flag = (string) $element;
871
872 12
        return ($flag == "true" || $flag == "1");
873
    }
874
}
875