Completed
Pull Request — master (#5938)
by Luís
10:36
created

XmlDriver::loadMetadataForClass()   F

Complexity

Conditions 139
Paths > 20000

Size

Total Lines 606
Code Lines 323

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 291
CRAP Score 143.4505

Importance

Changes 0
Metric Value
dl 0
loc 606
c 0
b 0
f 0
ccs 291
cts 310
cp 0.9387
rs 2
cc 139
eloc 323
nc 4294967295
nop 2
crap 143.4505

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
28
/**
29
 * XmlDriver is a metadata driver that enables mapping through XML files.
30
 *
31
 * @license 	http://www.opensource.org/licenses/mit-license.php MIT
32
 * @link    	www.doctrine-project.org
33
 * @since   	2.0
34
 * @author		Benjamin Eberlei <[email protected]>
35
 * @author		Guilherme Blanco <[email protected]>
36
 * @author      Jonathan H. Wage <[email protected]>
37
 * @author      Roman Borschel <[email protected]>
38
 */
39
class XmlDriver extends FileDriver
40
{
41
    const DEFAULT_FILE_EXTENSION = '.dcm.xml';
42
43
    /**
44
     * {@inheritDoc}
45
     */
46 41
    public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION)
47
    {
48 41
        parent::__construct($locator, $fileExtension);
49 41
    }
50
51
    /**
52
     * {@inheritDoc}
53
     */
54 36
    public function loadMetadataForClass($className, ClassMetadata $metadata)
55
    {
56
        /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */
57
        /* @var $xmlRoot SimpleXMLElement */
58 36
        $xmlRoot = $this->getElement($className);
59
60 34
        if ($xmlRoot->getName() == 'entity') {
61 33
            if (isset($xmlRoot['repository-class'])) {
62
                $metadata->setCustomRepositoryClass((string) $xmlRoot['repository-class']);
63
            }
64 33
            if (isset($xmlRoot['read-only']) && $this->evaluateBoolean($xmlRoot['read-only'])) {
65 33
                $metadata->markReadOnly();
66
            }
67 8
        } else if ($xmlRoot->getName() == 'mapped-superclass') {
68 5
            $metadata->setCustomRepositoryClass(
69 5
                isset($xmlRoot['repository-class']) ? (string) $xmlRoot['repository-class'] : null
70
            );
71 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?

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...
72 3
        } else if ($xmlRoot->getName() == 'embeddable') {
73 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?

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...
74
        } else {
75
            throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
76
        }
77
78
        // Evaluate <entity...> attributes
79 34
        $primaryTable = [];
80
81 34
        if (isset($xmlRoot['table'])) {
82 13
            $primaryTable['name'] = (string) $xmlRoot['table'];
83
        }
84
85 34
        if (isset($xmlRoot['schema'])) {
86 1
            $primaryTable['schema'] = (string) $xmlRoot['schema'];
87
        }
88
89 34
        $metadata->setPrimaryTable($primaryTable);
90
91
        // Evaluate second level cache
92 34
        if (isset($xmlRoot->cache)) {
93 2
            $metadata->enableCache($this->cacheToArray($xmlRoot->cache));
94
        }
95
96
        // Evaluate named queries
97 34
        if (isset($xmlRoot->{'named-queries'})) {
98 2
            foreach ($xmlRoot->{'named-queries'}->{'named-query'} as $namedQueryElement) {
99 2
                $metadata->addNamedQuery(
100
                    [
101 2
                        'name'  => (string) $namedQueryElement['name'],
102 2
                        'query' => (string) $namedQueryElement['query']
103
                    ]
104
                );
105
            }
106
        }
107
108
        // Evaluate native named queries
109 34
        if (isset($xmlRoot->{'named-native-queries'})) {
110 3
            foreach ($xmlRoot->{'named-native-queries'}->{'named-native-query'} as $nativeQueryElement) {
111 3
                $metadata->addNamedNativeQuery(
112
                    [
113 3
                        'name'              => isset($nativeQueryElement['name']) ? (string) $nativeQueryElement['name'] : null,
114 3
                        'query'             => isset($nativeQueryElement->query) ? (string) $nativeQueryElement->query : null,
115 3
                        'resultClass'       => isset($nativeQueryElement['result-class']) ? (string) $nativeQueryElement['result-class'] : null,
116 3
                        'resultSetMapping'  => isset($nativeQueryElement['result-set-mapping']) ? (string) $nativeQueryElement['result-set-mapping'] : null,
117
                    ]
118
                );
119
            }
120
        }
121
122
        // Evaluate sql result set mapping
123 34
        if (isset($xmlRoot->{'sql-result-set-mappings'})) {
124 3
            foreach ($xmlRoot->{'sql-result-set-mappings'}->{'sql-result-set-mapping'} as $rsmElement) {
125 3
                $entities   = [];
126 3
                $columns    = [];
127 3
                foreach ($rsmElement as $entityElement) {
128
                    //<entity-result/>
129 3
                    if (isset($entityElement['entity-class'])) {
130
                        $entityResult = [
131 3
                            'fields'                => [],
132 3
                            'entityClass'           => (string) $entityElement['entity-class'],
133 3
                            'discriminatorColumn'   => isset($entityElement['discriminator-column']) ? (string) $entityElement['discriminator-column'] : null,
134
                        ];
135
136 3
                        foreach ($entityElement as $fieldElement) {
137 3
                            $entityResult['fields'][] = [
138 3
                                'name'      => isset($fieldElement['name']) ? (string) $fieldElement['name'] : null,
139 3
                                'column'    => isset($fieldElement['column']) ? (string) $fieldElement['column'] : null,
140
                            ];
141
                        }
142
143 3
                        $entities[] = $entityResult;
144
                    }
145
146
                    //<column-result/>
147 3
                    if (isset($entityElement['name'])) {
148 3
                        $columns[] = [
149 3
                            'name' => (string) $entityElement['name'],
150
                        ];
151
                    }
152
                }
153
154 3
                $metadata->addSqlResultSetMapping(
155
                    [
156 3
                        'name'          => (string) $rsmElement['name'],
157 3
                        'entities'      => $entities,
158 3
                        'columns'       => $columns
159
                    ]
160
                );
161
            }
162
        }
163
164 34
        if (isset($xmlRoot['inheritance-type'])) {
165 10
            $inheritanceType = (string) $xmlRoot['inheritance-type'];
166 10
            $metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceType));
167
168 10
            if ($metadata->inheritanceType != \Doctrine\ORM\Mapping\ClassMetadata::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?

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...
169
                // Evaluate <discriminator-column...>
170 10
                if (isset($xmlRoot->{'discriminator-column'})) {
171 7
                    $discrColumn = $xmlRoot->{'discriminator-column'};
172 7
                    $metadata->setDiscriminatorColumn(
173
                        [
174 7
                            'name' => isset($discrColumn['name']) ? (string) $discrColumn['name'] : null,
175 7
                            'type' => isset($discrColumn['type']) ? (string) $discrColumn['type'] : 'string',
176 7
                            'length' => isset($discrColumn['length']) ? (string) $discrColumn['length'] : 255,
177 7
                            'columnDefinition' => isset($discrColumn['column-definition']) ? (string) $discrColumn['column-definition'] : null
178
                        ]
179
                    );
180
                } else {
181 6
                    $metadata->setDiscriminatorColumn(['name' => 'dtype', 'type' => 'string', 'length' => 255]);
182
                }
183
184
                // Evaluate <discriminator-map...>
185 10
                if (isset($xmlRoot->{'discriminator-map'})) {
186 10
                    $map = [];
187 10
                    foreach ($xmlRoot->{'discriminator-map'}->{'discriminator-mapping'} as $discrMapElement) {
188 10
                        $map[(string) $discrMapElement['value']] = (string) $discrMapElement['class'];
189
                    }
190 10
                    $metadata->setDiscriminatorMap($map);
191
                }
192
            }
193
        }
194
195
196
        // Evaluate <change-tracking-policy...>
197 34
        if (isset($xmlRoot['change-tracking-policy'])) {
198
            $metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_'
199
                . strtoupper((string) $xmlRoot['change-tracking-policy'])));
200
        }
201
202
        // Evaluate <indexes...>
203 34
        if (isset($xmlRoot->indexes)) {
204 2
            $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?

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...
205 2
            foreach ($xmlRoot->indexes->index as $indexXml) {
206 2
                $index = ['columns' => explode(',', (string) $indexXml['columns'])];
207
208 2
                if (isset($indexXml['flags'])) {
209 1
                    $index['flags'] = explode(',', (string) $indexXml['flags']);
210
                }
211
212 2
                if (isset($indexXml->options)) {
213 1
                    $index['options'] = $this->_parseOptions($indexXml->options->children());
214
                }
215
216 2
                if (isset($indexXml['name'])) {
217 1
                    $metadata->table['indexes'][(string) $indexXml['name']] = $index;
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?

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...
218
                } else {
219 2
                    $metadata->table['indexes'][] = $index;
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?

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...
220
                }
221
            }
222
        }
223
224
        // Evaluate <unique-constraints..>
225 34
        if (isset($xmlRoot->{'unique-constraints'})) {
226 1
            $metadata->table['uniqueConstraints'] = [];
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?

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...
227 1
            foreach ($xmlRoot->{'unique-constraints'}->{'unique-constraint'} as $uniqueXml) {
228 1
                $unique = ['columns' => explode(',', (string) $uniqueXml['columns'])];
229
230 1
                if (isset($uniqueXml->options)) {
231 1
                    $unique['options'] = $this->_parseOptions($uniqueXml->options->children());
232
                }
233
234 1
                if (isset($uniqueXml['name'])) {
235 1
                    $metadata->table['uniqueConstraints'][(string) $uniqueXml['name']] = $unique;
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?

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...
236
                } else {
237 1
                    $metadata->table['uniqueConstraints'][] = $unique;
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?

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...
238
                }
239
            }
240
        }
241
242 34
        if (isset($xmlRoot->options)) {
243 3
            $metadata->table['options'] = $this->_parseOptions($xmlRoot->options->children());
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?

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...
244
        }
245
246
        // The mapping assignment is done in 2 times as a bug might occurs on some php/xml lib versions
247
        // The internal SimpleXmlIterator get resetted, to this generate a duplicate field exception
248 34
        $mappings = [];
249
        // Evaluate <field ...> mappings
250 34
        if (isset($xmlRoot->field)) {
251 22
            foreach ($xmlRoot->field as $fieldMapping) {
252 22
                $mapping = $this->columnToArray($fieldMapping);
253
254 22
                if (isset($mapping['version'])) {
255 1
                    $metadata->setVersionMapping($mapping);
256 1
                    unset($mapping['version']);
257
                }
258
259 22
                $metadata->mapField($mapping);
260
            }
261
        }
262
263 34
        if (isset($xmlRoot->embedded)) {
264 3
            foreach ($xmlRoot->embedded as $embeddedMapping) {
265 3
                $columnPrefix = isset($embeddedMapping['column-prefix'])
266 3
                    ? (string) $embeddedMapping['column-prefix']
267 3
                    : null;
268
269 3
                $useColumnPrefix = isset($embeddedMapping['use-column-prefix'])
270 2
                    ? $this->evaluateBoolean($embeddedMapping['use-column-prefix'])
271 3
                    : true;
272
273
                $mapping = [
274 3
                    'fieldName' => (string) $embeddedMapping['name'],
275 3
                    'class' => (string) $embeddedMapping['class'],
276 3
                    'columnPrefix' => $useColumnPrefix ? $columnPrefix : false
277
                ];
278
279 3
                $metadata->mapEmbedded($mapping);
280
            }
281
        }
282
283 34
        foreach ($mappings as $mapping) {
284
            if (isset($mapping['version'])) {
285
                $metadata->setVersionMapping($mapping);
286
            }
287
288
            $metadata->mapField($mapping);
289
        }
290
291
        // Evaluate <id ...> mappings
292 34
        $associationIds = [];
293 34
        foreach ($xmlRoot->id as $idElement) {
294 31
            if (isset($idElement['association-key']) && $this->evaluateBoolean($idElement['association-key'])) {
295 2
                $associationIds[(string) $idElement['name']] = true;
296 2
                continue;
297
            }
298
299
            $mapping = [
300 30
                'id' => true,
301 30
                'fieldName' => (string) $idElement['name']
302
            ];
303
304 30
            if (isset($idElement['type'])) {
305 19
                $mapping['type'] = (string) $idElement['type'];
306
            }
307
308 30
            if (isset($idElement['length'])) {
309 3
                $mapping['length'] = (string) $idElement['length'];
310
            }
311
312 30
            if (isset($idElement['column'])) {
313 21
                $mapping['columnName'] = (string) $idElement['column'];
314
            }
315
316 30
            if (isset($idElement['column-definition'])) {
317 1
                $mapping['columnDefinition'] = (string) $idElement['column-definition'];
318
            }
319
320 30
            if (isset($idElement->options)) {
321 1
                $mapping['options'] = $this->_parseOptions($idElement->options->children());
322
            }
323
324 30
            $metadata->mapField($mapping);
325
326 30
            if (isset($idElement->generator)) {
327 29
                $strategy = isset($idElement->generator['strategy']) ?
328 29
                        (string) $idElement->generator['strategy'] : 'AUTO';
329 29
                $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_'
330 29
                    . $strategy));
331
            }
332
333
            // Check for SequenceGenerator/TableGenerator definition
334 30
            if (isset($idElement->{'sequence-generator'})) {
335 1
                $seqGenerator = $idElement->{'sequence-generator'};
336 1
                $metadata->setSequenceGeneratorDefinition(
337
                    [
338 1
                        'sequenceName' => (string) $seqGenerator['sequence-name'],
339 1
                        'allocationSize' => (string) $seqGenerator['allocation-size'],
340 1
                        'initialValue' => (string) $seqGenerator['initial-value']
341
                    ]
342
                );
343 29
            } else if (isset($idElement->{'custom-id-generator'})) {
344 2
                $customGenerator = $idElement->{'custom-id-generator'};
345 2
                $metadata->setCustomGeneratorDefinition(
346
                    [
347 2
                        'class' => (string) $customGenerator['class']
348
                    ]
349
                );
350 27
            } else if (isset($idElement->{'table-generator'})) {
351 30
                throw MappingException::tableIdGeneratorNotImplemented($className);
352
            }
353
        }
354
355
        // Evaluate <one-to-one ...> mappings
356 34
        if (isset($xmlRoot->{'one-to-one'})) {
357 6
            foreach ($xmlRoot->{'one-to-one'} as $oneToOneElement) {
358
                $mapping = [
359 6
                    'fieldName' => (string) $oneToOneElement['field'],
360 6
                    'targetEntity' => (string) $oneToOneElement['target-entity']
361
                ];
362
363 6
                if (isset($associationIds[$mapping['fieldName']])) {
364
                    $mapping['id'] = true;
365
                }
366
367 6
                if (isset($oneToOneElement['fetch'])) {
368 2
                    $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string) $oneToOneElement['fetch']);
369
                }
370
371 6
                if (isset($oneToOneElement['mapped-by'])) {
372 2
                    $mapping['mappedBy'] = (string) $oneToOneElement['mapped-by'];
373
                } else {
374 6
                    if (isset($oneToOneElement['inversed-by'])) {
375 6
                        $mapping['inversedBy'] = (string) $oneToOneElement['inversed-by'];
376
                    }
377 6
                    $joinColumns = [];
378
379 6
                    if (isset($oneToOneElement->{'join-column'})) {
380 5
                        $joinColumns[] = $this->joinColumnToArray($oneToOneElement->{'join-column'});
381 1
                    } else if (isset($oneToOneElement->{'join-columns'})) {
382 1
                        foreach ($oneToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
383 1
                            $joinColumns[] = $this->joinColumnToArray($joinColumnElement);
384
                        }
385
                    }
386
387 6
                    $mapping['joinColumns'] = $joinColumns;
388
                }
389
390 6
                if (isset($oneToOneElement->cascade)) {
391 4
                    $mapping['cascade'] = $this->_getCascadeMappings($oneToOneElement->cascade);
392
                }
393
394 6
                if (isset($oneToOneElement['orphan-removal'])) {
395 3
                    $mapping['orphanRemoval'] = $this->evaluateBoolean($oneToOneElement['orphan-removal']);
396
                }
397
398
                // Evaluate second level cache
399 6
                if (isset($oneToOneElement->cache)) {
400
                    $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($oneToOneElement->cache));
401
                }
402
403 6
                $metadata->mapOneToOne($mapping);
404
            }
405
        }
406
407
        // Evaluate <one-to-many ...> mappings
408 34
        if (isset($xmlRoot->{'one-to-many'})) {
409 6
            foreach ($xmlRoot->{'one-to-many'} as $oneToManyElement) {
410
                $mapping = [
411 6
                    'fieldName' => (string) $oneToManyElement['field'],
412 6
                    'targetEntity' => (string) $oneToManyElement['target-entity'],
413 6
                    'mappedBy' => (string) $oneToManyElement['mapped-by']
414
                ];
415
416 6
                if (isset($oneToManyElement['fetch'])) {
417 2
                    $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string) $oneToManyElement['fetch']);
418
                }
419
420 6
                if (isset($oneToManyElement->cascade)) {
421 4
                    $mapping['cascade'] = $this->_getCascadeMappings($oneToManyElement->cascade);
422
                }
423
424 6
                if (isset($oneToManyElement['orphan-removal'])) {
425 4
                    $mapping['orphanRemoval'] = $this->evaluateBoolean($oneToManyElement['orphan-removal']);
426
                }
427
428 6
                if (isset($oneToManyElement->{'order-by'})) {
429 4
                    $orderBy = [];
430 4
                    foreach ($oneToManyElement->{'order-by'}->{'order-by-field'} as $orderByField) {
431 4
                        $orderBy[(string) $orderByField['name']] = (string) $orderByField['direction'];
432
                    }
433 4
                    $mapping['orderBy'] = $orderBy;
434
                }
435
436 6
                if (isset($oneToManyElement['index-by'])) {
437 1
                    $mapping['indexBy'] = (string) $oneToManyElement['index-by'];
438 5
                } else if (isset($oneToManyElement->{'index-by'})) {
439
                    throw new \InvalidArgumentException("<index-by /> is not a valid tag");
440
                }
441
442
                // Evaluate second level cache
443 6
                if (isset($oneToManyElement->cache)) {
444 1
                    $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($oneToManyElement->cache));
445
                }
446
447 6
                $metadata->mapOneToMany($mapping);
448
            }
449
        }
450
451
        // Evaluate <many-to-one ...> mappings
452 34
        if (isset($xmlRoot->{'many-to-one'})) {
453 7
            foreach ($xmlRoot->{'many-to-one'} as $manyToOneElement) {
454
                $mapping = [
455 7
                    'fieldName' => (string) $manyToOneElement['field'],
456 7
                    'targetEntity' => (string) $manyToOneElement['target-entity']
457
                ];
458
459 7
                if (isset($associationIds[$mapping['fieldName']])) {
460 2
                    $mapping['id'] = true;
461
                }
462
463 7
                if (isset($manyToOneElement['fetch'])) {
464 1
                    $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string) $manyToOneElement['fetch']);
465
                }
466
467 7
                if (isset($manyToOneElement['inversed-by'])) {
468 1
                    $mapping['inversedBy'] = (string) $manyToOneElement['inversed-by'];
469
                }
470
471 7
                $joinColumns = [];
472
473 7
                if (isset($manyToOneElement->{'join-column'})) {
474 4
                    $joinColumns[] = $this->joinColumnToArray($manyToOneElement->{'join-column'});
475 3
                } else if (isset($manyToOneElement->{'join-columns'})) {
476 2
                    foreach ($manyToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
477 2
                        $joinColumns[] = $this->joinColumnToArray($joinColumnElement);
478
                    }
479
                }
480
481 7
                $mapping['joinColumns'] = $joinColumns;
482
483 7
                if (isset($manyToOneElement->cascade)) {
484 2
                    $mapping['cascade'] = $this->_getCascadeMappings($manyToOneElement->cascade);
485
                }
486
487
                // Evaluate second level cache
488 7
                if (isset($manyToOneElement->cache)) {
489 1
                    $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($manyToOneElement->cache));
490
                }
491
492 7
                $metadata->mapManyToOne($mapping);
493
494
            }
495
        }
496
497
        // Evaluate <many-to-many ...> mappings
498 33
        if (isset($xmlRoot->{'many-to-many'})) {
499 10
            foreach ($xmlRoot->{'many-to-many'} as $manyToManyElement) {
500
                $mapping = [
501 10
                    'fieldName' => (string) $manyToManyElement['field'],
502 10
                    'targetEntity' => (string) $manyToManyElement['target-entity']
503
                ];
504
505 10
                if (isset($manyToManyElement['fetch'])) {
506 3
                    $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string) $manyToManyElement['fetch']);
507
                }
508
509 10
                if (isset($manyToManyElement['orphan-removal'])) {
510
                    $mapping['orphanRemoval'] = $this->evaluateBoolean($manyToManyElement['orphan-removal']);
511
                }
512
513 10
                if (isset($manyToManyElement['mapped-by'])) {
514 2
                    $mapping['mappedBy'] = (string) $manyToManyElement['mapped-by'];
515 8
                } else if (isset($manyToManyElement->{'join-table'})) {
516 6
                    if (isset($manyToManyElement['inversed-by'])) {
517 2
                        $mapping['inversedBy'] = (string) $manyToManyElement['inversed-by'];
518
                    }
519
520 6
                    $joinTableElement = $manyToManyElement->{'join-table'};
521
                    $joinTable = [
522 6
                        'name' => (string) $joinTableElement['name']
523
                    ];
524
525 6
                    if (isset($joinTableElement['schema'])) {
526
                        $joinTable['schema'] = (string) $joinTableElement['schema'];
527
                    }
528
529 6
                    foreach ($joinTableElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
530 6
                        $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement);
531
                    }
532
533 6
                    foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} as $joinColumnElement) {
534 6
                        $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement);
535
                    }
536
537 6
                    $mapping['joinTable'] = $joinTable;
538
                }
539
540 10
                if (isset($manyToManyElement->cascade)) {
541 6
                    $mapping['cascade'] = $this->_getCascadeMappings($manyToManyElement->cascade);
542
                }
543
544 10
                if (isset($manyToManyElement->{'order-by'})) {
545
                    $orderBy = [];
546
                    foreach ($manyToManyElement->{'order-by'}->{'order-by-field'} as $orderByField) {
547
                        $orderBy[(string) $orderByField['name']] = (string) $orderByField['direction'];
548
                    }
549
                    $mapping['orderBy'] = $orderBy;
550
                }
551
552 10
                if (isset($manyToManyElement['index-by'])) {
553
                    $mapping['indexBy'] = (string) $manyToManyElement['index-by'];
554 10
                } else if (isset($manyToManyElement->{'index-by'})) {
555
                    throw new \InvalidArgumentException("<index-by /> is not a valid tag");
556
                }
557
558
                // Evaluate second level cache
559 10
                if (isset($manyToManyElement->cache)) {
560
                    $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], $this->cacheToArray($manyToManyElement->cache));
561
                }
562
563 10
                $metadata->mapManyToMany($mapping);
564
            }
565
        }
566
567
        // Evaluate association-overrides
568 33
        if (isset($xmlRoot->{'attribute-overrides'})) {
569 2
            foreach ($xmlRoot->{'attribute-overrides'}->{'attribute-override'} as $overrideElement) {
570 2
                $fieldName = (string) $overrideElement['name'];
571 2
                foreach ($overrideElement->field as $field) {
572 2
                    $mapping = $this->columnToArray($field);
573 2
                    $mapping['fieldName'] = $fieldName;
574 2
                    $metadata->setAttributeOverride($fieldName, $mapping);
575
                }
576
            }
577
        }
578
579
        // Evaluate association-overrides
580 33
        if (isset($xmlRoot->{'association-overrides'})) {
581 4
            foreach ($xmlRoot->{'association-overrides'}->{'association-override'} as $overrideElement) {
582 4
                $fieldName  = (string) $overrideElement['name'];
583 4
                $override   = [];
584
585
                // Check for join-columns
586 4
                if (isset($overrideElement->{'join-columns'})) {
587 2
                    $joinColumns = [];
588 2
                    foreach ($overrideElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
589 2
                        $joinColumns[] = $this->joinColumnToArray($joinColumnElement);
590
                    }
591 2
                    $override['joinColumns'] = $joinColumns;
592
                }
593
594
                // Check for join-table
595 4
                if ($overrideElement->{'join-table'}) {
596 2
                    $joinTable          = null;
0 ignored issues
show
Unused Code introduced by
$joinTable is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
597 2
                    $joinTableElement   = $overrideElement->{'join-table'};
598
599
                    $joinTable = [
600 2
                        'name'      => (string) $joinTableElement['name'],
601 2
                        'schema'    => (string) $joinTableElement['schema']
602
                    ];
603
604 2
                    if (isset($joinTableElement->{'join-columns'})) {
605 2
                        foreach ($joinTableElement->{'join-columns'}->{'join-column'} as $joinColumnElement) {
606 2
                            $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement);
607
                        }
608
                    }
609
610 2
                    if (isset($joinTableElement->{'inverse-join-columns'})) {
611 2
                        foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} as $joinColumnElement) {
612 2
                            $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement);
613
                        }
614
                    }
615
616 2
                    $override['joinTable'] = $joinTable;
617
                }
618
619
                // Check for inversed-by
620 4
                if (isset($overrideElement->{'inversed-by'})) {
621 1
                    $override['inversedBy'] = (string) $overrideElement->{'inversed-by'}['name'];
622
                }
623
624
                // Check for `fetch`
625 4
                if (isset($overrideElement['fetch'])) {
626 1
                    $override['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string) $overrideElement['fetch']);
627
                }
628
629 4
                $metadata->setAssociationOverride($fieldName, $override);
630
            }
631
        }
632
633
        // Evaluate <lifecycle-callbacks...>
634 33
        if (isset($xmlRoot->{'lifecycle-callbacks'})) {
635 3
            foreach ($xmlRoot->{'lifecycle-callbacks'}->{'lifecycle-callback'} as $lifecycleCallback) {
636 3
                $metadata->addLifecycleCallback((string) $lifecycleCallback['method'], constant('Doctrine\ORM\Events::' . (string) $lifecycleCallback['type']));
637
            }
638
        }
639
640
        // Evaluate entity listener
641 33
        if (isset($xmlRoot->{'entity-listeners'})) {
642 4
            foreach ($xmlRoot->{'entity-listeners'}->{'entity-listener'} as $listenerElement) {
643 4
                $className = (string) $listenerElement['class'];
644
                // Evaluate the listener using naming convention.
645 4
                if ($listenerElement->count() === 0) {
646 2
                    EntityListenerBuilder::bindEntityListener($metadata, $className);
0 ignored issues
show
Compatibility introduced by
$metadata of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
647
648 2
                    continue;
649
                }
650
651 2
                foreach ($listenerElement as $callbackElement) {
652 2
                    $eventName   = (string) $callbackElement['type'];
653 2
                    $methodName  = (string) $callbackElement['method'];
654
655 2
                    $metadata->addEntityListener($eventName, $className, $methodName);
656
                }
657
            }
658
        }
659 33
    }
660
661
    /**
662
     * Parses (nested) option elements.
663
     *
664
     * @param SimpleXMLElement $options The XML element.
665
     *
666
     * @return array The options array.
667
     */
668 4
    private function _parseOptions(SimpleXMLElement $options)
669
    {
670 4
        $array = [];
671
672
        /* @var $option SimpleXMLElement */
673 4
        foreach ($options as $option) {
674 4
            if ($option->count()) {
675 3
                $value = $this->_parseOptions($option->children());
676
            } else {
677 4
                $value = (string) $option;
678
            }
679
680 4
            $attributes = $option->attributes();
681
682 4
            if (isset($attributes->name)) {
683 4
                $nameAttribute = (string) $attributes->name;
684 4
                $array[$nameAttribute] = in_array($nameAttribute, ['unsigned', 'fixed'])
685 3
                    ? $this->evaluateBoolean($value)
686 4
                    : $value;
687
            } else {
688 4
                $array[] = $value;
689
            }
690
        }
691
692 4
        return $array;
693
    }
694
695
    /**
696
     * Constructs a joinColumn mapping array based on the information
697
     * found in the given SimpleXMLElement.
698
     *
699
     * @param SimpleXMLElement $joinColumnElement The XML element.
700
     *
701
     * @return array The mapping array.
702
     */
703 11
    private function joinColumnToArray(SimpleXMLElement $joinColumnElement)
704
    {
705
        $joinColumn = [
706 11
            'name' => (string) $joinColumnElement['name'],
707 11
            'referencedColumnName' => (string) $joinColumnElement['referenced-column-name']
708
        ];
709
710 11
        if (isset($joinColumnElement['unique'])) {
711 2
            $joinColumn['unique'] = $this->evaluateBoolean($joinColumnElement['unique']);
712
        }
713
714 11
        if (isset($joinColumnElement['nullable'])) {
715 4
            $joinColumn['nullable'] = $this->evaluateBoolean($joinColumnElement['nullable']);
716
        }
717
718 11
        if (isset($joinColumnElement['on-delete'])) {
719 3
            $joinColumn['onDelete'] = (string) $joinColumnElement['on-delete'];
720
        }
721
722 11
        if (isset($joinColumnElement['column-definition'])) {
723 3
            $joinColumn['columnDefinition'] = (string) $joinColumnElement['column-definition'];
724
        }
725
726 11
        return $joinColumn;
727
    }
728
729
     /**
730
     * Parses the given field as array.
731
     *
732
     * @param SimpleXMLElement $fieldMapping
733
     *
734
     * @return array
735
     */
736 22
    private function columnToArray(SimpleXMLElement $fieldMapping)
737
    {
738
        $mapping = [
739 22
            'fieldName' => (string) $fieldMapping['name'],
740
        ];
741
742 22
        if (isset($fieldMapping['type'])) {
743 18
            $mapping['type'] = (string) $fieldMapping['type'];
744
        }
745
746 22
        if (isset($fieldMapping['column'])) {
747 14
            $mapping['columnName'] = (string) $fieldMapping['column'];
748
        }
749
750 22
        if (isset($fieldMapping['length'])) {
751 9
            $mapping['length'] = (int) $fieldMapping['length'];
752
        }
753
754 22
        if (isset($fieldMapping['precision'])) {
755 1
            $mapping['precision'] = (int) $fieldMapping['precision'];
756
        }
757
758 22
        if (isset($fieldMapping['scale'])) {
759 1
            $mapping['scale'] = (int) $fieldMapping['scale'];
760
        }
761
762 22
        if (isset($fieldMapping['unique'])) {
763 8
            $mapping['unique'] = $this->evaluateBoolean($fieldMapping['unique']);
764
        }
765
766 22
        if (isset($fieldMapping['nullable'])) {
767 7
            $mapping['nullable'] = $this->evaluateBoolean($fieldMapping['nullable']);
768
        }
769
770 22
        if (isset($fieldMapping['version']) && $fieldMapping['version']) {
771 1
            $mapping['version'] = $this->evaluateBoolean($fieldMapping['version']);
772
        }
773
774 22
        if (isset($fieldMapping['column-definition'])) {
775 4
            $mapping['columnDefinition'] = (string) $fieldMapping['column-definition'];
776
        }
777
778 22
        if (isset($fieldMapping->options)) {
779 3
            $mapping['options'] = $this->_parseOptions($fieldMapping->options->children());
780
        }
781
782 22
        return $mapping;
783
    }
784
785
    /**
786
     * Parse / Normalize the cache configuration
787
     *
788
     * @param SimpleXMLElement $cacheMapping
789
     *
790
     * @return array
791
     */
792 2
    private function cacheToArray(SimpleXMLElement $cacheMapping)
793
    {
794 2
        $region = isset($cacheMapping['region']) ? (string) $cacheMapping['region'] : null;
795 2
        $usage  = isset($cacheMapping['usage']) ? strtoupper($cacheMapping['usage']) : null;
796
797 2
        if ($usage && ! defined('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $usage)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $usage of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
798
            throw new \InvalidArgumentException(sprintf('Invalid cache usage "%s"', $usage));
799
        }
800
801 2
        if ($usage) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $usage of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

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

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

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

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