Passed
Pull Request — 2.6 (#7317)
by Alex
08:53
created

XmlDriver::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
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 Doctrine\Common\Collections\Criteria;
23
use SimpleXMLElement;
24
use Doctrine\Common\Persistence\Mapping\Driver\FileDriver;
25
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
26
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
27
use Doctrine\ORM\Mapping\MappingException;
28
use Doctrine\ORM\Mapping\ClassMetadata as Metadata;
29
30
/**
31
 * XmlDriver is a metadata driver that enables mapping through XML files.
32
 *
33
 * @license 	http://www.opensource.org/licenses/mit-license.php MIT
34
 * @link    	www.doctrine-project.org
35
 * @since   	2.0
36
 * @author		Benjamin Eberlei <[email protected]>
37
 * @author		Guilherme Blanco <[email protected]>
38
 * @author      Jonathan H. Wage <[email protected]>
39
 * @author      Roman Borschel <[email protected]>
40
 */
41
class XmlDriver extends FileDriver
42
{
43
    const DEFAULT_FILE_EXTENSION = '.dcm.xml';
44
45
    /**
46
     * {@inheritDoc}
47
     */
48 46
    public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION)
49
    {
50 46
        parent::__construct($locator, $fileExtension);
51 46
    }
52
53
    /**
54
     * {@inheritDoc}
55
     */
56 41
    public function loadMetadataForClass($className, ClassMetadata $metadata)
57
    {
58
        /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */
59
        /* @var $xmlRoot SimpleXMLElement */
60 41
        $xmlRoot = $this->getElement($className);
61
62 39
        if ($xmlRoot->getName() == 'entity') {
63 38
            if (isset($xmlRoot['repository-class'])) {
64
                $metadata->setCustomRepositoryClass((string) $xmlRoot['repository-class']);
65
            }
66 38
            if (isset($xmlRoot['read-only']) && $this->evaluateBoolean($xmlRoot['read-only'])) {
67 38
                $metadata->markReadOnly();
68
            }
69 8
        } else if ($xmlRoot->getName() == 'mapped-superclass') {
70 5
            $metadata->setCustomRepositoryClass(
71 5
                isset($xmlRoot['repository-class']) ? (string) $xmlRoot['repository-class'] : null
72
            );
73 5
            $metadata->isMappedSuperclass = true;
0 ignored issues
show
Bug introduced by
Accessing isMappedSuperclass on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
74 3
        } else if ($xmlRoot->getName() == 'embeddable') {
75 3
            $metadata->isEmbeddedClass = true;
0 ignored issues
show
Bug introduced by
Accessing isEmbeddedClass on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
76
        } else {
77
            throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
78
        }
79
80
        // Evaluate <entity...> attributes
81 39
        $primaryTable = [];
82
83 39
        if (isset($xmlRoot['table'])) {
84 16
            $primaryTable['name'] = (string) $xmlRoot['table'];
85
        }
86
87 39
        if (isset($xmlRoot['schema'])) {
88 1
            $primaryTable['schema'] = (string) $xmlRoot['schema'];
89
        }
90
91 39
        $metadata->setPrimaryTable($primaryTable);
92
93
        // Evaluate second level cache
94 39
        if (isset($xmlRoot->cache)) {
95 2
            $metadata->enableCache($this->cacheToArray($xmlRoot->cache));
96
        }
97
98
        // Evaluate named queries
99 39
        if (isset($xmlRoot->{'named-queries'})) {
100 4
            foreach ($xmlRoot->{'named-queries'}->{'named-query'} as $namedQueryElement) {
101 4
                $metadata->addNamedQuery(
102
                    [
103 4
                        'name'  => (string) $namedQueryElement['name'],
104 4
                        'query' => (string) $namedQueryElement['query']
105
                    ]
106
                );
107
            }
108
        }
109
110
        // Evaluate native named queries
111 39
        if (isset($xmlRoot->{'named-native-queries'})) {
112 3
            foreach ($xmlRoot->{'named-native-queries'}->{'named-native-query'} as $nativeQueryElement) {
113 3
                $metadata->addNamedNativeQuery(
114
                    [
115 3
                        'name'              => isset($nativeQueryElement['name']) ? (string) $nativeQueryElement['name'] : null,
116 3
                        'query'             => isset($nativeQueryElement->query) ? (string) $nativeQueryElement->query : null,
117 3
                        'resultClass'       => isset($nativeQueryElement['result-class']) ? (string) $nativeQueryElement['result-class'] : null,
118 3
                        'resultSetMapping'  => isset($nativeQueryElement['result-set-mapping']) ? (string) $nativeQueryElement['result-set-mapping'] : null,
119
                    ]
120
                );
121
            }
122
        }
123
124
        // Evaluate sql result set mapping
125 39
        if (isset($xmlRoot->{'sql-result-set-mappings'})) {
126 3
            foreach ($xmlRoot->{'sql-result-set-mappings'}->{'sql-result-set-mapping'} as $rsmElement) {
127 3
                $entities   = [];
128 3
                $columns    = [];
129 3
                foreach ($rsmElement as $entityElement) {
130
                    //<entity-result/>
131 3
                    if (isset($entityElement['entity-class'])) {
132
                        $entityResult = [
133 3
                            'fields'                => [],
134 3
                            'entityClass'           => (string) $entityElement['entity-class'],
135 3
                            'discriminatorColumn'   => isset($entityElement['discriminator-column']) ? (string) $entityElement['discriminator-column'] : null,
136
                        ];
137
138 3
                        foreach ($entityElement as $fieldElement) {
139 3
                            $entityResult['fields'][] = [
140 3
                                'name'      => isset($fieldElement['name']) ? (string) $fieldElement['name'] : null,
141 3
                                'column'    => isset($fieldElement['column']) ? (string) $fieldElement['column'] : null,
142
                            ];
143
                        }
144
145 3
                        $entities[] = $entityResult;
146
                    }
147
148
                    //<column-result/>
149 3
                    if (isset($entityElement['name'])) {
150 3
                        $columns[] = [
151 3
                            'name' => (string) $entityElement['name'],
152
                        ];
153
                    }
154
                }
155
156 3
                $metadata->addSqlResultSetMapping(
157
                    [
158 3
                        'name'          => (string) $rsmElement['name'],
159 3
                        'entities'      => $entities,
160 3
                        'columns'       => $columns
161
                    ]
162
                );
163
            }
164
        }
165
166 39
        if (isset($xmlRoot['inheritance-type'])) {
167 10
            $inheritanceType = (string) $xmlRoot['inheritance-type'];
168 10
            $metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceType));
169
170 10
            if ($metadata->inheritanceType != Metadata::INHERITANCE_TYPE_NONE) {
0 ignored issues
show
Bug introduced by
Accessing inheritanceType on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
171
                // Evaluate <discriminator-column...>
172 10
                if (isset($xmlRoot->{'discriminator-column'})) {
173 7
                    $discrColumn = $xmlRoot->{'discriminator-column'};
174 7
                    $metadata->setDiscriminatorColumn(
175
                        [
176 7
                            'name' => isset($discrColumn['name']) ? (string) $discrColumn['name'] : null,
177 7
                            'type' => isset($discrColumn['type']) ? (string) $discrColumn['type'] : 'string',
178 7
                            'length' => isset($discrColumn['length']) ? (string) $discrColumn['length'] : 255,
179 7
                            'columnDefinition' => isset($discrColumn['column-definition']) ? (string) $discrColumn['column-definition'] : null
180
                        ]
181
                    );
182
                } else {
183 6
                    $metadata->setDiscriminatorColumn(['name' => 'dtype', 'type' => 'string', 'length' => 255]);
184
                }
185
186
                // Evaluate <discriminator-map...>
187 10
                if (isset($xmlRoot->{'discriminator-map'})) {
188 10
                    $map = [];
189 10
                    foreach ($xmlRoot->{'discriminator-map'}->{'discriminator-mapping'} as $discrMapElement) {
190 10
                        $map[(string) $discrMapElement['value']] = (string) $discrMapElement['class'];
191
                    }
192 10
                    $metadata->setDiscriminatorMap($map);
193
                }
194
            }
195
        }
196
197
198
        // Evaluate <change-tracking-policy...>
199 39
        if (isset($xmlRoot['change-tracking-policy'])) {
200
            $metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_'
201
                . strtoupper((string) $xmlRoot['change-tracking-policy'])));
202
        }
203
204
        // Evaluate <indexes...>
205 39
        if (isset($xmlRoot->indexes)) {
206 4
            $metadata->table['indexes'] = [];
0 ignored issues
show
Bug introduced by
Accessing table on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
207 4
            foreach ($xmlRoot->indexes->index as $indexXml) {
208 4
                $index = ['columns' => explode(',', (string) $indexXml['columns'])];
209
210 4
                if (isset($indexXml['flags'])) {
211 1
                    $index['flags'] = explode(',', (string) $indexXml['flags']);
212
                }
213
214 4
                if (isset($indexXml->options)) {
215 1
                    $index['options'] = $this->_parseOptions($indexXml->options->children());
216
                }
217
218 4
                if (isset($indexXml['name'])) {
219 3
                    $metadata->table['indexes'][(string) $indexXml['name']] = $index;
220
                } else {
221 4
                    $metadata->table['indexes'][] = $index;
222
                }
223
            }
224
        }
225
226
        // Evaluate <unique-constraints..>
227 39
        if (isset($xmlRoot->{'unique-constraints'})) {
228 3
            $metadata->table['uniqueConstraints'] = [];
229 3
            foreach ($xmlRoot->{'unique-constraints'}->{'unique-constraint'} as $uniqueXml) {
230 3
                $unique = ['columns' => explode(',', (string) $uniqueXml['columns'])];
231
232 3
                if (isset($uniqueXml->options)) {
233 3
                    $unique['options'] = $this->_parseOptions($uniqueXml->options->children());
234
                }
235
236 3
                if (isset($uniqueXml['name'])) {
237 3
                    $metadata->table['uniqueConstraints'][(string) $uniqueXml['name']] = $unique;
238
                } else {
239 3
                    $metadata->table['uniqueConstraints'][] = $unique;
240
                }
241
            }
242
        }
243
244 39
        if (isset($xmlRoot->options)) {
245 5
            $metadata->table['options'] = $this->_parseOptions($xmlRoot->options->children());
246
        }
247
248
        // The mapping assignment is done in 2 times as a bug might occurs on some php/xml lib versions
249
        // The internal SimpleXmlIterator get resetted, to this generate a duplicate field exception
250 39
        $mappings = [];
251
        // Evaluate <field ...> mappings
252 39
        if (isset($xmlRoot->field)) {
253 24
            foreach ($xmlRoot->field as $fieldMapping) {
254 24
                $mapping = $this->columnToArray($fieldMapping);
255
256 24
                if (isset($mapping['version'])) {
257 3
                    $metadata->setVersionMapping($mapping);
258 3
                    unset($mapping['version']);
259
                }
260
261 24
                $metadata->mapField($mapping);
262
            }
263
        }
264
265 39
        if (isset($xmlRoot->embedded)) {
266 3
            foreach ($xmlRoot->embedded as $embeddedMapping) {
267 3
                $columnPrefix = isset($embeddedMapping['column-prefix'])
268 3
                    ? (string) $embeddedMapping['column-prefix']
269 3
                    : null;
270
271 3
                $useColumnPrefix = isset($embeddedMapping['use-column-prefix'])
272 2
                    ? $this->evaluateBoolean($embeddedMapping['use-column-prefix'])
273 3
                    : true;
274
275
                $mapping = [
276 3
                    'fieldName' => (string) $embeddedMapping['name'],
277 3
                    'class' => (string) $embeddedMapping['class'],
278 3
                    'columnPrefix' => $useColumnPrefix ? $columnPrefix : false
279
                ];
280
281 3
                $metadata->mapEmbedded($mapping);
282
            }
283
        }
284
285 39
        foreach ($mappings as $mapping) {
286
            if (isset($mapping['version'])) {
287
                $metadata->setVersionMapping($mapping);
288
            }
289
290
            $metadata->mapField($mapping);
291
        }
292
293
        // Evaluate <id ...> mappings
294 39
        $associationIds = [];
295 39
        foreach ($xmlRoot->id as $idElement) {
296 34
            if (isset($idElement['association-key']) && $this->evaluateBoolean($idElement['association-key'])) {
297 2
                $associationIds[(string) $idElement['name']] = true;
298 2
                continue;
299
            }
300
301
            $mapping = [
302 33
                'id' => true,
303 33
                'fieldName' => (string) $idElement['name']
304
            ];
305
306 33
            if (isset($idElement['type'])) {
307 21
                $mapping['type'] = (string) $idElement['type'];
308
            }
309
310 33
            if (isset($idElement['length'])) {
311 3
                $mapping['length'] = (string) $idElement['length'];
312
            }
313
314 33
            if (isset($idElement['column'])) {
315 24
                $mapping['columnName'] = (string) $idElement['column'];
316
            }
317
318 33
            if (isset($idElement['column-definition'])) {
319 1
                $mapping['columnDefinition'] = (string) $idElement['column-definition'];
320
            }
321
322 33
            if (isset($idElement->options)) {
323 3
                $mapping['options'] = $this->_parseOptions($idElement->options->children());
324
            }
325
326 33
            $metadata->mapField($mapping);
327
328 33
            if (isset($idElement->generator)) {
329 32
                $strategy = isset($idElement->generator['strategy']) ?
330 32
                        (string) $idElement->generator['strategy'] : 'AUTO';
331 32
                $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_'
332 32
                    . $strategy));
333
            }
334
335
            // Check for SequenceGenerator/TableGenerator definition
336 33
            if (isset($idElement->{'sequence-generator'})) {
337 3
                $seqGenerator = $idElement->{'sequence-generator'};
338 3
                $metadata->setSequenceGeneratorDefinition(
339
                    [
340 3
                        'sequenceName' => (string) $seqGenerator['sequence-name'],
341 3
                        'allocationSize' => (string) $seqGenerator['allocation-size'],
342 3
                        'initialValue' => (string) $seqGenerator['initial-value']
343
                    ]
344
                );
345 30
            } else if (isset($idElement->{'custom-id-generator'})) {
346 2
                $customGenerator = $idElement->{'custom-id-generator'};
347 2
                $metadata->setCustomGeneratorDefinition(
348
                    [
349 2
                        'class' => (string) $customGenerator['class']
350
                    ]
351
                );
352 28
            } else if (isset($idElement->{'table-generator'})) {
353 33
                throw MappingException::tableIdGeneratorNotImplemented($className);
354
            }
355
        }
356
357
        // Evaluate <one-to-one ...> mappings
358 39
        if (isset($xmlRoot->{'one-to-one'})) {
359 8
            foreach ($xmlRoot->{'one-to-one'} as $oneToOneElement) {
360
                $mapping = [
361 8
                    'fieldName' => (string) $oneToOneElement['field'],
362 8
                    'targetEntity' => (string) $oneToOneElement['target-entity']
363
                ];
364
365 8
                if (isset($associationIds[$mapping['fieldName']])) {
366
                    $mapping['id'] = true;
367
                }
368
369 8
                if (isset($oneToOneElement['fetch'])) {
370 2
                    $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string) $oneToOneElement['fetch']);
371
                }
372
373 8
                if (isset($oneToOneElement['mapped-by'])) {
374 2
                    $mapping['mappedBy'] = (string) $oneToOneElement['mapped-by'];
375
                } else {
376 8
                    if (isset($oneToOneElement['inversed-by'])) {
377 8
                        $mapping['inversedBy'] = (string) $oneToOneElement['inversed-by'];
378
                    }
379 8
                    $joinColumns = [];
380
381 8
                    if (isset($oneToOneElement->{'join-column'})) {
382 7
                        $joinColumns[] = $this->joinColumnToArray($oneToOneElement->{'join-column'});
383 1
                    } else if (isset($oneToOneElement->{'join-columns'})) {
384 1
                        foreach ($oneToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
385 1
                            $joinColumns[] = $this->joinColumnToArray($joinColumnElement);
386
                        }
387
                    }
388
389 8
                    $mapping['joinColumns'] = $joinColumns;
390
                }
391
392 8
                if (isset($oneToOneElement->cascade)) {
393 6
                    $mapping['cascade'] = $this->_getCascadeMappings($oneToOneElement->cascade);
394
                }
395
396 8
                if (isset($oneToOneElement['orphan-removal'])) {
397 3
                    $mapping['orphanRemoval'] = $this->evaluateBoolean($oneToOneElement['orphan-removal']);
398
                }
399
400
                // Evaluate second level cache
401 8
                if (isset($oneToOneElement->cache)) {
402
                    $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($oneToOneElement->cache));
0 ignored issues
show
Bug introduced by
The method getAssociationCacheDefaults() does not exist on Doctrine\Common\Persistence\Mapping\ClassMetadata. Did you maybe mean getAssociationNames()? ( Ignorable by Annotation )

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

402
                    /** @scrutinizer ignore-call */ 
403
                    $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($oneToOneElement->cache));

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
403
                }
404
405 8
                $metadata->mapOneToOne($mapping);
406
            }
407
        }
408
409
        // Evaluate <one-to-many ...> mappings
410 39
        if (isset($xmlRoot->{'one-to-many'})) {
411 9
            foreach ($xmlRoot->{'one-to-many'} as $oneToManyElement) {
412
                $mapping = [
413 9
                    'fieldName' => (string) $oneToManyElement['field'],
414 9
                    'targetEntity' => (string) $oneToManyElement['target-entity'],
415 9
                    'mappedBy' => (string) $oneToManyElement['mapped-by']
416
                ];
417
418 9
                if (isset($oneToManyElement['fetch'])) {
419 2
                    $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string) $oneToManyElement['fetch']);
420
                }
421
422 9
                if (isset($oneToManyElement->cascade)) {
423 6
                    $mapping['cascade'] = $this->_getCascadeMappings($oneToManyElement->cascade);
424
                }
425
426 9
                if (isset($oneToManyElement['orphan-removal'])) {
427 6
                    $mapping['orphanRemoval'] = $this->evaluateBoolean($oneToManyElement['orphan-removal']);
428
                }
429
430 9
                if (isset($oneToManyElement->{'order-by'})) {
431 7
                    $orderBy = [];
432 7
                    foreach ($oneToManyElement->{'order-by'}->{'order-by-field'} as $orderByField) {
433 7
                        $orderBy[(string) $orderByField['name']] = isset($orderByField['direction'])
434 6
                            ? (string) $orderByField['direction']
435 7
                            : Criteria::ASC
436
                        ;
437
                    }
438 7
                    $mapping['orderBy'] = $orderBy;
439
                }
440
441 9
                if (isset($oneToManyElement['index-by'])) {
442 3
                    $mapping['indexBy'] = (string) $oneToManyElement['index-by'];
443 6
                } else if (isset($oneToManyElement->{'index-by'})) {
444
                    throw new \InvalidArgumentException("<index-by /> is not a valid tag");
445
                }
446
447
                // Evaluate second level cache
448 9
                if (isset($oneToManyElement->cache)) {
449 1
                    $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($oneToManyElement->cache));
450
                }
451
452 9
                $metadata->mapOneToMany($mapping);
453
            }
454
        }
455
456
        // Evaluate <many-to-one ...> mappings
457 39
        if (isset($xmlRoot->{'many-to-one'})) {
458 8
            foreach ($xmlRoot->{'many-to-one'} as $manyToOneElement) {
459
                $mapping = [
460 8
                    'fieldName' => (string) $manyToOneElement['field'],
461 8
                    'targetEntity' => (string) $manyToOneElement['target-entity']
462
                ];
463
464 8
                if (isset($associationIds[$mapping['fieldName']])) {
465 2
                    $mapping['id'] = true;
466
                }
467
468 8
                if (isset($manyToOneElement['fetch'])) {
469 1
                    $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string) $manyToOneElement['fetch']);
470
                }
471
472 8
                if (isset($manyToOneElement['inversed-by'])) {
473 1
                    $mapping['inversedBy'] = (string) $manyToOneElement['inversed-by'];
474
                }
475
476 8
                $joinColumns = [];
477
478 8
                if (isset($manyToOneElement->{'join-column'})) {
479 5
                    $joinColumns[] = $this->joinColumnToArray($manyToOneElement->{'join-column'});
480 3
                } else if (isset($manyToOneElement->{'join-columns'})) {
481 2
                    foreach ($manyToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
482 2
                        $joinColumns[] = $this->joinColumnToArray($joinColumnElement);
483
                    }
484
                }
485
486 8
                $mapping['joinColumns'] = $joinColumns;
487
488 8
                if (isset($manyToOneElement->cascade)) {
489 2
                    $mapping['cascade'] = $this->_getCascadeMappings($manyToOneElement->cascade);
490
                }
491
492
                // Evaluate second level cache
493 8
                if (isset($manyToOneElement->cache)) {
494 1
                    $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($manyToOneElement->cache));
495
                }
496
497 8
                $metadata->mapManyToOne($mapping);
498
499
            }
500
        }
501
502
        // Evaluate <many-to-many ...> mappings
503 38
        if (isset($xmlRoot->{'many-to-many'})) {
504 13
            foreach ($xmlRoot->{'many-to-many'} as $manyToManyElement) {
505
                $mapping = [
506 13
                    'fieldName' => (string) $manyToManyElement['field'],
507 13
                    'targetEntity' => (string) $manyToManyElement['target-entity']
508
                ];
509
510 13
                if (isset($manyToManyElement['fetch'])) {
511 3
                    $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string) $manyToManyElement['fetch']);
512
                }
513
514 13
                if (isset($manyToManyElement['orphan-removal'])) {
515
                    $mapping['orphanRemoval'] = $this->evaluateBoolean($manyToManyElement['orphan-removal']);
516
                }
517
518 13
                if (isset($manyToManyElement['mapped-by'])) {
519 3
                    $mapping['mappedBy'] = (string) $manyToManyElement['mapped-by'];
520 10
                } else if (isset($manyToManyElement->{'join-table'})) {
521 8
                    if (isset($manyToManyElement['inversed-by'])) {
522 2
                        $mapping['inversedBy'] = (string) $manyToManyElement['inversed-by'];
523
                    }
524
525 8
                    $joinTableElement = $manyToManyElement->{'join-table'};
526
                    $joinTable = [
527 8
                        'name' => (string) $joinTableElement['name']
528
                    ];
529
530 8
                    if (isset($joinTableElement['schema'])) {
531
                        $joinTable['schema'] = (string) $joinTableElement['schema'];
532
                    }
533
534 8
                    foreach ($joinTableElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
535 8
                        $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement);
536
                    }
537
538 8
                    foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} as $joinColumnElement) {
539 8
                        $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement);
540
                    }
541
542 8
                    $mapping['joinTable'] = $joinTable;
543
                }
544
545 13
                if (isset($manyToManyElement->cascade)) {
546 8
                    $mapping['cascade'] = $this->_getCascadeMappings($manyToManyElement->cascade);
547
                }
548
549 13
                if (isset($manyToManyElement->{'order-by'})) {
550 1
                    $orderBy = [];
551 1
                    foreach ($manyToManyElement->{'order-by'}->{'order-by-field'} as $orderByField) {
552 1
                        $orderBy[(string) $orderByField['name']] = isset($orderByField['direction'])
553
                            ? (string) $orderByField['direction']
554 1
                            : Criteria::ASC
555
                        ;
556
                    }
557 1
                    $mapping['orderBy'] = $orderBy;
558
                }
559
560 13
                if (isset($manyToManyElement['index-by'])) {
561
                    $mapping['indexBy'] = (string) $manyToManyElement['index-by'];
562 13
                } else if (isset($manyToManyElement->{'index-by'})) {
563
                    throw new \InvalidArgumentException("<index-by /> is not a valid tag");
564
                }
565
566
                // Evaluate second level cache
567 13
                if (isset($manyToManyElement->cache)) {
568
                    $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($manyToManyElement->cache));
569
                }
570
571 13
                $metadata->mapManyToMany($mapping);
572
            }
573
        }
574
575
        // Evaluate association-overrides
576 38
        if (isset($xmlRoot->{'attribute-overrides'})) {
577 2
            foreach ($xmlRoot->{'attribute-overrides'}->{'attribute-override'} as $overrideElement) {
578 2
                $fieldName = (string) $overrideElement['name'];
579 2
                foreach ($overrideElement->field as $field) {
580 2
                    $mapping = $this->columnToArray($field);
581 2
                    $mapping['fieldName'] = $fieldName;
582 2
                    $metadata->setAttributeOverride($fieldName, $mapping);
583
                }
584
            }
585
        }
586
587
        // Evaluate association-overrides
588 38
        if (isset($xmlRoot->{'association-overrides'})) {
589 4
            foreach ($xmlRoot->{'association-overrides'}->{'association-override'} as $overrideElement) {
590 4
                $fieldName  = (string) $overrideElement['name'];
591 4
                $override   = [];
592
593
                // Check for join-columns
594 4
                if (isset($overrideElement->{'join-columns'})) {
595 2
                    $joinColumns = [];
596 2
                    foreach ($overrideElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
597 2
                        $joinColumns[] = $this->joinColumnToArray($joinColumnElement);
598
                    }
599 2
                    $override['joinColumns'] = $joinColumns;
600
                }
601
602
                // Check for join-table
603 4
                if ($overrideElement->{'join-table'}) {
604 2
                    $joinTable          = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $joinTable is dead and can be removed.
Loading history...
605 2
                    $joinTableElement   = $overrideElement->{'join-table'};
606
607
                    $joinTable = [
608 2
                        'name'      => (string) $joinTableElement['name'],
609 2
                        'schema'    => (string) $joinTableElement['schema']
610
                    ];
611
612 2
                    if (isset($joinTableElement->{'join-columns'})) {
613 2
                        foreach ($joinTableElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
614 2
                            $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement);
615
                        }
616
                    }
617
618 2
                    if (isset($joinTableElement->{'inverse-join-columns'})) {
619 2
                        foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} as $joinColumnElement) {
620 2
                            $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement);
621
                        }
622
                    }
623
624 2
                    $override['joinTable'] = $joinTable;
625
                }
626
627
                // Check for inversed-by
628 4
                if (isset($overrideElement->{'inversed-by'})) {
629 1
                    $override['inversedBy'] = (string) $overrideElement->{'inversed-by'}['name'];
630
                }
631
632
                // Check for `fetch`
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
633 4
                if (isset($overrideElement['fetch'])) {
634 1
                    $override['fetch'] = constant(Metadata::class . '::FETCH_' . (string) $overrideElement['fetch']);
635
                }
636
637 4
                $metadata->setAssociationOverride($fieldName, $override);
638
            }
639
        }
640
641
        // Evaluate <lifecycle-callbacks...>
642 38
        if (isset($xmlRoot->{'lifecycle-callbacks'})) {
643 5
            foreach ($xmlRoot->{'lifecycle-callbacks'}->{'lifecycle-callback'} as $lifecycleCallback) {
644 5
                $metadata->addLifecycleCallback((string) $lifecycleCallback['method'], constant('Doctrine\ORM\Events::' . (string) $lifecycleCallback['type']));
645
            }
646
        }
647
648
        // Evaluate entity listener
649 38
        if (isset($xmlRoot->{'entity-listeners'})) {
650 6
            foreach ($xmlRoot->{'entity-listeners'}->{'entity-listener'} as $listenerElement) {
651 6
                $className = (string) $listenerElement['class'];
652
                // Evaluate the listener using naming convention.
653 6
                if ($listenerElement->count() === 0) {
654 2
                    EntityListenerBuilder::bindEntityListener($metadata, $className);
655
656 2
                    continue;
657
                }
658
659 4
                foreach ($listenerElement as $callbackElement) {
660 4
                    $eventName   = (string) $callbackElement['type'];
661 4
                    $methodName  = (string) $callbackElement['method'];
662
663 4
                    $metadata->addEntityListener($eventName, $className, $methodName);
664
                }
665
            }
666
        }
667 38
    }
668
669
    /**
670
     * Parses (nested) option elements.
671
     *
672
     * @param SimpleXMLElement $options The XML element.
673
     *
674
     * @return array The options array.
675
     */
676 6
    private function _parseOptions(SimpleXMLElement $options)
677
    {
678 6
        $array = [];
679
680
        /* @var $option SimpleXMLElement */
681 6
        foreach ($options as $option) {
682 6
            if ($option->count()) {
683 5
                $value = $this->_parseOptions($option->children());
684
            } else {
685 6
                $value = (string) $option;
686
            }
687
688 6
            $attributes = $option->attributes();
689
690 6
            if (isset($attributes->name)) {
691 6
                $nameAttribute = (string) $attributes->name;
692 6
                $array[$nameAttribute] = in_array($nameAttribute, ['unsigned', 'fixed'])
693 5
                    ? $this->evaluateBoolean($value)
694 6
                    : $value;
695
            } else {
696 6
                $array[] = $value;
697
            }
698
        }
699
700 6
        return $array;
701
    }
702
703
    /**
704
     * Constructs a joinColumn mapping array based on the information
705
     * found in the given SimpleXMLElement.
706
     *
707
     * @param SimpleXMLElement $joinColumnElement The XML element.
708
     *
709
     * @return array The mapping array.
710
     */
711 14
    private function joinColumnToArray(SimpleXMLElement $joinColumnElement)
712
    {
713
        $joinColumn = [
714 14
            'name' => (string) $joinColumnElement['name'],
715 14
            'referencedColumnName' => (string) $joinColumnElement['referenced-column-name']
716
        ];
717
718 14
        if (isset($joinColumnElement['unique'])) {
719 4
            $joinColumn['unique'] = $this->evaluateBoolean($joinColumnElement['unique']);
720
        }
721
722 14
        if (isset($joinColumnElement['nullable'])) {
723 6
            $joinColumn['nullable'] = $this->evaluateBoolean($joinColumnElement['nullable']);
724
        }
725
726 14
        if (isset($joinColumnElement['on-delete'])) {
727 6
            $joinColumn['onDelete'] = (string) $joinColumnElement['on-delete'];
728
        }
729
730 14
        if (isset($joinColumnElement['column-definition'])) {
731 5
            $joinColumn['columnDefinition'] = (string) $joinColumnElement['column-definition'];
732
        }
733
734 14
        return $joinColumn;
735
    }
736
737
     /**
738
     * Parses the given field as array.
739
     *
740
     * @param SimpleXMLElement $fieldMapping
741
     *
742
     * @return array
743
     */
744 24
    private function columnToArray(SimpleXMLElement $fieldMapping)
745
    {
746
        $mapping = [
747 24
            'fieldName' => (string) $fieldMapping['name'],
748
        ];
749
750 24
        if (isset($fieldMapping['type'])) {
751 20
            $mapping['type'] = (string) $fieldMapping['type'];
752
        }
753
754 24
        if (isset($fieldMapping['column'])) {
755 16
            $mapping['columnName'] = (string) $fieldMapping['column'];
756
        }
757
758 24
        if (isset($fieldMapping['length'])) {
759 11
            $mapping['length'] = (int) $fieldMapping['length'];
760
        }
761
762 24
        if (isset($fieldMapping['precision'])) {
763 1
            $mapping['precision'] = (int) $fieldMapping['precision'];
764
        }
765
766 24
        if (isset($fieldMapping['scale'])) {
767 1
            $mapping['scale'] = (int) $fieldMapping['scale'];
768
        }
769
770 24
        if (isset($fieldMapping['unique'])) {
771 10
            $mapping['unique'] = $this->evaluateBoolean($fieldMapping['unique']);
772
        }
773
774 24
        if (isset($fieldMapping['nullable'])) {
775 9
            $mapping['nullable'] = $this->evaluateBoolean($fieldMapping['nullable']);
776
        }
777
778 24
        if (isset($fieldMapping['version']) && $fieldMapping['version']) {
779 3
            $mapping['version'] = $this->evaluateBoolean($fieldMapping['version']);
780
        }
781
782 24
        if (isset($fieldMapping['column-definition'])) {
783 6
            $mapping['columnDefinition'] = (string) $fieldMapping['column-definition'];
784
        }
785
786 24
        if (isset($fieldMapping->options)) {
787 5
            $mapping['options'] = $this->_parseOptions($fieldMapping->options->children());
788
        }
789
790 24
        return $mapping;
791
    }
792
793
    /**
794
     * Parse / Normalize the cache configuration
795
     *
796
     * @param SimpleXMLElement $cacheMapping
797
     *
798
     * @return array
799
     */
800 2
    private function cacheToArray(SimpleXMLElement $cacheMapping)
801
    {
802 2
        $region = isset($cacheMapping['region']) ? (string) $cacheMapping['region'] : null;
803 2
        $usage  = isset($cacheMapping['usage']) ? strtoupper($cacheMapping['usage']) : null;
804
805 2
        if ($usage && ! defined('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $usage)) {
806
            throw new \InvalidArgumentException(sprintf('Invalid cache usage "%s"', $usage));
807
        }
808
809 2
        if ($usage) {
810 2
            $usage = constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $usage);
811
        }
812
813
        return [
814 2
            'usage'  => $usage,
815 2
            'region' => $region,
816
        ];
817
    }
818
819
    /**
820
     * Gathers a list of cascade options found in the given cascade element.
821
     *
822
     * @param SimpleXMLElement $cascadeElement The cascade element.
823
     *
824
     * @return array The list of cascade options.
825
     */
826 8
    private function _getCascadeMappings(SimpleXMLElement $cascadeElement)
827
    {
828 8
        $cascades = [];
829
        /* @var $action SimpleXmlElement */
830 8
        foreach ($cascadeElement->children() as $action) {
831
            // According to the JPA specifications, XML uses "cascade-persist"
832
            // instead of "persist". Here, both variations
833
            // are supported because both YAML and Annotation use "persist"
834
            // and we want to make sure that this driver doesn't need to know
835
            // anything about the supported cascading actions
836 8
            $cascades[] = str_replace('cascade-', '', $action->getName());
837
        }
838
839 8
        return $cascades;
840
    }
841
842
    /**
843
     * {@inheritDoc}
844
     */
845 41
    protected function loadMappingFile($file)
846
    {
847 41
        $result = [];
848
        // Note: we do not use `simplexml_load_file()` because of https://bugs.php.net/bug.php?id=62577
849 41
        $xmlElement = simplexml_load_string(file_get_contents($file));
850
851 41
        if (isset($xmlElement->entity)) {
852 39
            foreach ($xmlElement->entity as $entityElement) {
853 39
                $entityName = (string) $entityElement['name'];
854 39
                $result[$entityName] = $entityElement;
855
            }
856 9
        } else if (isset($xmlElement->{'mapped-superclass'})) {
857 5
            foreach ($xmlElement->{'mapped-superclass'} as $mappedSuperClass) {
858 5
                $className = (string) $mappedSuperClass['name'];
859 5
                $result[$className] = $mappedSuperClass;
860
            }
861 4
        } else if (isset($xmlElement->embeddable)) {
862 3
            foreach ($xmlElement->embeddable as $embeddableElement) {
863 3
                $embeddableName = (string) $embeddableElement['name'];
864 3
                $result[$embeddableName] = $embeddableElement;
865
            }
866
        }
867
868 41
        return $result;
869
    }
870
871
    /**
872
     * @param mixed $element
873
     *
874
     * @return bool
875
     */
876 14
    protected function evaluateBoolean($element)
877
    {
878 14
        $flag = (string) $element;
879
880 14
        return ($flag == "true" || $flag == "1");
881
    }
882
}
883