Completed
Push — master ( 5e6dee...56408e )
by Andreas
24:01 queued 06:28
created

XmlDriver::setShardKey()   B

Complexity

Conditions 10
Paths 16

Size

Total Lines 34

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 16.8468

Importance

Changes 0
Metric Value
dl 0
loc 34
ccs 13
cts 22
cp 0.5909
rs 7.6666
c 0
b 0
f 0
cc 10
nc 16
nop 2
crap 16.8468

How to fix   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
declare(strict_types=1);
4
5
namespace Doctrine\ODM\MongoDB\Mapping\Driver;
6
7
use Doctrine\Common\Persistence\Mapping\Driver\FileDriver;
8
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
9
use Doctrine\ODM\MongoDB\Mapping\MappingException;
10
use Doctrine\ODM\MongoDB\Utility\CollectionHelper;
11
use DOMDocument;
12
use InvalidArgumentException;
13
use LibXMLError;
14
use SimpleXMLElement;
15
use function array_keys;
16
use function array_map;
17
use function assert;
18
use function constant;
19
use function count;
20
use function current;
21
use function explode;
22
use function implode;
23
use function in_array;
24
use function is_numeric;
25
use function iterator_to_array;
26
use function libxml_clear_errors;
27
use function libxml_get_errors;
28
use function libxml_use_internal_errors;
29
use function next;
30
use function preg_match;
31
use function simplexml_load_file;
32
use function sprintf;
33
use function strtoupper;
34
use function trim;
35
36
/**
37
 * XmlDriver is a metadata driver that enables mapping through XML files.
38
 *
39
 * @method SimpleXMLElement getElement(string $className)
40
 */
41
class XmlDriver extends FileDriver
42
{
43
    public const DEFAULT_FILE_EXTENSION = '.dcm.xml';
44
45
    private const DEFAULT_GRIDFS_MAPPINGS = [
46
        'length' => [
47
            'name' => 'length',
48
            'type' => 'int',
49
            'notSaved' => true,
50
        ],
51
        'chunk-size' => [
52
            'name' => 'chunkSize',
53
            'type' => 'int',
54
            'notSaved' => true,
55
        ],
56
        'filename' => [
57
            'name' => 'filename',
58
            'type' => 'string',
59
            'notSaved' => true,
60
        ],
61
        'upload-date' => [
62
            'name' => 'uploadDate',
63
            'type' => 'date',
64
            'notSaved' => true,
65
        ],
66
    ];
67
68
    /**
69
     * {@inheritDoc}
70
     */
71 17
    public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION)
72
    {
73 17
        parent::__construct($locator, $fileExtension);
74 17
    }
75
76
    /**
77
     * {@inheritDoc}
78
     */
79 12
    public function loadMetadataForClass($className, \Doctrine\Common\Persistence\Mapping\ClassMetadata $class)
80
    {
81 12
        assert($class instanceof ClassMetadata);
82 12
        $xmlRoot = $this->getElement($className);
83
84 10
        if ($xmlRoot->getName() === 'document') {
85 9
            if (isset($xmlRoot['repository-class'])) {
86 9
                $class->setCustomRepositoryClass((string) $xmlRoot['repository-class']);
87
            }
88 3
        } elseif ($xmlRoot->getName() === 'mapped-superclass') {
89 1
            $class->setCustomRepositoryClass(
90 1
                isset($xmlRoot['repository-class']) ? (string) $xmlRoot['repository-class'] : null
91
            );
92 1
            $class->isMappedSuperclass = true;
93 2
        } elseif ($xmlRoot->getName() === 'embedded-document') {
94 1
            $class->isEmbeddedDocument = true;
95 2
        } elseif ($xmlRoot->getName() === 'query-result-document') {
96 1
            $class->isQueryResultDocument = true;
97 1
        } elseif ($xmlRoot->getName() === 'gridfs-file') {
98 1
            $class->isFile = true;
99
100 1
            if (isset($xmlRoot['chunk-size-bytes'])) {
101 1
                $class->setChunkSizeBytes((int) $xmlRoot['chunk-size-bytes']);
102
            }
103
        }
104
105 10
        if (isset($xmlRoot['db'])) {
106 4
            $class->setDatabase((string) $xmlRoot['db']);
107
        }
108
109 10
        if (isset($xmlRoot['collection'])) {
110 6
            if (isset($xmlRoot['capped-collection'])) {
111
                $config           = ['name' => (string) $xmlRoot['collection']];
112
                $config['capped'] = (bool) $xmlRoot['capped-collection'];
113
                if (isset($xmlRoot['capped-collection-max'])) {
114
                    $config['max'] = (int) $xmlRoot['capped-collection-max'];
115
                }
116
                if (isset($xmlRoot['capped-collection-size'])) {
117
                    $config['size'] = (int) $xmlRoot['capped-collection-size'];
118
                }
119
                $class->setCollection($config);
120
            } else {
121 6
                $class->setCollection((string) $xmlRoot['collection']);
122
            }
123
        }
124 10
        if (isset($xmlRoot['bucket-name'])) {
125
            $class->setBucketName((string) $xmlRoot['bucket-name']);
126
        }
127 10
        if (isset($xmlRoot['write-concern'])) {
128
            $class->setWriteConcern((string) $xmlRoot['write-concern']);
129
        }
130 10
        if (isset($xmlRoot['inheritance-type'])) {
131
            $inheritanceType = (string) $xmlRoot['inheritance-type'];
132
            $class->setInheritanceType(constant(ClassMetadata::class . '::INHERITANCE_TYPE_' . $inheritanceType));
133
        }
134 10
        if (isset($xmlRoot['change-tracking-policy'])) {
135 1
            $class->setChangeTrackingPolicy(constant(ClassMetadata::class . '::CHANGETRACKING_' . strtoupper((string) $xmlRoot['change-tracking-policy'])));
136
        }
137 10
        if (isset($xmlRoot->{'discriminator-field'})) {
138
            $discrField = $xmlRoot->{'discriminator-field'};
139
            $class->setDiscriminatorField((string) $discrField['name']);
140
        }
141 10
        if (isset($xmlRoot->{'discriminator-map'})) {
142
            $map = [];
143
            foreach ($xmlRoot->{'discriminator-map'}->{'discriminator-mapping'} as $discrMapElement) {
144
                $map[(string) $discrMapElement['value']] = (string) $discrMapElement['class'];
145
            }
146
            $class->setDiscriminatorMap($map);
147
        }
148 10
        if (isset($xmlRoot->{'default-discriminator-value'})) {
149
            $class->setDefaultDiscriminatorValue((string) $xmlRoot->{'default-discriminator-value'}['value']);
150
        }
151 10
        if (isset($xmlRoot->{'indexes'})) {
152 1
            foreach ($xmlRoot->{'indexes'}->{'index'} as $index) {
153 1
                $this->addIndex($class, $index);
154
            }
155
        }
156 10
        if (isset($xmlRoot->{'shard-key'})) {
157
            $this->setShardKey($class, $xmlRoot->{'shard-key'}[0]);
158
        }
159 10
        if (isset($xmlRoot['read-only']) && (string) $xmlRoot['read-only'] === 'true') {
160
            $class->markReadOnly();
161
        }
162 10
        if (isset($xmlRoot->{'read-preference'})) {
163
            $class->setReadPreference(...$this->transformReadPreference($xmlRoot->{'read-preference'}));
164
        }
165
166 10
        if (isset($xmlRoot->id)) {
167 8
            $field   = $xmlRoot->id;
0 ignored issues
show
Bug introduced by
The property id does not seem to exist in SimpleXMLElement.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
168
            $mapping = [
169 8
                'id' => true,
170
                'fieldName' => 'id',
171
            ];
172
173
            /** @var SimpleXMLElement $attributes */
174 8
            $attributes = $field->attributes();
175 8
            foreach ($attributes as $key => $value) {
176 2
                $mapping[$key] = (string) $value;
177
            }
178
179 8
            if (isset($attributes['field-name'])) {
180
                $mapping['fieldName'] = (string) $attributes['field-name'];
181
            }
182
183 8
            if (isset($mapping['strategy'])) {
184 2
                $mapping['options'] = [];
185 2
                if (isset($field->{'generator-option'})) {
186 1
                    foreach ($field->{'generator-option'} as $generatorOptions) {
187 1
                        $attributesGenerator = iterator_to_array($generatorOptions->attributes());
188 1
                        if (! isset($attributesGenerator['name']) || ! isset($attributesGenerator['value'])) {
189
                            continue;
190
                        }
191
192 1
                        $mapping['options'][(string) $attributesGenerator['name']] = (string) $attributesGenerator['value'];
193
                    }
194
                }
195
            }
196
197 8
            $this->addFieldMapping($class, $mapping);
198
        }
199
200 10
        if (isset($xmlRoot->field)) {
201 5
            foreach ($xmlRoot->field as $field) {
202 5
                $mapping    = [];
203 5
                $attributes = $field->attributes();
204 5
                foreach ($attributes as $key => $value) {
205 5
                    $mapping[$key]     = (string) $value;
206 5
                    $booleanAttributes = ['reference', 'embed', 'unique', 'sparse'];
207 5
                    if (! in_array($key, $booleanAttributes)) {
208 5
                        continue;
209
                    }
210
211 1
                    $mapping[$key] = ($mapping[$key] === 'true');
212
                }
213
214 5
                if (isset($attributes['not-saved'])) {
215 1
                    $mapping['notSaved'] = ((string) $attributes['not-saved'] === 'true');
216
                }
217
218 5
                if (isset($attributes['field-name'])) {
219 2
                    $mapping['fieldName'] = (string) $attributes['field-name'];
220
                }
221
222 5
                if (isset($attributes['also-load'])) {
223 1
                    $mapping['alsoLoadFields'] = explode(',', (string) $attributes['also-load']);
224 4
                } elseif (isset($attributes['version'])) {
225
                    $mapping['version'] = ((string) $attributes['version'] === 'true');
226 4
                } elseif (isset($attributes['lock'])) {
227
                    $mapping['lock'] = ((string) $attributes['lock'] === 'true');
228
                }
229
230 5
                $this->addFieldMapping($class, $mapping);
231
            }
232
        }
233
234 9
        $this->addGridFSMappings($class, $xmlRoot);
235
236 9
        if (isset($xmlRoot->{'embed-one'})) {
237 1
            foreach ($xmlRoot->{'embed-one'} as $embed) {
238 1
                $this->addEmbedMapping($class, $embed, 'one');
239
            }
240
        }
241 9
        if (isset($xmlRoot->{'embed-many'})) {
242 1
            foreach ($xmlRoot->{'embed-many'} as $embed) {
243 1
                $this->addEmbedMapping($class, $embed, 'many');
244
            }
245
        }
246 9
        if (isset($xmlRoot->{'reference-many'})) {
247 3
            foreach ($xmlRoot->{'reference-many'} as $reference) {
248 3
                $this->addReferenceMapping($class, $reference, 'many');
249
            }
250
        }
251 9
        if (isset($xmlRoot->{'reference-one'})) {
252 2
            foreach ($xmlRoot->{'reference-one'} as $reference) {
253 2
                $this->addReferenceMapping($class, $reference, 'one');
254
            }
255
        }
256 9
        if (isset($xmlRoot->{'lifecycle-callbacks'})) {
257 1
            foreach ($xmlRoot->{'lifecycle-callbacks'}->{'lifecycle-callback'} as $lifecycleCallback) {
258 1
                $class->addLifecycleCallback((string) $lifecycleCallback['method'], constant('Doctrine\ODM\MongoDB\Events::' . (string) $lifecycleCallback['type']));
259
            }
260
        }
261 9
        if (! isset($xmlRoot->{'also-load-methods'})) {
262 9
            return;
263
        }
264
265 1
        foreach ($xmlRoot->{'also-load-methods'}->{'also-load-method'} as $alsoLoadMethod) {
266 1
            $class->registerAlsoLoadMethod((string) $alsoLoadMethod['method'], (string) $alsoLoadMethod['field']);
267
        }
268 1
    }
269
270 10
    private function addFieldMapping(ClassMetadata $class, array $mapping) : void
271
    {
272 10
        if (isset($mapping['name'])) {
273 8
            $name = $mapping['name'];
274 10
        } elseif (isset($mapping['fieldName'])) {
275 10
            $name = $mapping['fieldName'];
276
        } else {
277
            throw new InvalidArgumentException('Cannot infer a MongoDB name from the mapping');
278
        }
279
280 10
        $class->mapField($mapping);
281
282
        // Index this field if either "index", "unique", or "sparse" are set
283 10
        if (! (isset($mapping['index']) || isset($mapping['unique']) || isset($mapping['sparse']))) {
284 10
            return;
285
        }
286
287 1
        $keys    = [$name => $mapping['order'] ?? 'asc'];
288 1
        $options = [];
289
290 1
        if (isset($mapping['background'])) {
291
            $options['background'] = (bool) $mapping['background'];
292
        }
293 1
        if (isset($mapping['index-name'])) {
294
            $options['name'] = (string) $mapping['index-name'];
295
        }
296 1
        if (isset($mapping['sparse'])) {
297 1
            $options['sparse'] = (bool) $mapping['sparse'];
298
        }
299 1
        if (isset($mapping['unique'])) {
300 1
            $options['unique'] = (bool) $mapping['unique'];
301
        }
302
303 1
        $class->addIndex($keys, $options);
304 1
    }
305
306 2
    private function addEmbedMapping(ClassMetadata $class, SimpleXMLElement $embed, string $type) : void
307
    {
308 2
        $attributes      = $embed->attributes();
309 2
        $defaultStrategy = $type === 'one' ? ClassMetadata::STORAGE_STRATEGY_SET : CollectionHelper::DEFAULT_STRATEGY;
310
        $mapping         = [
311 2
            'type'            => $type,
312
            'embedded'        => true,
313 2
            'targetDocument'  => isset($attributes['target-document']) ? (string) $attributes['target-document'] : null,
314 2
            'collectionClass' => isset($attributes['collection-class']) ? (string) $attributes['collection-class'] : null,
315 2
            'name'            => (string) $attributes['field'],
316 2
            'strategy'        => (string) ($attributes['strategy'] ?? $defaultStrategy),
317
        ];
318 2
        if (isset($attributes['field-name'])) {
319
            $mapping['fieldName'] = (string) $attributes['field-name'];
320
        }
321 2
        if (isset($embed->{'discriminator-field'})) {
322
            $attr                          = $embed->{'discriminator-field'};
323
            $mapping['discriminatorField'] = (string) $attr['name'];
324
        }
325 2
        if (isset($embed->{'discriminator-map'})) {
326
            foreach ($embed->{'discriminator-map'}->{'discriminator-mapping'} as $discriminatorMapping) {
327
                $attr                                                 = $discriminatorMapping->attributes();
328
                $mapping['discriminatorMap'][(string) $attr['value']] = (string) $attr['class'];
329
            }
330
        }
331 2
        if (isset($embed->{'default-discriminator-value'})) {
332
            $mapping['defaultDiscriminatorValue'] = (string) $embed->{'default-discriminator-value'}['value'];
333
        }
334 2
        if (isset($attributes['not-saved'])) {
335
            $mapping['notSaved'] = ((string) $attributes['not-saved'] === 'true');
336
        }
337 2
        if (isset($attributes['also-load'])) {
338
            $mapping['alsoLoadFields'] = explode(',', $attributes['also-load']);
339
        }
340 2
        $this->addFieldMapping($class, $mapping);
341 2
    }
342
343 3
    private function addReferenceMapping(ClassMetadata $class, $reference, string $type) : void
344
    {
345 3
        $cascade = array_keys((array) $reference->cascade);
346 3
        if (count($cascade) === 1) {
347 1
            $cascade = current($cascade) ?: next($cascade);
348
        }
349 3
        $attributes      = $reference->attributes();
350 3
        $defaultStrategy = $type === 'one' ? ClassMetadata::STORAGE_STRATEGY_SET : CollectionHelper::DEFAULT_STRATEGY;
351
        $mapping         = [
352 3
            'cascade'          => $cascade,
353 3
            'orphanRemoval'    => isset($attributes['orphan-removal']) ? ((string) $attributes['orphan-removal'] === 'true') : false,
354 3
            'type'             => $type,
355
            'reference'        => true,
356 3
            'storeAs'          => (string) ($attributes['store-as'] ?? ClassMetadata::REFERENCE_STORE_AS_DB_REF),
357 3
            'targetDocument'   => isset($attributes['target-document']) ? (string) $attributes['target-document'] : null,
358 3
            'collectionClass'  => isset($attributes['collection-class']) ? (string) $attributes['collection-class'] : null,
359 3
            'name'             => (string) $attributes['field'],
360 3
            'strategy'         => (string) ($attributes['strategy'] ?? $defaultStrategy),
361 3
            'inversedBy'       => isset($attributes['inversed-by']) ? (string) $attributes['inversed-by'] : null,
362 3
            'mappedBy'         => isset($attributes['mapped-by']) ? (string) $attributes['mapped-by'] : null,
363 3
            'repositoryMethod' => isset($attributes['repository-method']) ? (string) $attributes['repository-method'] : null,
364 3
            'limit'            => isset($attributes['limit']) ? (int) $attributes['limit'] : null,
365 3
            'skip'             => isset($attributes['skip']) ? (int) $attributes['skip'] : null,
366
            'prime'            => [],
367
        ];
368
369 3
        if (isset($attributes['field-name'])) {
370
            $mapping['fieldName'] = (string) $attributes['field-name'];
371
        }
372 3
        if (isset($reference->{'discriminator-field'})) {
373
            $attr                          = $reference->{'discriminator-field'};
374
            $mapping['discriminatorField'] = (string) $attr['name'];
375
        }
376 3
        if (isset($reference->{'discriminator-map'})) {
377
            foreach ($reference->{'discriminator-map'}->{'discriminator-mapping'} as $discriminatorMapping) {
378
                $attr                                                 = $discriminatorMapping->attributes();
379
                $mapping['discriminatorMap'][(string) $attr['value']] = (string) $attr['class'];
380
            }
381
        }
382 3
        if (isset($reference->{'default-discriminator-value'})) {
383
            $mapping['defaultDiscriminatorValue'] = (string) $reference->{'default-discriminator-value'}['value'];
384
        }
385 3
        if (isset($reference->{'sort'})) {
386
            foreach ($reference->{'sort'}->{'sort'} as $sort) {
387
                $attr                                     = $sort->attributes();
388
                $mapping['sort'][(string) $attr['field']] = (string) ($attr['order'] ?? 'asc');
389
            }
390
        }
391 3
        if (isset($reference->{'criteria'})) {
392
            foreach ($reference->{'criteria'}->{'criteria'} as $criteria) {
393
                $attr                                         = $criteria->attributes();
394
                $mapping['criteria'][(string) $attr['field']] = (string) $attr['value'];
395
            }
396
        }
397 3
        if (isset($attributes['not-saved'])) {
398
            $mapping['notSaved'] = ((string) $attributes['not-saved'] === 'true');
399
        }
400 3
        if (isset($attributes['also-load'])) {
401
            $mapping['alsoLoadFields'] = explode(',', $attributes['also-load']);
402
        }
403 3
        if (isset($reference->{'prime'})) {
404 1
            foreach ($reference->{'prime'}->{'field'} as $field) {
405 1
                $attr               = $field->attributes();
406 1
                $mapping['prime'][] = (string) $attr['name'];
407
            }
408
        }
409
410 3
        $this->addFieldMapping($class, $mapping);
411 3
    }
412
413 1
    private function addIndex(ClassMetadata $class, SimpleXMLElement $xmlIndex) : void
414
    {
415 1
        $attributes = $xmlIndex->attributes();
416
417 1
        $keys = [];
418
419 1
        foreach ($xmlIndex->{'key'} as $key) {
420 1
            $keys[(string) $key['name']] = (string) ($key['order'] ?? 'asc');
421
        }
422
423 1
        $options = [];
424
425 1
        if (isset($attributes['background'])) {
426
            $options['background'] = ((string) $attributes['background'] === 'true');
427
        }
428 1
        if (isset($attributes['name'])) {
429
            $options['name'] = (string) $attributes['name'];
430
        }
431 1
        if (isset($attributes['sparse'])) {
432
            $options['sparse'] = ((string) $attributes['sparse'] === 'true');
433
        }
434 1
        if (isset($attributes['unique'])) {
435
            $options['unique'] = ((string) $attributes['unique'] === 'true');
436
        }
437
438 1
        if (isset($xmlIndex->{'option'})) {
439
            foreach ($xmlIndex->{'option'} as $option) {
440
                $value = (string) $option['value'];
441
                if ($value === 'true') {
442
                    $value = true;
443
                } elseif ($value === 'false') {
444
                    $value = false;
445
                } elseif (is_numeric($value)) {
446
                    $value = preg_match('/^[-]?\d+$/', $value) ? (int) $value : (float) $value;
447
                }
448
                $options[(string) $option['name']] = $value;
449
            }
450
        }
451
452 1
        if (isset($xmlIndex->{'partial-filter-expression'})) {
453 1
            $partialFilterExpressionMapping = $xmlIndex->{'partial-filter-expression'};
454
455 1
            if (isset($partialFilterExpressionMapping->and)) {
456 1
                foreach ($partialFilterExpressionMapping->and as $and) {
457 1
                    if (! isset($and->field)) {
458
                        continue;
459
                    }
460
461 1
                    $partialFilterExpression = $this->getPartialFilterExpression($and->field);
462 1
                    if (! $partialFilterExpression) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $partialFilterExpression of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
463
                        continue;
464
                    }
465
466 1
                    $options['partialFilterExpression']['$and'][] = $partialFilterExpression;
467
                }
468 1
            } elseif (isset($partialFilterExpressionMapping->field)) {
469 1
                $partialFilterExpression = $this->getPartialFilterExpression($partialFilterExpressionMapping->field);
470
471 1
                if ($partialFilterExpression) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $partialFilterExpression of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
472 1
                    $options['partialFilterExpression'] = $partialFilterExpression;
473
                }
474
            }
475
        }
476
477 1
        $class->addIndex($keys, $options);
478 1
    }
479
480 1
    private function getPartialFilterExpression(SimpleXMLElement $fields) : array
481
    {
482 1
        $partialFilterExpression = [];
483 1
        foreach ($fields as $field) {
484 1
            $operator = (string) $field['operator'] ?: null;
485
486 1
            if (! isset($field['value'])) {
487 1
                if (! isset($field->field)) {
488
                    continue;
489
                }
490
491 1
                $nestedExpression = $this->getPartialFilterExpression($field->field);
492 1
                if (! $nestedExpression) {
493
                    continue;
494
                }
495
496 1
                $value = $nestedExpression;
497
            } else {
498 1
                $value = trim((string) $field['value']);
499
            }
500
501 1
            if ($value === 'true') {
502
                $value = true;
503 1
            } elseif ($value === 'false') {
504
                $value = false;
505 1
            } elseif (is_numeric($value)) {
506 1
                $value = preg_match('/^[-]?\d+$/', $value) ? (int) $value : (float) $value;
507
            }
508
509 1
            $partialFilterExpression[(string) $field['name']] = $operator ? ['$' . $operator => $value] : $value;
510
        }
511
512 1
        return $partialFilterExpression;
513
    }
514
515 1
    private function setShardKey(ClassMetadata $class, SimpleXMLElement $xmlShardkey) : void
516
    {
517 1
        $attributes = $xmlShardkey->attributes();
518
519 1
        $keys    = [];
520 1
        $options = [];
521 1
        foreach ($xmlShardkey->{'key'} as $key) {
522 1
            $keys[(string) $key['name']] = (string) ($key['order'] ?? 'asc');
523
        }
524
525 1
        if (isset($attributes['unique'])) {
526 1
            $options['unique'] = ((string) $attributes['unique'] === 'true');
527
        }
528
529 1
        if (isset($attributes['numInitialChunks'])) {
530 1
            $options['numInitialChunks'] = (int) $attributes['numInitialChunks'];
531
        }
532
533 1
        if (isset($xmlShardkey->{'option'})) {
534
            foreach ($xmlShardkey->{'option'} as $option) {
535
                $value = (string) $option['value'];
536
                if ($value === 'true') {
537
                    $value = true;
538
                } elseif ($value === 'false') {
539
                    $value = false;
540
                } elseif (is_numeric($value)) {
541
                    $value = preg_match('/^[-]?\d+$/', $value) ? (int) $value : (float) $value;
542
                }
543
                $options[(string) $option['name']] = $value;
544
            }
545
        }
546
547 1
        $class->setShardKey($keys, $options);
548 1
    }
549
550
    /**
551
     * Parses <read-preference> to a format suitable for the underlying driver.
552
     *
553
     * list($readPreference, $tags) = $this->transformReadPreference($xml->{read-preference});
554
     */
555
    private function transformReadPreference(SimpleXMLElement $xmlReadPreference) : array
556
    {
557
        $tags = null;
558
        if (isset($xmlReadPreference->{'tag-set'})) {
559
            $tags = [];
560
            foreach ($xmlReadPreference->{'tag-set'} as $tagSet) {
561
                $set = [];
562
                foreach ($tagSet->tag as $tag) {
563
                    $set[(string) $tag['name']] = (string) $tag['value'];
564
                }
565
                $tags[] = $set;
566
            }
567
        }
568
569
        return [(string) $xmlReadPreference['mode'], $tags];
570
    }
571
572
    /**
573
     * {@inheritDoc}
574
     */
575 12
    protected function loadMappingFile($file) : array
576
    {
577 12
        $result = [];
578
579 12
        $this->validateSchema($file);
580
581 10
        $xmlElement = simplexml_load_file($file);
582
583 10
        foreach (['document', 'embedded-document', 'mapped-superclass', 'query-result-document', 'gridfs-file'] as $type) {
584 10
            if (! isset($xmlElement->$type)) {
585 10
                continue;
586
            }
587
588 10
            foreach ($xmlElement->$type as $documentElement) {
589 10
                $documentName          = (string) $documentElement['name'];
590 10
                $result[$documentName] = $documentElement;
591
            }
592
        }
593
594 10
        return $result;
595
    }
596
597 12
    private function validateSchema(string $filename) : void
598
    {
599 12
        $document = new DOMDocument();
600 12
        $document->load($filename);
601
602 12
        $previousUseErrors = libxml_use_internal_errors(true);
603
604
        try {
605 12
            libxml_clear_errors();
606
607 12
            if (! $document->schemaValidate(__DIR__ . '/../../../../../../doctrine-mongo-mapping.xsd')) {
608 2
                throw MappingException::xmlMappingFileInvalid($filename, $this->formatErrors(libxml_get_errors()));
609
            }
610 10
        } finally {
611 12
            libxml_use_internal_errors($previousUseErrors);
612
        }
613 10
    }
614
615
    /**
616
     * @param LibXMLError[] $xmlErrors
617
     */
618 2
    private function formatErrors(array $xmlErrors) : string
619
    {
620
        return implode("\n", array_map(static function (LibXMLError $error) : string {
621 2
            return sprintf('Line %d:%d: %s', $error->line, $error->column, $error->message);
622 2
        }, $xmlErrors));
623
    }
624
625 9
    private function addGridFSMappings(ClassMetadata $class, SimpleXMLElement $xmlRoot) : void
626
    {
627 9
        if (! $class->isFile) {
628 8
            return;
629
        }
630
631 1
        foreach (self::DEFAULT_GRIDFS_MAPPINGS as $name => $mapping) {
632 1
            if (! isset($xmlRoot->{$name})) {
633
                continue;
634
            }
635
636 1
            if (isset($xmlRoot->{$name}->attributes()['field-name'])) {
637 1
                $mapping['fieldName'] = (string) $xmlRoot->{$name}->attributes()['field-name'];
638
            }
639
640 1
            $this->addFieldMapping($class, $mapping);
641
        }
642
643 1
        if (! isset($xmlRoot->metadata)) {
644
            return;
645
        }
646
647 1
        $xmlRoot->metadata->addAttribute('field', 'metadata');
648 1
        $this->addEmbedMapping($class, $xmlRoot->metadata, 'one');
649 1
    }
650
}
651