Completed
Push — master ( 056d89...fc4c80 )
by Maciej
10s
created

Doctrine/ODM/MongoDB/Mapping/Driver/XmlDriver.php (4 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Doctrine\ODM\MongoDB\Mapping\Driver;
4
5
use Doctrine\Common\Persistence\Mapping\Driver\FileDriver;
6
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
7
use Doctrine\ODM\MongoDB\Utility\CollectionHelper;
8
9
/**
10
 * XmlDriver is a metadata driver that enables mapping through XML files.
11
 *
12
 * @since       1.0
13
 */
14
class XmlDriver extends FileDriver
15
{
16
    const DEFAULT_FILE_EXTENSION = '.dcm.xml';
17
18
    /**
19
     * {@inheritDoc}
20
     */
21 13
    public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION)
22
    {
23 13
        parent::__construct($locator, $fileExtension);
24 13
    }
25
26
    /**
27
     * {@inheritDoc}
28
     */
29 7
    public function loadMetadataForClass($className, \Doctrine\Common\Persistence\Mapping\ClassMetadata $class)
30
    {
31
        /* @var $class ClassMetadata */
32
        /* @var $xmlRoot \SimpleXMLElement */
33 7
        $xmlRoot = $this->getElement($className);
34 7
        if ( ! $xmlRoot) {
35
            return;
36
        }
37
38 7
        if ($xmlRoot->getName() == 'document') {
39 7
            if (isset($xmlRoot['repository-class'])) {
40 7
                $class->setCustomRepositoryClass((string) $xmlRoot['repository-class']);
41
            }
42 2
        } elseif ($xmlRoot->getName() == 'mapped-superclass') {
43 1
            $class->setCustomRepositoryClass(
44 1
                isset($xmlRoot['repository-class']) ? (string) $xmlRoot['repository-class'] : null
45
            );
46 1
            $class->isMappedSuperclass = true;
47 1
        } elseif ($xmlRoot->getName() == 'embedded-document') {
48 1
            $class->isEmbeddedDocument = true;
49 1
        } elseif ($xmlRoot->getName() == 'query-result-document') {
50 1
            $class->isQueryResultDocument = true;
51
        }
52 7
        if (isset($xmlRoot['db'])) {
53 4
            $class->setDatabase((string) $xmlRoot['db']);
54
        }
55 7
        if (isset($xmlRoot['collection'])) {
56 6
            if (isset($xmlRoot['capped-collection'])) {
57
                $config = array('name' => (string) $xmlRoot['collection']);
58
                $config['capped'] = (bool) $xmlRoot['capped-collection'];
59
                if (isset($xmlRoot['capped-collection-max'])) {
60
                    $config['max'] = (int) $xmlRoot['capped-collection-max'];
61
                }
62
                if (isset($xmlRoot['capped-collection-size'])) {
63
                    $config['size'] = (int) $xmlRoot['capped-collection-size'];
64
                }
65
                $class->setCollection($config);
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface Doctrine\Common\Persistence\Mapping\ClassMetadata as the method setCollection() does only exist in the following implementations of said interface: Doctrine\ODM\MongoDB\Mapping\ClassMetadata.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
66
            } else {
67 6
                $class->setCollection((string) $xmlRoot['collection']);
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface Doctrine\Common\Persistence\Mapping\ClassMetadata as the method setCollection() does only exist in the following implementations of said interface: Doctrine\ODM\MongoDB\Mapping\ClassMetadata.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
68
            }
69
        }
70 7
        if (isset($xmlRoot['writeConcern'])) {
71
            $class->setWriteConcern((string) $xmlRoot['writeConcern']);
72
        }
73 7
        if (isset($xmlRoot['inheritance-type'])) {
74
            $inheritanceType = (string) $xmlRoot['inheritance-type'];
75
            $class->setInheritanceType(constant(ClassMetadata::class . '::INHERITANCE_TYPE_' . $inheritanceType));
76
        }
77 7 View Code Duplication
        if (isset($xmlRoot['change-tracking-policy'])) {
78 1
            $class->setChangeTrackingPolicy(constant(ClassMetadata::class . '::CHANGETRACKING_' . strtoupper((string) $xmlRoot['change-tracking-policy'])));
79
        }
80 7
        if (isset($xmlRoot->{'discriminator-field'})) {
81
            $discrField = $xmlRoot->{'discriminator-field'};
82
            /* XSD only allows for "name", which is consistent with association
83
             * configurations, but fall back to "fieldName" for BC.
84
             */
85
            $class->setDiscriminatorField(
86
                (string) ($discrField['name'] ?? $discrField['fieldName'])
87
            );
88
        }
89 7 View Code Duplication
        if (isset($xmlRoot->{'discriminator-map'})) {
90
            $map = array();
91
            foreach ($xmlRoot->{'discriminator-map'}->{'discriminator-mapping'} as $discrMapElement) {
92
                $map[(string) $discrMapElement['value']] = (string) $discrMapElement['class'];
93
            }
94
            $class->setDiscriminatorMap($map);
95
        }
96 7
        if (isset($xmlRoot->{'default-discriminator-value'})) {
97
            $class->setDefaultDiscriminatorValue((string) $xmlRoot->{'default-discriminator-value'}['value']);
98
        }
99 7
        if (isset($xmlRoot->{'indexes'})) {
100 2
            foreach ($xmlRoot->{'indexes'}->{'index'} as $index) {
101 2
                $this->addIndex($class, $index);
102
            }
103
        }
104 7
        if (isset($xmlRoot->{'shard-key'})) {
105
            $this->setShardKey($class, $xmlRoot->{'shard-key'}[0]);
106
        }
107 7
        if (isset($xmlRoot['read-only']) && 'true' === (string) $xmlRoot['read-only']) {
108
            $class->markReadOnly();
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface Doctrine\Common\Persistence\Mapping\ClassMetadata as the method markReadOnly() does only exist in the following implementations of said interface: Doctrine\ODM\MongoDB\Mapping\ClassMetadata.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
109
        }
110 7
        if (isset($xmlRoot->{'read-preference'})) {
111
            $class->setReadPreference(...$this->transformReadPreference($xmlRoot->{'read-preference'}));
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface Doctrine\Common\Persistence\Mapping\ClassMetadata as the method setReadPreference() does only exist in the following implementations of said interface: Doctrine\ODM\MongoDB\Mapping\ClassMetadata.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
112
        }
113 7
        if (isset($xmlRoot->field)) {
114 7
            foreach ($xmlRoot->field as $field) {
115 7
                $mapping = array();
116 7
                $attributes = $field->attributes();
117 7
                foreach ($attributes as $key => $value) {
118 7
                    $mapping[$key] = (string) $value;
119 7
                    $booleanAttributes = array('id', 'reference', 'embed', 'unique', 'sparse');
120 7
                    if (in_array($key, $booleanAttributes)) {
121 7
                        $mapping[$key] = ('true' === $mapping[$key]);
122
                    }
123
                }
124 7
                if (isset($mapping['id']) && $mapping['id'] === true && isset($mapping['strategy'])) {
125 2
                    $mapping['options'] = array();
126 2
                    if (isset($field->{'id-generator-option'})) {
127 1
                        foreach ($field->{'id-generator-option'} as $generatorOptions) {
128 1
                            $attributesGenerator = iterator_to_array($generatorOptions->attributes());
129 1
                            if (isset($attributesGenerator['name']) && isset($attributesGenerator['value'])) {
130 1
                                $mapping['options'][(string) $attributesGenerator['name']] = (string) $attributesGenerator['value'];
131
                            }
132
                        }
133
                    }
134
                }
135
136 7
                if (isset($attributes['not-saved'])) {
137
                    $mapping['notSaved'] = ('true' === (string) $attributes['not-saved']);
138
                }
139
140 7
                if (isset($attributes['also-load'])) {
141
                    $mapping['alsoLoadFields'] = explode(',', $attributes['also-load']);
142 7
                } elseif (isset($attributes['version'])) {
143
                    $mapping['version'] = ('true' === (string) $attributes['version']);
144 7
                } elseif (isset($attributes['lock'])) {
145
                    $mapping['lock'] = ('true' === (string) $attributes['lock']);
146
                }
147
148 7
                $this->addFieldMapping($class, $mapping);
149
            }
150
        }
151 7
        if (isset($xmlRoot->{'embed-one'})) {
152 1
            foreach ($xmlRoot->{'embed-one'} as $embed) {
153 1
                $this->addEmbedMapping($class, $embed, 'one');
154
            }
155
        }
156 7
        if (isset($xmlRoot->{'embed-many'})) {
157 1
            foreach ($xmlRoot->{'embed-many'} as $embed) {
158 1
                $this->addEmbedMapping($class, $embed, 'many');
159
            }
160
        }
161 7
        if (isset($xmlRoot->{'reference-many'})) {
162 3
            foreach ($xmlRoot->{'reference-many'} as $reference) {
163 3
                $this->addReferenceMapping($class, $reference, 'many');
164
            }
165
        }
166 7
        if (isset($xmlRoot->{'reference-one'})) {
167 2
            foreach ($xmlRoot->{'reference-one'} as $reference) {
168 2
                $this->addReferenceMapping($class, $reference, 'one');
169
            }
170
        }
171 7
        if (isset($xmlRoot->{'lifecycle-callbacks'})) {
172 1
            foreach ($xmlRoot->{'lifecycle-callbacks'}->{'lifecycle-callback'} as $lifecycleCallback) {
173 1
                $class->addLifecycleCallback((string) $lifecycleCallback['method'], constant('Doctrine\ODM\MongoDB\Events::' . (string) $lifecycleCallback['type']));
174
            }
175
        }
176 7
        if (isset($xmlRoot->{'also-load-methods'})) {
177 1
            foreach ($xmlRoot->{'also-load-methods'}->{'also-load-method'} as $alsoLoadMethod) {
178 1
                $class->registerAlsoLoadMethod((string) $alsoLoadMethod['method'], (string) $alsoLoadMethod['field']);
179
            }
180
        }
181 7
    }
182
183 8
    private function addFieldMapping(ClassMetadata $class, $mapping)
184
    {
185 8
        if (isset($mapping['name'])) {
186 8
            $name = $mapping['name'];
187
        } elseif (isset($mapping['fieldName'])) {
188
            $name = $mapping['fieldName'];
189
        } else {
190
            throw new \InvalidArgumentException('Cannot infer a MongoDB name from the mapping');
191
        }
192
193 8
        $class->mapField($mapping);
194
195
        // Index this field if either "index", "unique", or "sparse" are set
196 8
        if ( ! (isset($mapping['index']) || isset($mapping['unique']) || isset($mapping['sparse']))) {
197 8
            return;
198
        }
199
200 1
        $keys = array($name => $mapping['order'] ?? 'asc');
201 1
        $options = array();
202
203 1
        if (isset($mapping['background'])) {
204
            $options['background'] = (boolean) $mapping['background'];
205
        }
206 1
        if (isset($mapping['drop-dups'])) {
207
            $options['dropDups'] = (boolean) $mapping['drop-dups'];
208
        }
209 1
        if (isset($mapping['index-name'])) {
210
            $options['name'] = (string) $mapping['index-name'];
211
        }
212 1
        if (isset($mapping['sparse'])) {
213 1
            $options['sparse'] = (boolean) $mapping['sparse'];
214
        }
215 1
        if (isset($mapping['unique'])) {
216 1
            $options['unique'] = (boolean) $mapping['unique'];
217
        }
218
219 1
        $class->addIndex($keys, $options);
220 1
    }
221
222 1
    private function addEmbedMapping(ClassMetadata $class, $embed, $type)
223
    {
224 1
        $attributes = $embed->attributes();
225 1
        $defaultStrategy = $type == 'one' ? ClassMetadata::STORAGE_STRATEGY_SET : CollectionHelper::DEFAULT_STRATEGY;
226
        $mapping = array(
227 1
            'type'            => $type,
228
            'embedded'        => true,
229 1
            'targetDocument'  => isset($attributes['target-document']) ? (string) $attributes['target-document'] : null,
230 1
            'collectionClass' => isset($attributes['collection-class']) ? (string) $attributes['collection-class'] : null,
231 1
            'name'            => (string) $attributes['field'],
232 1
            'strategy'        => (string) ($attributes['strategy'] ?? $defaultStrategy),
233
        );
234 1
        if (isset($attributes['fieldName'])) {
235
            $mapping['fieldName'] = (string) $attributes['fieldName'];
236
        }
237 1 View Code Duplication
        if (isset($embed->{'discriminator-field'})) {
238
            $attr = $embed->{'discriminator-field'};
239
            $mapping['discriminatorField'] = (string) $attr['name'];
240
        }
241 1 View Code Duplication
        if (isset($embed->{'discriminator-map'})) {
242
            foreach ($embed->{'discriminator-map'}->{'discriminator-mapping'} as $discriminatorMapping) {
243
                $attr = $discriminatorMapping->attributes();
244
                $mapping['discriminatorMap'][(string) $attr['value']] = (string) $attr['class'];
245
            }
246
        }
247 1
        if (isset($embed->{'default-discriminator-value'})) {
248
            $mapping['defaultDiscriminatorValue'] = (string) $embed->{'default-discriminator-value'}['value'];
249
        }
250 1
        if (isset($attributes['not-saved'])) {
251
            $mapping['notSaved'] = ('true' === (string) $attributes['not-saved']);
252
        }
253 1
        if (isset($attributes['also-load'])) {
254
            $mapping['alsoLoadFields'] = explode(',', $attributes['also-load']);
255
        }
256 1
        $this->addFieldMapping($class, $mapping);
257 1
    }
258
259 4
    private function addReferenceMapping(ClassMetadata $class, $reference, $type)
260
    {
261 4
        $cascade = array_keys((array) $reference->cascade);
262 4
        if (1 === count($cascade)) {
263 1
            $cascade = current($cascade) ?: next($cascade);
264
        }
265 4
        $attributes = $reference->attributes();
266 4
        $defaultStrategy = $type == 'one' ? ClassMetadata::STORAGE_STRATEGY_SET : CollectionHelper::DEFAULT_STRATEGY;
267
        $mapping = array(
268 4
            'cascade'          => $cascade,
269 4
            'orphanRemoval'    => isset($attributes['orphan-removal']) ? ('true' === (string) $attributes['orphan-removal']) : false,
270 4
            'type'             => $type,
271
            'reference'        => true,
272 4
            'storeAs'          => (string) ($attributes['store-as'] ?? ClassMetadata::REFERENCE_STORE_AS_DB_REF),
273 4
            'targetDocument'   => isset($attributes['target-document']) ? (string) $attributes['target-document'] : null,
274 4
            'collectionClass'  => isset($attributes['collection-class']) ? (string) $attributes['collection-class'] : null,
275 4
            'name'             => (string) $attributes['field'],
276 4
            'strategy'         => (string) ($attributes['strategy'] ?? $defaultStrategy),
277 4
            'inversedBy'       => isset($attributes['inversed-by']) ? (string) $attributes['inversed-by'] : null,
278 4
            'mappedBy'         => isset($attributes['mapped-by']) ? (string) $attributes['mapped-by'] : null,
279 4
            'repositoryMethod' => isset($attributes['repository-method']) ? (string) $attributes['repository-method'] : null,
280 4
            'limit'            => isset($attributes['limit']) ? (integer) $attributes['limit'] : null,
281 4
            'skip'             => isset($attributes['skip']) ? (integer) $attributes['skip'] : null,
282
            'prime'            => [],
283
        );
284
285 4
        if (isset($attributes['fieldName'])) {
286
            $mapping['fieldName'] = (string) $attributes['fieldName'];
287
        }
288 4 View Code Duplication
        if (isset($reference->{'discriminator-field'})) {
289
            $attr = $reference->{'discriminator-field'};
290
            $mapping['discriminatorField'] = (string) $attr['name'];
291
        }
292 4 View Code Duplication
        if (isset($reference->{'discriminator-map'})) {
293
            foreach ($reference->{'discriminator-map'}->{'discriminator-mapping'} as $discriminatorMapping) {
294
                $attr = $discriminatorMapping->attributes();
295
                $mapping['discriminatorMap'][(string) $attr['value']] = (string) $attr['class'];
296
            }
297
        }
298 4
        if (isset($reference->{'default-discriminator-value'})) {
299
            $mapping['defaultDiscriminatorValue'] = (string) $reference->{'default-discriminator-value'}['value'];
300
        }
301 4 View Code Duplication
        if (isset($reference->{'sort'})) {
302
            foreach ($reference->{'sort'}->{'sort'} as $sort) {
303
                $attr = $sort->attributes();
304
                $mapping['sort'][(string) $attr['field']] = (string) ($attr['order'] ?? 'asc');
305
            }
306
        }
307 4
        if (isset($reference->{'criteria'})) {
308
            foreach ($reference->{'criteria'}->{'criteria'} as $criteria) {
309
                $attr = $criteria->attributes();
310
                $mapping['criteria'][(string) $attr['field']] = (string) $attr['value'];
311
            }
312
        }
313 4
        if (isset($attributes['not-saved'])) {
314
            $mapping['notSaved'] = ('true' === (string) $attributes['not-saved']);
315
        }
316 4
        if (isset($attributes['also-load'])) {
317
            $mapping['alsoLoadFields'] = explode(',', $attributes['also-load']);
318
        }
319 4 View Code Duplication
        if (isset($reference->{'prime'})) {
320 1
            foreach ($reference->{'prime'}->{'field'} as $field) {
321 1
                $attr = $field->attributes();
322 1
                $mapping['prime'][] = (string) $attr['name'];
323
            }
324
        }
325
326 4
        $this->addFieldMapping($class, $mapping);
327 4
    }
328
329 2
    private function addIndex(ClassMetadata $class, \SimpleXmlElement $xmlIndex)
330
    {
331 2
        $attributes = $xmlIndex->attributes();
332
333 2
        $keys = array();
334
335 2
        foreach ($xmlIndex->{'key'} as $key) {
336 2
            $keys[(string) $key['name']] = (string) ($key['order'] ?? 'asc');
337
        }
338
339 2
        $options = array();
340
341 2
        if (isset($attributes['background'])) {
342
            $options['background'] = ('true' === (string) $attributes['background']);
343
        }
344 2
        if (isset($attributes['drop-dups'])) {
345
            $options['dropDups'] = ('true' === (string) $attributes['drop-dups']);
346
        }
347 2
        if (isset($attributes['name'])) {
348
            $options['name'] = (string) $attributes['name'];
349
        }
350 2
        if (isset($attributes['sparse'])) {
351
            $options['sparse'] = ('true' === (string) $attributes['sparse']);
352
        }
353 2
        if (isset($attributes['unique'])) {
354
            $options['unique'] = ('true' === (string) $attributes['unique']);
355
        }
356
357 2 View Code Duplication
        if (isset($xmlIndex->{'option'})) {
358
            foreach ($xmlIndex->{'option'} as $option) {
359
                $value = (string) $option['value'];
360
                if ($value === 'true') {
361
                    $value = true;
362
                } elseif ($value === 'false') {
363
                    $value = false;
364
                } elseif (is_numeric($value)) {
365
                    $value = preg_match('/^[-]?\d+$/', $value) ? (integer) $value : (float) $value;
366
                }
367
                $options[(string) $option['name']] = $value;
368
            }
369
        }
370
371 2
        if (isset($xmlIndex->{'partial-filter-expression'})) {
372 2
            $partialFilterExpressionMapping = $xmlIndex->{'partial-filter-expression'};
373
374 2
            if (isset($partialFilterExpressionMapping->and)) {
375 2
                foreach ($partialFilterExpressionMapping->and as $and) {
376 2
                    if (! isset($and->field)) {
377 1
                        continue;
378
                    }
379
380 2
                    $partialFilterExpression = $this->getPartialFilterExpression($and->field);
381 2
                    if (! $partialFilterExpression) {
382
                        continue;
383
                    }
384
385 2
                    $options['partialFilterExpression']['$and'][] = $partialFilterExpression;
386
                }
387 1
            } elseif (isset($partialFilterExpressionMapping->field)) {
388 1
                $partialFilterExpression = $this->getPartialFilterExpression($partialFilterExpressionMapping->field);
389
390 1
                if ($partialFilterExpression) {
391 1
                    $options['partialFilterExpression'] = $partialFilterExpression;
392
                }
393
            }
394
        }
395
396 2
        $class->addIndex($keys, $options);
397 2
    }
398
399 2
    private function getPartialFilterExpression(\SimpleXMLElement $fields)
400
    {
401 2
        $partialFilterExpression = [];
402 2
        foreach ($fields as $field) {
403 2
            $operator = (string) $field['operator'] ?: null;
404
405 2
            if (! isset($field['value'])) {
406 1
                if (! isset($field->field)) {
407
                    continue;
408
                }
409
410 1
                $nestedExpression = $this->getPartialFilterExpression($field->field);
411 1
                if (! $nestedExpression) {
412
                    continue;
413
                }
414
415 1
                $value = $nestedExpression;
416
            } else {
417 2
                $value = trim((string) $field['value']);
418
            }
419
420 2
            if ($value === 'true') {
421
                $value = true;
422 2
            } elseif ($value === 'false') {
423
                $value = false;
424 2
            } elseif (is_numeric($value)) {
425 1
                $value = preg_match('/^[-]?\d+$/', $value) ? (integer) $value : (float) $value;
426
            }
427
428 2
            $partialFilterExpression[(string) $field['name']] = $operator ? ['$' . $operator => $value] : $value;
429
        }
430
431 2
        return $partialFilterExpression;
432
    }
433
434 1
    private function setShardKey(ClassMetadata $class, \SimpleXmlElement $xmlShardkey)
435
    {
436 1
        $attributes = $xmlShardkey->attributes();
437
438 1
        $keys = array();
439 1
        $options = array();
440 1
        foreach ($xmlShardkey->{'key'} as $key) {
441 1
            $keys[(string) $key['name']] = (string) ($key['order'] ?? 'asc');
442
        }
443
444 1
        if (isset($attributes['unique'])) {
445 1
            $options['unique'] = ('true' === (string) $attributes['unique']);
446
        }
447
448 1
        if (isset($attributes['numInitialChunks'])) {
449 1
            $options['numInitialChunks'] = (int) $attributes['numInitialChunks'];
450
        }
451
452 1 View Code Duplication
        if (isset($xmlShardkey->{'option'})) {
453
            foreach ($xmlShardkey->{'option'} as $option) {
454
                $value = (string) $option['value'];
455
                if ($value === 'true') {
456
                    $value = true;
457
                } elseif ($value === 'false') {
458
                    $value = false;
459
                } elseif (is_numeric($value)) {
460
                    $value = preg_match('/^[-]?\d+$/', $value) ? (integer) $value : (float) $value;
461
                }
462
                $options[(string) $option['name']] = $value;
463
            }
464
        }
465
466 1
        $class->setShardKey($keys, $options);
467 1
    }
468
469
    /**
470
     * Parses <read-preference> to a format suitable for the underlying driver.
471
     *
472
     * list($readPreference, $tags) = $this->transformReadPreference($xml->{read-preference});
473
     *
474
     * @param \SimpleXMLElement $xmlReadPreference
475
     * @return array
476
     */
477
    private function transformReadPreference($xmlReadPreference)
478
    {
479
        $tags = null;
480
        if (isset($xmlReadPreference->{'tag-set'})) {
481
            $tags = [];
482
            foreach ($xmlReadPreference->{'tag-set'} as $tagSet) {
483
                $set = [];
484
                foreach ($tagSet->tag as $tag) {
485
                    $set[(string) $tag['name']] = (string) $tag['value'];
486
                }
487
                $tags[] = $set;
488
            }
489
        }
490
        return [(string) $xmlReadPreference['mode'], $tags];
491
    }
492
493
    /**
494
     * {@inheritDoc}
495
     */
496 7
    protected function loadMappingFile($file)
497
    {
498 7
        $result = array();
499 7
        $xmlElement = simplexml_load_file($file);
500
501 7
        foreach (array('document', 'embedded-document', 'mapped-superclass', 'query-result-document') as $type) {
502 7
            if (isset($xmlElement->$type)) {
503 7
                foreach ($xmlElement->$type as $documentElement) {
504 7
                    $documentName = (string) $documentElement['name'];
505 7
                    $result[$documentName] = $documentElement;
506
                }
507
            }
508
        }
509
510 7
        return $result;
511
    }
512
}
513