Passed
Pull Request — 2.7 (#7281)
by Michael
10:04
created

XmlDriver::_getCascadeMappings()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 14
rs 9.7998
c 0
b 0
f 0
ccs 5
cts 5
cp 1
crap 2
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 45
    public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION)
49
    {
50 45
        parent::__construct($locator, $fileExtension);
51 45
    }
52
53
    /**
54
     * {@inheritDoc}
55
     */
56 40
    public function loadMetadataForClass($className, ClassMetadata $metadata)
57
    {
58
        /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */
59
        /* @var $xmlRoot SimpleXMLElement */
60 40
        $xmlRoot = $this->getElement($className);
61
62 38
        if ($xmlRoot->getName() == 'entity') {
63 37
            if (isset($xmlRoot['repository-class'])) {
64
                $metadata->setCustomRepositoryClass((string) $xmlRoot['repository-class']);
65
            }
66 37
            if (isset($xmlRoot['read-only']) && $this->evaluateBoolean($xmlRoot['read-only'])) {
67 37
                $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 38
        $primaryTable = [];
82
83 38
        if (isset($xmlRoot['table'])) {
84 16
            $primaryTable['name'] = (string) $xmlRoot['table'];
85
        }
86
87 38
        if (isset($xmlRoot['schema'])) {
88 1
            $primaryTable['schema'] = (string) $xmlRoot['schema'];
89
        }
90
91 38
        $metadata->setPrimaryTable($primaryTable);
92
93
        // Evaluate second level cache
94 38
        if (isset($xmlRoot->cache)) {
95 2
            $metadata->enableCache($this->cacheToArray($xmlRoot->cache));
96
        }
97
98
        // Evaluate named queries
99 38
        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 38
        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 38
        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 38
        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 38
        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 38
        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 38
        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 38
        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 38
        $mappings = [];
251
        // Evaluate <field ...> mappings
252 38
        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 38
        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 38
        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 38
        $associationIds = [];
295 38
        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 38
        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 38
        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 38
        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 37
        if (isset($xmlRoot->{'many-to-many'})) {
504 12
            foreach ($xmlRoot->{'many-to-many'} as $manyToManyElement) {
505
                $mapping = [
506 12
                    'fieldName' => (string) $manyToManyElement['field'],
507 12
                    'targetEntity' => (string) $manyToManyElement['target-entity']
508
                ];
509
510 12
                if (isset($manyToManyElement['fetch'])) {
511 3
                    $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string) $manyToManyElement['fetch']);
512
                }
513
514 12
                if (isset($manyToManyElement['orphan-removal'])) {
515
                    $mapping['orphanRemoval'] = $this->evaluateBoolean($manyToManyElement['orphan-removal']);
516
                }
517
518 12
                if (isset($manyToManyElement['mapped-by'])) {
519 2
                    $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 12
                if (isset($manyToManyElement->cascade)) {
546 8
                    $mapping['cascade'] = $this->_getCascadeMappings($manyToManyElement->cascade);
547
                }
548
549 12
                if (isset($manyToManyElement->{'order-by'})) {
550
                    $orderBy = [];
551
                    foreach ($manyToManyElement->{'order-by'}->{'order-by-field'} as $orderByField) {
552
                        $orderBy[(string) $orderByField['name']] = (string) $orderByField['direction'];
553
                    }
554
                    $mapping['orderBy'] = $orderBy;
555
                }
556
557 12
                if (isset($manyToManyElement['index-by'])) {
558
                    $mapping['indexBy'] = (string) $manyToManyElement['index-by'];
559 12
                } else if (isset($manyToManyElement->{'index-by'})) {
560
                    throw new \InvalidArgumentException("<index-by /> is not a valid tag");
561
                }
562
563
                // Evaluate second level cache
564 12
                if (isset($manyToManyElement->cache)) {
565
                    $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($manyToManyElement->cache));
566
                }
567
568 12
                $metadata->mapManyToMany($mapping);
569
            }
570
        }
571
572
        // Evaluate association-overrides
573 37
        if (isset($xmlRoot->{'attribute-overrides'})) {
574 2
            foreach ($xmlRoot->{'attribute-overrides'}->{'attribute-override'} as $overrideElement) {
575 2
                $fieldName = (string) $overrideElement['name'];
576 2
                foreach ($overrideElement->field as $field) {
577 2
                    $mapping = $this->columnToArray($field);
578 2
                    $mapping['fieldName'] = $fieldName;
579 2
                    $metadata->setAttributeOverride($fieldName, $mapping);
580
                }
581
            }
582
        }
583
584
        // Evaluate association-overrides
585 37
        if (isset($xmlRoot->{'association-overrides'})) {
586 4
            foreach ($xmlRoot->{'association-overrides'}->{'association-override'} as $overrideElement) {
587 4
                $fieldName  = (string) $overrideElement['name'];
588 4
                $override   = [];
589
590
                // Check for join-columns
591 4
                if (isset($overrideElement->{'join-columns'})) {
592 2
                    $joinColumns = [];
593 2
                    foreach ($overrideElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
594 2
                        $joinColumns[] = $this->joinColumnToArray($joinColumnElement);
595
                    }
596 2
                    $override['joinColumns'] = $joinColumns;
597
                }
598
599
                // Check for join-table
600 4
                if ($overrideElement->{'join-table'}) {
601 2
                    $joinTable          = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $joinTable is dead and can be removed.
Loading history...
602 2
                    $joinTableElement   = $overrideElement->{'join-table'};
603
604
                    $joinTable = [
605 2
                        'name'      => (string) $joinTableElement['name'],
606 2
                        'schema'    => (string) $joinTableElement['schema']
607
                    ];
608
609 2
                    if (isset($joinTableElement->{'join-columns'})) {
610 2
                        foreach ($joinTableElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
611 2
                            $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement);
612
                        }
613
                    }
614
615 2
                    if (isset($joinTableElement->{'inverse-join-columns'})) {
616 2
                        foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} as $joinColumnElement) {
617 2
                            $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement);
618
                        }
619
                    }
620
621 2
                    $override['joinTable'] = $joinTable;
622
                }
623
624
                // Check for inversed-by
625 4
                if (isset($overrideElement->{'inversed-by'})) {
626 1
                    $override['inversedBy'] = (string) $overrideElement->{'inversed-by'}['name'];
627
                }
628
629
                // 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...
630 4
                if (isset($overrideElement['fetch'])) {
631 1
                    $override['fetch'] = constant(Metadata::class . '::FETCH_' . (string) $overrideElement['fetch']);
632
                }
633
634 4
                $metadata->setAssociationOverride($fieldName, $override);
635
            }
636
        }
637
638
        // Evaluate <lifecycle-callbacks...>
639 37
        if (isset($xmlRoot->{'lifecycle-callbacks'})) {
640 5
            foreach ($xmlRoot->{'lifecycle-callbacks'}->{'lifecycle-callback'} as $lifecycleCallback) {
641 5
                $metadata->addLifecycleCallback((string) $lifecycleCallback['method'], constant('Doctrine\ORM\Events::' . (string) $lifecycleCallback['type']));
642
            }
643
        }
644
645
        // Evaluate entity listener
646 37
        if (isset($xmlRoot->{'entity-listeners'})) {
647 6
            foreach ($xmlRoot->{'entity-listeners'}->{'entity-listener'} as $listenerElement) {
648 6
                $className = (string) $listenerElement['class'];
649
                // Evaluate the listener using naming convention.
650 6
                if ($listenerElement->count() === 0) {
651 2
                    EntityListenerBuilder::bindEntityListener($metadata, $className);
652
653 2
                    continue;
654
                }
655
656 4
                foreach ($listenerElement as $callbackElement) {
657 4
                    $eventName   = (string) $callbackElement['type'];
658 4
                    $methodName  = (string) $callbackElement['method'];
659
660 4
                    $metadata->addEntityListener($eventName, $className, $methodName);
661
                }
662
            }
663
        }
664 37
    }
665
666
    /**
667
     * Parses (nested) option elements.
668
     *
669
     * @param SimpleXMLElement $options The XML element.
670
     *
671
     * @return array The options array.
672
     */
673 6
    private function _parseOptions(SimpleXMLElement $options)
674
    {
675 6
        $array = [];
676
677
        /* @var $option SimpleXMLElement */
678 6
        foreach ($options as $option) {
679 6
            if ($option->count()) {
680 5
                $value = $this->_parseOptions($option->children());
681
            } else {
682 6
                $value = (string) $option;
683
            }
684
685 6
            $attributes = $option->attributes();
686
687 6
            if (isset($attributes->name)) {
688 6
                $nameAttribute = (string) $attributes->name;
689 6
                $array[$nameAttribute] = in_array($nameAttribute, ['unsigned', 'fixed'])
690 5
                    ? $this->evaluateBoolean($value)
691 6
                    : $value;
692
            } else {
693 6
                $array[] = $value;
694
            }
695
        }
696
697 6
        return $array;
698
    }
699
700
    /**
701
     * Constructs a joinColumn mapping array based on the information
702
     * found in the given SimpleXMLElement.
703
     *
704
     * @param SimpleXMLElement $joinColumnElement The XML element.
705
     *
706
     * @return array The mapping array.
707
     */
708 14
    private function joinColumnToArray(SimpleXMLElement $joinColumnElement)
709
    {
710
        $joinColumn = [
711 14
            'name' => (string) $joinColumnElement['name'],
712 14
            'referencedColumnName' => (string) $joinColumnElement['referenced-column-name']
713
        ];
714
715 14
        if (isset($joinColumnElement['unique'])) {
716 4
            $joinColumn['unique'] = $this->evaluateBoolean($joinColumnElement['unique']);
717
        }
718
719 14
        if (isset($joinColumnElement['nullable'])) {
720 6
            $joinColumn['nullable'] = $this->evaluateBoolean($joinColumnElement['nullable']);
721
        }
722
723 14
        if (isset($joinColumnElement['on-delete'])) {
724 6
            $joinColumn['onDelete'] = (string) $joinColumnElement['on-delete'];
725
        }
726
727 14
        if (isset($joinColumnElement['column-definition'])) {
728 5
            $joinColumn['columnDefinition'] = (string) $joinColumnElement['column-definition'];
729
        }
730
731 14
        return $joinColumn;
732
    }
733
734
     /**
735
     * Parses the given field as array.
736
     *
737
     * @param SimpleXMLElement $fieldMapping
738
     *
739
     * @return array
740
     */
741 24
    private function columnToArray(SimpleXMLElement $fieldMapping)
742
    {
743
        $mapping = [
744 24
            'fieldName' => (string) $fieldMapping['name'],
745
        ];
746
747 24
        if (isset($fieldMapping['type'])) {
748 20
            $mapping['type'] = (string) $fieldMapping['type'];
749
        }
750
751 24
        if (isset($fieldMapping['column'])) {
752 16
            $mapping['columnName'] = (string) $fieldMapping['column'];
753
        }
754
755 24
        if (isset($fieldMapping['length'])) {
756 11
            $mapping['length'] = (int) $fieldMapping['length'];
757
        }
758
759 24
        if (isset($fieldMapping['precision'])) {
760 1
            $mapping['precision'] = (int) $fieldMapping['precision'];
761
        }
762
763 24
        if (isset($fieldMapping['scale'])) {
764 1
            $mapping['scale'] = (int) $fieldMapping['scale'];
765
        }
766
767 24
        if (isset($fieldMapping['unique'])) {
768 10
            $mapping['unique'] = $this->evaluateBoolean($fieldMapping['unique']);
769
        }
770
771 24
        if (isset($fieldMapping['nullable'])) {
772 9
            $mapping['nullable'] = $this->evaluateBoolean($fieldMapping['nullable']);
773
        }
774
775 24
        if (isset($fieldMapping['version']) && $fieldMapping['version']) {
776 3
            $mapping['version'] = $this->evaluateBoolean($fieldMapping['version']);
777
        }
778
779 24
        if (isset($fieldMapping['column-definition'])) {
780 6
            $mapping['columnDefinition'] = (string) $fieldMapping['column-definition'];
781
        }
782
783 24
        if (isset($fieldMapping->options)) {
784 5
            $mapping['options'] = $this->_parseOptions($fieldMapping->options->children());
785
        }
786
787 24
        return $mapping;
788
    }
789
790
    /**
791
     * Parse / Normalize the cache configuration
792
     *
793
     * @param SimpleXMLElement $cacheMapping
794
     *
795
     * @return array
796
     */
797 2
    private function cacheToArray(SimpleXMLElement $cacheMapping)
798
    {
799 2
        $region = isset($cacheMapping['region']) ? (string) $cacheMapping['region'] : null;
800 2
        $usage  = isset($cacheMapping['usage']) ? strtoupper($cacheMapping['usage']) : null;
801
802 2
        if ($usage && ! defined('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $usage)) {
803
            throw new \InvalidArgumentException(sprintf('Invalid cache usage "%s"', $usage));
804
        }
805
806 2
        if ($usage) {
807 2
            $usage = constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $usage);
808
        }
809
810
        return [
811 2
            'usage'  => $usage,
812 2
            'region' => $region,
813
        ];
814
    }
815
816
    /**
817
     * Gathers a list of cascade options found in the given cascade element.
818
     *
819
     * @param SimpleXMLElement $cascadeElement The cascade element.
820
     *
821
     * @return array The list of cascade options.
822
     */
823 8
    private function _getCascadeMappings(SimpleXMLElement $cascadeElement)
824
    {
825 8
        $cascades = [];
826
        /* @var $action SimpleXmlElement */
827 8
        foreach ($cascadeElement->children() as $action) {
828
            // According to the JPA specifications, XML uses "cascade-persist"
829
            // instead of "persist". Here, both variations
830
            // are supported because both YAML and Annotation use "persist"
831
            // and we want to make sure that this driver doesn't need to know
832
            // anything about the supported cascading actions
833 8
            $cascades[] = str_replace('cascade-', '', $action->getName());
834
        }
835
836 8
        return $cascades;
837
    }
838
839
    /**
840
     * {@inheritDoc}
841
     */
842 40
    protected function loadMappingFile($file)
843
    {
844 40
        $result = [];
845
        // Note: we do not use `simplexml_load_file()` because of https://bugs.php.net/bug.php?id=62577
846 40
        $xmlElement = simplexml_load_string(file_get_contents($file));
847
848 40
        if (isset($xmlElement->entity)) {
849 38
            foreach ($xmlElement->entity as $entityElement) {
850 38
                $entityName = (string) $entityElement['name'];
851 38
                $result[$entityName] = $entityElement;
852
            }
853 9
        } else if (isset($xmlElement->{'mapped-superclass'})) {
854 5
            foreach ($xmlElement->{'mapped-superclass'} as $mappedSuperClass) {
855 5
                $className = (string) $mappedSuperClass['name'];
856 5
                $result[$className] = $mappedSuperClass;
857
            }
858 4
        } else if (isset($xmlElement->embeddable)) {
859 3
            foreach ($xmlElement->embeddable as $embeddableElement) {
860 3
                $embeddableName = (string) $embeddableElement['name'];
861 3
                $result[$embeddableName] = $embeddableElement;
862
            }
863
        }
864
865 40
        return $result;
866
    }
867
868
    /**
869
     * @param mixed $element
870
     *
871
     * @return bool
872
     */
873 14
    protected function evaluateBoolean($element)
874
    {
875 14
        $flag = (string) $element;
876
877 14
        return ($flag == "true" || $flag == "1");
878
    }
879
}
880