XmlDriver::addIndex()   F
last analyzed

Complexity

Conditions 19
Paths 320

Size

Total Lines 66

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 38.0371

Importance

Changes 0
Metric Value
dl 0
loc 66
ccs 25
cts 40
cp 0.625
rs 2.1833
c 0
b 0
f 0
cc 19
nc 320
nop 2
crap 38.0371

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
declare(strict_types=1);
4
5
namespace Doctrine\ODM\MongoDB\Mapping\Driver;
6
7
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
8
use Doctrine\ODM\MongoDB\Mapping\MappingException;
9
use Doctrine\ODM\MongoDB\Utility\CollectionHelper;
10
use Doctrine\Persistence\Mapping\Driver\FileDriver;
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 class_exists;
19
use function constant;
20
use function count;
21
use function current;
22
use function explode;
23
use function implode;
24
use function in_array;
25
use function interface_exists;
26
use function is_numeric;
27
use function iterator_to_array;
28
use function libxml_clear_errors;
29
use function libxml_get_errors;
30
use function libxml_use_internal_errors;
31
use function next;
32
use function preg_match;
33
use function simplexml_load_file;
34
use function sprintf;
35
use function strtoupper;
36
use function trim;
37
38
/**
39
 * XmlDriver is a metadata driver that enables mapping through XML files.
40
 *
41
 * @method SimpleXMLElement getElement(string $className)
42
 */
43
class XmlDriver extends FileDriver
44
{
45
    public const DEFAULT_FILE_EXTENSION = '.dcm.xml';
46
47
    private const DEFAULT_GRIDFS_MAPPINGS = [
48
        'length' => [
49
            'name' => 'length',
50
            'type' => 'int',
51
            'notSaved' => true,
52
        ],
53
        'chunk-size' => [
54
            'name' => 'chunkSize',
55
            'type' => 'int',
56
            'notSaved' => true,
57
        ],
58
        'filename' => [
59
            'name' => 'filename',
60
            'type' => 'string',
61
            'notSaved' => true,
62
        ],
63
        'upload-date' => [
64
            'name' => 'uploadDate',
65
            'type' => 'date',
66
            'notSaved' => true,
67
        ],
68
    ];
69
70
    /**
71
     * {@inheritDoc}
72
     */
73 46
    public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION)
74
    {
75 46
        parent::__construct($locator, $fileExtension);
76 46
    }
77
78
    /**
79
     * {@inheritDoc}
80
     */
81 18
    public function loadMetadataForClass($className, \Doctrine\Persistence\Mapping\ClassMetadata $class)
82
    {
83 18
        assert($class instanceof ClassMetadata);
84 18
        $xmlRoot = $this->getElement($className);
85
86 16
        if ($xmlRoot->getName() === 'document') {
87 9
            if (isset($xmlRoot['repository-class'])) {
88 9
                $class->setCustomRepositoryClass((string) $xmlRoot['repository-class']);
89
            }
90 11
        } elseif ($xmlRoot->getName() === 'mapped-superclass') {
91 3
            $class->setCustomRepositoryClass(
92 3
                isset($xmlRoot['repository-class']) ? (string) $xmlRoot['repository-class'] : null
93
            );
94 3
            $class->isMappedSuperclass = true;
95 8
        } elseif ($xmlRoot->getName() === 'embedded-document') {
96 1
            $class->isEmbeddedDocument = true;
97 8
        } elseif ($xmlRoot->getName() === 'query-result-document') {
98 1
            $class->isQueryResultDocument = true;
99 7
        } elseif ($xmlRoot->getName() === 'view') {
100 5
            if (isset($xmlRoot['repository-class'])) {
101 4
                $class->setCustomRepositoryClass((string) $xmlRoot['repository-class']);
102
            }
103
104 5
            if (! isset($xmlRoot['root-class'])) {
105 1
                throw MappingException::viewWithoutRootClass($className);
106
            }
107
108 4
            $rootClass = (string) $xmlRoot['root-class'];
109 4
            if (! class_exists($rootClass)) {
110 1
                throw MappingException::viewRootClassNotFound($className, $rootClass);
111
            }
112
113 3
            $class->markViewOf($rootClass);
114 2
        } elseif ($xmlRoot->getName() === 'gridfs-file') {
115 2
            $class->isFile = true;
116
117 2
            if (isset($xmlRoot['chunk-size-bytes'])) {
118 1
                $class->setChunkSizeBytes((int) $xmlRoot['chunk-size-bytes']);
119
            }
120
121 2
            if (isset($xmlRoot['repository-class'])) {
122 1
                $class->setCustomRepositoryClass((string) $xmlRoot['repository-class']);
123
            }
124
        }
125
126 14
        if (isset($xmlRoot['db'])) {
127 4
            $class->setDatabase((string) $xmlRoot['db']);
128
        }
129
130 14
        if (isset($xmlRoot['collection'])) {
131 6
            if (isset($xmlRoot['capped-collection'])) {
132
                $config           = ['name' => (string) $xmlRoot['collection']];
133
                $config['capped'] = (bool) $xmlRoot['capped-collection'];
134
                if (isset($xmlRoot['capped-collection-max'])) {
135
                    $config['max'] = (int) $xmlRoot['capped-collection-max'];
136
                }
137
                if (isset($xmlRoot['capped-collection-size'])) {
138
                    $config['size'] = (int) $xmlRoot['capped-collection-size'];
139
                }
140
                $class->setCollection($config);
141
            } else {
142 6
                $class->setCollection((string) $xmlRoot['collection']);
143
            }
144
        }
145 14
        if (isset($xmlRoot['bucket-name'])) {
146
            $class->setBucketName((string) $xmlRoot['bucket-name']);
147
        }
148 14
        if (isset($xmlRoot['view'])) {
149 1
            $class->setCollection((string) $xmlRoot['view']);
150
        }
151 14
        if (isset($xmlRoot['write-concern'])) {
152
            $class->setWriteConcern((string) $xmlRoot['write-concern']);
153
        }
154 14
        if (isset($xmlRoot['inheritance-type'])) {
155
            $inheritanceType = (string) $xmlRoot['inheritance-type'];
156
            $class->setInheritanceType(constant(ClassMetadata::class . '::INHERITANCE_TYPE_' . $inheritanceType));
157
        }
158 14
        if (isset($xmlRoot['change-tracking-policy'])) {
159 1
            $class->setChangeTrackingPolicy(constant(ClassMetadata::class . '::CHANGETRACKING_' . strtoupper((string) $xmlRoot['change-tracking-policy'])));
160
        }
161 14
        if (isset($xmlRoot->{'discriminator-field'})) {
162
            $discrField = $xmlRoot->{'discriminator-field'};
163
            $class->setDiscriminatorField((string) $discrField['name']);
164
        }
165 14
        if (isset($xmlRoot->{'discriminator-map'})) {
166
            $map = [];
167
            foreach ($xmlRoot->{'discriminator-map'}->{'discriminator-mapping'} as $discrMapElement) {
168
                $map[(string) $discrMapElement['value']] = (string) $discrMapElement['class'];
169
            }
170
            $class->setDiscriminatorMap($map);
171
        }
172 14
        if (isset($xmlRoot->{'default-discriminator-value'})) {
173
            $class->setDefaultDiscriminatorValue((string) $xmlRoot->{'default-discriminator-value'}['value']);
174
        }
175 14
        if (isset($xmlRoot->{'indexes'})) {
176 1
            foreach ($xmlRoot->{'indexes'}->{'index'} as $index) {
177 1
                $this->addIndex($class, $index);
178
            }
179
        }
180 14
        if (isset($xmlRoot->{'shard-key'})) {
181
            $this->setShardKey($class, $xmlRoot->{'shard-key'}[0]);
182
        }
183 14
        if (isset($xmlRoot['read-only']) && (string) $xmlRoot['read-only'] === 'true') {
184
            $class->markReadOnly();
185
        }
186 14
        if (isset($xmlRoot->{'read-preference'})) {
187
            $class->setReadPreference(...$this->transformReadPreference($xmlRoot->{'read-preference'}));
188
        }
189
190 14
        if (isset($xmlRoot->id)) {
191 14
            $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...
192
            $mapping = [
193 14
                'id' => true,
194
                'fieldName' => 'id',
195
            ];
196
197
            /** @var SimpleXMLElement $attributes */
198 14
            $attributes = $field->attributes();
199 14
            foreach ($attributes as $key => $value) {
200 4
                $mapping[$key] = (string) $value;
201
            }
202
203 14
            if (isset($attributes['field-name'])) {
204 2
                $mapping['fieldName'] = (string) $attributes['field-name'];
205
            }
206
207 14
            if (isset($mapping['strategy'])) {
208 2
                $mapping['options'] = [];
209 2
                if (isset($field->{'generator-option'})) {
210 1
                    foreach ($field->{'generator-option'} as $generatorOptions) {
211 1
                        $attributesGenerator = iterator_to_array($generatorOptions->attributes());
212 1
                        if (! isset($attributesGenerator['name']) || ! isset($attributesGenerator['value'])) {
213
                            continue;
214
                        }
215
216 1
                        $mapping['options'][(string) $attributesGenerator['name']] = (string) $attributesGenerator['value'];
217
                    }
218
                }
219
            }
220
221 14
            $this->addFieldMapping($class, $mapping);
222
        }
223
224 14
        if (isset($xmlRoot->field)) {
225 8
            foreach ($xmlRoot->field as $field) {
226 8
                $mapping    = [];
227 8
                $attributes = $field->attributes();
228 8
                foreach ($attributes as $key => $value) {
229 8
                    $mapping[$key]     = (string) $value;
230 8
                    $booleanAttributes = ['reference', 'embed', 'unique', 'sparse'];
231 8
                    if (! in_array($key, $booleanAttributes)) {
232 8
                        continue;
233
                    }
234
235 1
                    $mapping[$key] = ($mapping[$key] === 'true');
236
                }
237
238 8
                if (isset($attributes['not-saved'])) {
239 1
                    $mapping['notSaved'] = ((string) $attributes['not-saved'] === 'true');
240
                }
241
242 8
                if (isset($attributes['field-name'])) {
243 2
                    $mapping['fieldName'] = (string) $attributes['field-name'];
244
                }
245
246 8
                if (isset($attributes['also-load'])) {
247 1
                    $mapping['alsoLoadFields'] = explode(',', (string) $attributes['also-load']);
248 7
                } elseif (isset($attributes['version'])) {
249
                    $mapping['version'] = ((string) $attributes['version'] === 'true');
250 7
                } elseif (isset($attributes['lock'])) {
251
                    $mapping['lock'] = ((string) $attributes['lock'] === 'true');
252
                }
253
254 8
                $this->addFieldMapping($class, $mapping);
255
            }
256
        }
257
258 14
        $this->addGridFSMappings($class, $xmlRoot);
259
260 14
        if (isset($xmlRoot->{'embed-one'})) {
261 1
            foreach ($xmlRoot->{'embed-one'} as $embed) {
262 1
                $this->addEmbedMapping($class, $embed, 'one');
263
            }
264
        }
265 14
        if (isset($xmlRoot->{'embed-many'})) {
266 1
            foreach ($xmlRoot->{'embed-many'} as $embed) {
267 1
                $this->addEmbedMapping($class, $embed, 'many');
268
            }
269
        }
270 14
        if (isset($xmlRoot->{'reference-many'})) {
271 3
            foreach ($xmlRoot->{'reference-many'} as $reference) {
272 3
                $this->addReferenceMapping($class, $reference, 'many');
273
            }
274
        }
275 14
        if (isset($xmlRoot->{'reference-one'})) {
276 2
            foreach ($xmlRoot->{'reference-one'} as $reference) {
277 2
                $this->addReferenceMapping($class, $reference, 'one');
278
            }
279
        }
280 14
        if (isset($xmlRoot->{'lifecycle-callbacks'})) {
281 1
            foreach ($xmlRoot->{'lifecycle-callbacks'}->{'lifecycle-callback'} as $lifecycleCallback) {
282 1
                $class->addLifecycleCallback((string) $lifecycleCallback['method'], constant('Doctrine\ODM\MongoDB\Events::' . (string) $lifecycleCallback['type']));
283
            }
284
        }
285 14
        if (! isset($xmlRoot->{'also-load-methods'})) {
286 14
            return;
287
        }
288
289 1
        foreach ($xmlRoot->{'also-load-methods'}->{'also-load-method'} as $alsoLoadMethod) {
290 1
            $class->registerAlsoLoadMethod((string) $alsoLoadMethod['method'], (string) $alsoLoadMethod['field']);
291
        }
292 1
    }
293
294 14
    private function addFieldMapping(ClassMetadata $class, array $mapping) : void
295
    {
296 14
        if (isset($mapping['name'])) {
297 11
            $name = $mapping['name'];
298 14
        } elseif (isset($mapping['fieldName'])) {
299 14
            $name = $mapping['fieldName'];
300
        } else {
301
            throw new InvalidArgumentException('Cannot infer a MongoDB name from the mapping');
302
        }
303
304 14
        $class->mapField($mapping);
305
306
        // Index this field if either "index", "unique", or "sparse" are set
307 14
        if (! (isset($mapping['index']) || isset($mapping['unique']) || isset($mapping['sparse']))) {
308 14
            return;
309
        }
310
311 1
        $keys    = [$name => $mapping['order'] ?? 'asc'];
312 1
        $options = [];
313
314 1
        if (isset($mapping['background'])) {
315
            $options['background'] = (bool) $mapping['background'];
316
        }
317 1
        if (isset($mapping['index-name'])) {
318
            $options['name'] = (string) $mapping['index-name'];
319
        }
320 1
        if (isset($mapping['sparse'])) {
321 1
            $options['sparse'] = (bool) $mapping['sparse'];
322
        }
323 1
        if (isset($mapping['unique'])) {
324 1
            $options['unique'] = (bool) $mapping['unique'];
325
        }
326
327 1
        $class->addIndex($keys, $options);
328 1
    }
329
330 2
    private function addEmbedMapping(ClassMetadata $class, SimpleXMLElement $embed, string $type) : void
331
    {
332 2
        $attributes      = $embed->attributes();
333 2
        $defaultStrategy = $type === 'one' ? ClassMetadata::STORAGE_STRATEGY_SET : CollectionHelper::DEFAULT_STRATEGY;
334
        $mapping         = [
335 2
            'type'            => $type,
336
            'embedded'        => true,
337 2
            'targetDocument'  => isset($attributes['target-document']) ? (string) $attributes['target-document'] : null,
338 2
            'collectionClass' => isset($attributes['collection-class']) ? (string) $attributes['collection-class'] : null,
339 2
            'name'            => (string) $attributes['field'],
340 2
            'strategy'        => (string) ($attributes['strategy'] ?? $defaultStrategy),
341
        ];
342 2
        if (isset($attributes['field-name'])) {
343
            $mapping['fieldName'] = (string) $attributes['field-name'];
344
        }
345 2
        if (isset($embed->{'discriminator-field'})) {
346
            $attr                          = $embed->{'discriminator-field'};
347
            $mapping['discriminatorField'] = (string) $attr['name'];
348
        }
349 2
        if (isset($embed->{'discriminator-map'})) {
350
            foreach ($embed->{'discriminator-map'}->{'discriminator-mapping'} as $discriminatorMapping) {
351
                $attr                                                 = $discriminatorMapping->attributes();
352
                $mapping['discriminatorMap'][(string) $attr['value']] = (string) $attr['class'];
353
            }
354
        }
355 2
        if (isset($embed->{'default-discriminator-value'})) {
356
            $mapping['defaultDiscriminatorValue'] = (string) $embed->{'default-discriminator-value'}['value'];
357
        }
358 2
        if (isset($attributes['not-saved'])) {
359
            $mapping['notSaved'] = ((string) $attributes['not-saved'] === 'true');
360
        }
361 2
        if (isset($attributes['also-load'])) {
362
            $mapping['alsoLoadFields'] = explode(',', $attributes['also-load']);
363
        }
364 2
        $this->addFieldMapping($class, $mapping);
365 2
    }
366
367 3
    private function addReferenceMapping(ClassMetadata $class, $reference, string $type) : void
368
    {
369 3
        $cascade = array_keys((array) $reference->cascade);
370 3
        if (count($cascade) === 1) {
371 1
            $cascade = current($cascade) ?: next($cascade);
372
        }
373 3
        $attributes      = $reference->attributes();
374 3
        $defaultStrategy = $type === 'one' ? ClassMetadata::STORAGE_STRATEGY_SET : CollectionHelper::DEFAULT_STRATEGY;
375
        $mapping         = [
376 3
            'cascade'          => $cascade,
377 3
            'orphanRemoval'    => isset($attributes['orphan-removal']) ? ((string) $attributes['orphan-removal'] === 'true') : false,
378 3
            'type'             => $type,
379
            'reference'        => true,
380 3
            'storeAs'          => (string) ($attributes['store-as'] ?? ClassMetadata::REFERENCE_STORE_AS_DB_REF),
381 3
            'targetDocument'   => isset($attributes['target-document']) ? (string) $attributes['target-document'] : null,
382 3
            'collectionClass'  => isset($attributes['collection-class']) ? (string) $attributes['collection-class'] : null,
383 3
            'name'             => (string) $attributes['field'],
384 3
            'strategy'         => (string) ($attributes['strategy'] ?? $defaultStrategy),
385 3
            'inversedBy'       => isset($attributes['inversed-by']) ? (string) $attributes['inversed-by'] : null,
386 3
            'mappedBy'         => isset($attributes['mapped-by']) ? (string) $attributes['mapped-by'] : null,
387 3
            'repositoryMethod' => isset($attributes['repository-method']) ? (string) $attributes['repository-method'] : null,
388 3
            'limit'            => isset($attributes['limit']) ? (int) $attributes['limit'] : null,
389 3
            'skip'             => isset($attributes['skip']) ? (int) $attributes['skip'] : null,
390
            'prime'            => [],
391
        ];
392
393 3
        if (isset($attributes['field-name'])) {
394
            $mapping['fieldName'] = (string) $attributes['field-name'];
395
        }
396 3
        if (isset($reference->{'discriminator-field'})) {
397
            $attr                          = $reference->{'discriminator-field'};
398
            $mapping['discriminatorField'] = (string) $attr['name'];
399
        }
400 3
        if (isset($reference->{'discriminator-map'})) {
401
            foreach ($reference->{'discriminator-map'}->{'discriminator-mapping'} as $discriminatorMapping) {
402
                $attr                                                 = $discriminatorMapping->attributes();
403
                $mapping['discriminatorMap'][(string) $attr['value']] = (string) $attr['class'];
404
            }
405
        }
406 3
        if (isset($reference->{'default-discriminator-value'})) {
407
            $mapping['defaultDiscriminatorValue'] = (string) $reference->{'default-discriminator-value'}['value'];
408
        }
409 3
        if (isset($reference->{'sort'})) {
410
            foreach ($reference->{'sort'}->{'sort'} as $sort) {
411
                $attr                                     = $sort->attributes();
412
                $mapping['sort'][(string) $attr['field']] = (string) ($attr['order'] ?? 'asc');
413
            }
414
        }
415 3
        if (isset($reference->{'criteria'})) {
416
            foreach ($reference->{'criteria'}->{'criteria'} as $criteria) {
417
                $attr                                         = $criteria->attributes();
418
                $mapping['criteria'][(string) $attr['field']] = (string) $attr['value'];
419
            }
420
        }
421 3
        if (isset($attributes['not-saved'])) {
422
            $mapping['notSaved'] = ((string) $attributes['not-saved'] === 'true');
423
        }
424 3
        if (isset($attributes['also-load'])) {
425
            $mapping['alsoLoadFields'] = explode(',', $attributes['also-load']);
426
        }
427 3
        if (isset($reference->{'prime'})) {
428 1
            foreach ($reference->{'prime'}->{'field'} as $field) {
429 1
                $attr               = $field->attributes();
430 1
                $mapping['prime'][] = (string) $attr['name'];
431
            }
432
        }
433
434 3
        $this->addFieldMapping($class, $mapping);
435 3
    }
436
437 1
    private function addIndex(ClassMetadata $class, SimpleXMLElement $xmlIndex) : void
438
    {
439 1
        $attributes = $xmlIndex->attributes();
440
441 1
        $keys = [];
442
443 1
        foreach ($xmlIndex->{'key'} as $key) {
444 1
            $keys[(string) $key['name']] = (string) ($key['order'] ?? 'asc');
445
        }
446
447 1
        $options = [];
448
449 1
        if (isset($attributes['background'])) {
450
            $options['background'] = ((string) $attributes['background'] === 'true');
451
        }
452 1
        if (isset($attributes['name'])) {
453
            $options['name'] = (string) $attributes['name'];
454
        }
455 1
        if (isset($attributes['sparse'])) {
456
            $options['sparse'] = ((string) $attributes['sparse'] === 'true');
457
        }
458 1
        if (isset($attributes['unique'])) {
459
            $options['unique'] = ((string) $attributes['unique'] === 'true');
460
        }
461
462 1
        if (isset($xmlIndex->{'option'})) {
463
            foreach ($xmlIndex->{'option'} as $option) {
464
                $value = (string) $option['value'];
465
                if ($value === 'true') {
466
                    $value = true;
467
                } elseif ($value === 'false') {
468
                    $value = false;
469
                } elseif (is_numeric($value)) {
470
                    $value = preg_match('/^[-]?\d+$/', $value) ? (int) $value : (float) $value;
471
                }
472
                $options[(string) $option['name']] = $value;
473
            }
474
        }
475
476 1
        if (isset($xmlIndex->{'partial-filter-expression'})) {
477 1
            $partialFilterExpressionMapping = $xmlIndex->{'partial-filter-expression'};
478
479 1
            if (isset($partialFilterExpressionMapping->and)) {
480 1
                foreach ($partialFilterExpressionMapping->and as $and) {
481 1
                    if (! isset($and->field)) {
482
                        continue;
483
                    }
484
485 1
                    $partialFilterExpression = $this->getPartialFilterExpression($and->field);
486 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...
487
                        continue;
488
                    }
489
490 1
                    $options['partialFilterExpression']['$and'][] = $partialFilterExpression;
491
                }
492 1
            } elseif (isset($partialFilterExpressionMapping->field)) {
493 1
                $partialFilterExpression = $this->getPartialFilterExpression($partialFilterExpressionMapping->field);
494
495 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...
496 1
                    $options['partialFilterExpression'] = $partialFilterExpression;
497
                }
498
            }
499
        }
500
501 1
        $class->addIndex($keys, $options);
502 1
    }
503
504 1
    private function getPartialFilterExpression(SimpleXMLElement $fields) : array
505
    {
506 1
        $partialFilterExpression = [];
507 1
        foreach ($fields as $field) {
508 1
            $operator = (string) $field['operator'] ?: null;
509
510 1
            if (! isset($field['value'])) {
511 1
                if (! isset($field->field)) {
512
                    continue;
513
                }
514
515 1
                $nestedExpression = $this->getPartialFilterExpression($field->field);
516 1
                if (! $nestedExpression) {
517
                    continue;
518
                }
519
520 1
                $value = $nestedExpression;
521
            } else {
522 1
                $value = trim((string) $field['value']);
523
            }
524
525 1
            if ($value === 'true') {
526
                $value = true;
527 1
            } elseif ($value === 'false') {
528
                $value = false;
529 1
            } elseif (is_numeric($value)) {
530 1
                $value = preg_match('/^[-]?\d+$/', $value) ? (int) $value : (float) $value;
531
            }
532
533 1
            $partialFilterExpression[(string) $field['name']] = $operator ? ['$' . $operator => $value] : $value;
534
        }
535
536 1
        return $partialFilterExpression;
537
    }
538
539 1
    private function setShardKey(ClassMetadata $class, SimpleXMLElement $xmlShardkey) : void
540
    {
541 1
        $attributes = $xmlShardkey->attributes();
542
543 1
        $keys    = [];
544 1
        $options = [];
545 1
        foreach ($xmlShardkey->{'key'} as $key) {
546 1
            $keys[(string) $key['name']] = (string) ($key['order'] ?? 'asc');
547
        }
548
549 1
        if (isset($attributes['unique'])) {
550 1
            $options['unique'] = ((string) $attributes['unique'] === 'true');
551
        }
552
553 1
        if (isset($attributes['numInitialChunks'])) {
554 1
            $options['numInitialChunks'] = (int) $attributes['numInitialChunks'];
555
        }
556
557 1
        if (isset($xmlShardkey->{'option'})) {
558
            foreach ($xmlShardkey->{'option'} as $option) {
559
                $value = (string) $option['value'];
560
                if ($value === 'true') {
561
                    $value = true;
562
                } elseif ($value === 'false') {
563
                    $value = false;
564
                } elseif (is_numeric($value)) {
565
                    $value = preg_match('/^[-]?\d+$/', $value) ? (int) $value : (float) $value;
566
                }
567
                $options[(string) $option['name']] = $value;
568
            }
569
        }
570
571 1
        $class->setShardKey($keys, $options);
572 1
    }
573
574
    /**
575
     * Parses <read-preference> to a format suitable for the underlying driver.
576
     *
577
     * list($readPreference, $tags) = $this->transformReadPreference($xml->{read-preference});
578
     */
579
    private function transformReadPreference(SimpleXMLElement $xmlReadPreference) : array
580
    {
581
        $tags = null;
582
        if (isset($xmlReadPreference->{'tag-set'})) {
583
            $tags = [];
584
            foreach ($xmlReadPreference->{'tag-set'} as $tagSet) {
585
                $set = [];
586
                foreach ($tagSet->tag as $tag) {
587
                    $set[(string) $tag['name']] = (string) $tag['value'];
588
                }
589
                $tags[] = $set;
590
            }
591
        }
592
593
        return [(string) $xmlReadPreference['mode'], $tags];
594
    }
595
596
    /**
597
     * {@inheritDoc}
598
     */
599 18
    protected function loadMappingFile($file) : array
600
    {
601 18
        $result = [];
602
603 18
        $this->validateSchema($file);
604
605 16
        $xmlElement = simplexml_load_file($file);
606
607 16
        foreach (['document', 'embedded-document', 'mapped-superclass', 'query-result-document', 'view', 'gridfs-file'] as $type) {
608 16
            if (! isset($xmlElement->$type)) {
609 16
                continue;
610
            }
611
612 16
            foreach ($xmlElement->$type as $documentElement) {
613 16
                $documentName          = (string) $documentElement['name'];
614 16
                $result[$documentName] = $documentElement;
615
            }
616
        }
617
618 16
        return $result;
619
    }
620
621 18
    private function validateSchema(string $filename) : void
622
    {
623 18
        $document = new DOMDocument();
624 18
        $document->load($filename);
625
626 18
        $previousUseErrors = libxml_use_internal_errors(true);
627
628
        try {
629 18
            libxml_clear_errors();
630
631 18
            if (! $document->schemaValidate(__DIR__ . '/../../../../../../doctrine-mongo-mapping.xsd')) {
632 2
                throw MappingException::xmlMappingFileInvalid($filename, $this->formatErrors(libxml_get_errors()));
633
            }
634 16
        } finally {
635 18
            libxml_use_internal_errors($previousUseErrors);
636
        }
637 16
    }
638
639
    /**
640
     * @param LibXMLError[] $xmlErrors
641
     */
642 2
    private function formatErrors(array $xmlErrors) : string
643
    {
644
        return implode("\n", array_map(static function (LibXMLError $error) : string {
645 2
            return sprintf('Line %d:%d: %s', $error->line, $error->column, $error->message);
646 2
        }, $xmlErrors));
647
    }
648
649 14
    private function addGridFSMappings(ClassMetadata $class, SimpleXMLElement $xmlRoot) : void
650
    {
651 14
        if (! $class->isFile) {
652 12
            return;
653
        }
654
655 2
        foreach (self::DEFAULT_GRIDFS_MAPPINGS as $name => $mapping) {
656 2
            if (! isset($xmlRoot->{$name})) {
657 1
                continue;
658
            }
659
660 1
            if (isset($xmlRoot->{$name}->attributes()['field-name'])) {
661 1
                $mapping['fieldName'] = (string) $xmlRoot->{$name}->attributes()['field-name'];
662
            }
663
664 1
            $this->addFieldMapping($class, $mapping);
665
        }
666
667 2
        if (! isset($xmlRoot->metadata)) {
668 1
            return;
669
        }
670
671 1
        $xmlRoot->metadata->addAttribute('field', 'metadata');
672 1
        $this->addEmbedMapping($class, $xmlRoot->metadata, 'one');
673 1
    }
674
}
675
676
interface_exists(ClassMetadata::class);
677