Completed
Pull Request — master (#11)
by Eric
62:01
created

loadPropertyMetadataXmlAttribute()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 11
Ratio 100 %

Code Coverage

Tests 3
CRAP Score 3

Importance

Changes 0
Metric Value
dl 11
loc 11
rs 9.4285
c 0
b 0
f 0
ccs 3
cts 3
cp 1
cc 3
eloc 6
nc 2
nop 2
crap 3
1
<?php
2
3
/*
4
 * This file is part of the Ivory Serializer package.
5
 *
6
 * (c) Eric GELOEN <[email protected]>
7
 *
8
 * For the full copyright and license information, please read the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Ivory\Serializer\Mapping\Loader;
13
14
use Ivory\Serializer\Exclusion\ExclusionPolicy;
15
use Ivory\Serializer\Mapping\ClassMetadataInterface;
16
use Ivory\Serializer\Mapping\PropertyMetadata;
17
use Ivory\Serializer\Mapping\PropertyMetadataInterface;
18
use Ivory\Serializer\Type\Parser\TypeParser;
19
use Ivory\Serializer\Type\Parser\TypeParserInterface;
20
21
/**
22
 * @author GeLo <[email protected]>
23
 */
24
abstract class AbstractClassMetadataLoader implements ClassMetadataLoaderInterface
25
{
26
    /**
27
     * @var TypeParserInterface
28
     */
29
    private $typeParser;
30
31
    /**
32
     * @var mixed[][]
33
     */
34
    private $data = [];
35
36
    /**
37
     * @param TypeParserInterface|null $typeParser
38
     */
39 1536
    public function __construct(TypeParserInterface $typeParser = null)
40
    {
41 1536
        $this->typeParser = $typeParser ?: new TypeParser();
42 1536
    }
43
44
    /**
45
     * {@inheritdoc}
46
     */
47 1248
    public function loadClassMetadata(ClassMetadataInterface $classMetadata)
48
    {
49 1248
        $class = $classMetadata->getName();
50
51 1248
        if (!array_key_exists($class, $this->data)) {
52 1248
            $this->data[$class] = $this->loadData($class);
53 816
        }
54
55 1224
        if (!is_array($data = $this->data[$class])) {
56 18
            return false;
57
        }
58
59 1206
        $this->doLoadClassMetadata($classMetadata, $data);
60
61 1056
        return true;
62
    }
63
64
    /**
65
     * @param string $class
66
     *
67
     * @return mixed[]|null
68
     */
69
    abstract protected function loadData($class);
70
71
    /**
72
     * @param ClassMetadataInterface $classMetadata
73
     * @param mixed[]                $data
74
     */
75 1206
    private function doLoadClassMetadata(ClassMetadataInterface $classMetadata, array $data)
76
    {
77 1206
        if (!isset($data['properties']) || empty($data['properties'])) {
78 9
            throw new \InvalidArgumentException(sprintf(
79 9
                'No mapping properties found for "%s".',
80 9
                $classMetadata->getName()
81 6
            ));
82
        }
83
84 1197
        $policy = $this->getExclusionPolicy($data);
85 1188
        $readableClass = $this->getReadable($data);
86 1182
        $writableClass = $this->getWritable($data);
87 1176
        $properties = $classMetadata->getProperties();
88
89 1176
        foreach ($data['properties'] as $property => $value) {
90 1176
            $propertyMetadata = $classMetadata->getProperty($property) ?: new PropertyMetadata($property);
91 1176
            $this->loadPropertyMetadata($propertyMetadata, $value, $readableClass, $writableClass);
0 ignored issues
show
Bug introduced by
It seems like $readableClass defined by $this->getReadable($data) on line 85 can also be of type null; however, Ivory\Serializer\Mapping...:loadPropertyMetadata() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Bug introduced by
It seems like $writableClass defined by $this->getWritable($data) on line 86 can also be of type null; however, Ivory\Serializer\Mapping...:loadPropertyMetadata() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
92
93 1083
            if ($this->isPropertyMetadataExposed($value, $policy)) {
94 1083
                $properties[$property] = $propertyMetadata;
95 722
            }
96 722
        }
97
98 1083
        if (($order = $this->getOrder($data, $properties)) !== null) {
99 108
            $properties = $this->sortProperties($properties, $order);
0 ignored issues
show
Bug introduced by
It seems like $order defined by $this->getOrder($data, $properties) on line 98 can also be of type array; however, Ivory\Serializer\Mapping...oader::sortProperties() does only seem to accept string|array<integer,string>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
100 72
        }
101
102 1056
        $classMetadata->setProperties($properties);
103 1056
104
        if (array_key_exists('xml_root', $data)) {
105
            $this->loadClassMetadataXmlRoot($classMetadata, $data['xml_root']);
106
        }
107
    }
108
109
    /**
110
     * @param PropertyMetadataInterface $propertyMetadata
111 1176
     * @param mixed                     $data
112
     * @param bool                      $classReadable
113
     * @param bool                      $classWritable
114
     */
115
    private function loadPropertyMetadata(
116
        PropertyMetadataInterface $propertyMetadata,
117 1176
        $data,
118 42
        $classReadable,
119 28
        $classWritable
120
    ) {
121 1176
        if (!is_array($data)) {
122 1167
            $data = [];
123
        }
124 1164
125 42
        $propertyMetadata->setReadable($this->getReadable($data, $classReadable));
0 ignored issues
show
Bug introduced by
It seems like $this->getReadable($data, $classReadable) targeting Ivory\Serializer\Mapping...taLoader::getReadable() can also be of type null; however, Ivory\Serializer\Mapping...nterface::setReadable() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
126 24
        $propertyMetadata->setWritable($this->getWritable($data, $classWritable));
0 ignored issues
show
Bug introduced by
It seems like $this->getWritable($data, $classWritable) targeting Ivory\Serializer\Mapping...taLoader::getWritable() can also be of type null; however, Ivory\Serializer\Mapping...nterface::setWritable() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
127
128 1158
        if (array_key_exists('exclude', $data)) {
129 42
            $this->validatePropertyMetadataExclude($data['exclude']);
130 24
        }
131
132 1152
        if (array_key_exists('expose', $data)) {
133 117
            $this->validatePropertyMetadataExpose($data['expose']);
134 72
        }
135
136 1143
        if (array_key_exists('alias', $data)) {
137 339
            $this->loadPropertyMetadataAlias($propertyMetadata, $data['alias']);
138 220
        }
139
140 1134
        if (array_key_exists('type', $data)) {
141 45
            $this->loadPropertyMetadataType($propertyMetadata, $data['type']);
142 24
        }
143
144 1125
        if (array_key_exists('accessor', $data)) {
145 45
            $this->loadPropertyMetadataAccessor($propertyMetadata, $data['accessor']);
146 24
        }
147
148 1116
        if (array_key_exists('mutator', $data)) {
149 93
            $this->loadPropertyMetadataMutator($propertyMetadata, $data['mutator']);
150 56
        }
151
152 1107
        if (array_key_exists('since', $data)) {
153 93
            $this->loadPropertyMetadataSinceVersion($propertyMetadata, $data['since']);
154 56
        }
155
156 1098
        if (array_key_exists('until', $data)) {
157 66
            $this->loadPropertyMetadataUntilVersion($propertyMetadata, $data['until']);
158 40
        }
159
160 1092
        if (array_key_exists('max_depth', $data)) {
161 105
            $this->loadPropertyMetadataMaxDepth($propertyMetadata, $data['max_depth']);
162 64
        }
163 1083
164
        if (array_key_exists('groups', $data)) {
165
            $this->loadPropertyMetadataGroups($propertyMetadata, $data['groups']);
166
        }
167
168
        if (array_key_exists('xml_attribute', $data)) {
169 117
            $this->loadPropertyMetadataXmlAttribute($propertyMetadata, $data['xml_attribute']);
170
        }
171 117
172 6
        if (array_key_exists('xml_value', $data)) {
173 6
            $this->loadPropertyMetadataXmlValue($propertyMetadata, $data['xml_value']);
174 6
        }
175 4
    }
176
177
    /**
178 111
     * @param ClassMetadataInterface $classMetadata
179
     * @param string                 $xmlRoot
180 111
     */
181 3 View Code Duplication
    private function loadClassMetadataXmlRoot(ClassMetadataInterface $classMetadata, $xmlRoot)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
182
    {
183
        if (!is_string($xmlRoot)) {
184 108
            throw new \InvalidArgumentException(sprintf(
185 108
                'The mapping xml root must be a non empty string, got "%s".',
186
                is_object($xmlRoot) ? get_class($xmlRoot) : gettype($xmlRoot)
187
            ));
188
        }
189
190
        if (empty($xmlRoot)) {
191 339
            throw new \InvalidArgumentException('The mapping xml root must be a non empty string.');
192
        }
193 339
194 6
        $classMetadata->setXmlRoot($xmlRoot);
195 6
    }
196 6
197 4
    /**
198
     * @param PropertyMetadataInterface $propertyMetadata
199
     * @param string                    $alias
200 333
     */
201 330 View Code Duplication
    private function loadPropertyMetadataAlias(PropertyMetadataInterface $propertyMetadata, $alias)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
202
    {
203
        if (!is_string($alias)) {
204
            throw new \InvalidArgumentException(sprintf(
205
                'The mapping property alias must be a non empty string, got "%s".',
206
                is_object($alias) ? get_class($alias) : gettype($alias)
207 45
            ));
208
        }
209 45
210 6
        $alias = trim($alias);
211 6
212 6
        if (empty($alias)) {
213 4
            throw new \InvalidArgumentException('The mapping property alias must be a non empty string.');
214
        }
215
216 39
        $propertyMetadata->setAlias($alias);
217
    }
218 39
219 3
    /**
220
     * @param PropertyMetadataInterface $propertyMetadata
221
     * @param string                    $type
222 36
     */
223 36
    private function loadPropertyMetadataType(PropertyMetadataInterface $propertyMetadata, $type)
224
    {
225
        if (!is_string($type)) {
226
            throw new \InvalidArgumentException(sprintf(
227
                'The mapping property type must be a non empty string, got "%s".',
228
                is_object($type) ? get_class($type) : gettype($type)
229 45
            ));
230
        }
231 45
232 6
        $propertyMetadata->setType($this->typeParser->parse($type));
233 6
    }
234 6
235 4
    /**
236
     * @param PropertyMetadataInterface $propertyMetadata
237
     * @param string                    $accessor
238 39
     */
239 View Code Duplication
    private function loadPropertyMetadataAccessor(PropertyMetadataInterface $propertyMetadata, $accessor)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
240 39
    {
241 3
        if (!is_string($accessor)) {
242
            throw new \InvalidArgumentException(sprintf(
243
                'The mapping property accessor must be a non empty string, got "%s".',
244 36
                is_object($accessor) ? get_class($accessor) : gettype($accessor)
245 36
            ));
246
        }
247
248
        $accessor = trim($accessor);
249
250
        if (empty($accessor)) {
251 93
            throw new \InvalidArgumentException('The mapping property accessor must be a non empty string.');
252
        }
253 93
254 6
        $propertyMetadata->setAccessor($accessor);
255 6
    }
256 6
257 4
    /**
258
     * @param PropertyMetadataInterface $propertyMetadata
259
     * @param string                    $mutator
260 87
     */
261 View Code Duplication
    private function loadPropertyMetadataMutator(PropertyMetadataInterface $propertyMetadata, $mutator)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
262 87
    {
263 3
        if (!is_string($mutator)) {
264
            throw new \InvalidArgumentException(sprintf(
265
                'The mapping property mutator must be a non empty string, got "%s".',
266 84
                is_object($mutator) ? get_class($mutator) : gettype($mutator)
267 84
            ));
268
        }
269
270
        $mutator = trim($mutator);
271
272
        if (empty($mutator)) {
273 93
            throw new \InvalidArgumentException('The mapping property mutator must be a non empty string.');
274
        }
275 93
276 6
        $propertyMetadata->setMutator($mutator);
277 6
    }
278 6
279 4
    /**
280
     * @param PropertyMetadataInterface $propertyMetadata
281
     * @param string                    $version
282 87
     */
283 View Code Duplication
    private function loadPropertyMetadataSinceVersion(PropertyMetadataInterface $propertyMetadata, $version)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
284 87
    {
285 3
        if (!is_string($version)) {
286
            throw new \InvalidArgumentException(sprintf(
287
                'The mapping property since version must be a non empty string, got "%s".',
288 84
                is_object($version) ? get_class($version) : gettype($version)
289 84
            ));
290
        }
291
292
        $version = trim($version);
293
294
        if (empty($version)) {
295 66
            throw new \InvalidArgumentException('The mapping property since version must be a non empty string.');
296
        }
297 66
298 3
        $propertyMetadata->setSinceVersion($version);
299 3
    }
300 3
301 2
    /**
302
     * @param PropertyMetadataInterface $propertyMetadata
303
     * @param string                    $version
304 63
     */
305 View Code Duplication
    private function loadPropertyMetadataUntilVersion(PropertyMetadataInterface $propertyMetadata, $version)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
306 63
    {
307 3
        if (!is_string($version)) {
308 3
            throw new \InvalidArgumentException(sprintf(
309
                'The mapping property until version must be a non empty string, got "%s".',
310 2
                is_object($version) ? get_class($version) : gettype($version)
311
            ));
312
        }
313 60
314 60
        $version = trim($version);
315
316
        if (empty($version)) {
317
            throw new \InvalidArgumentException('The mapping property until version must be a non empty string.');
318
        }
319
320 105
        $propertyMetadata->setUntilVersion($version);
321
    }
322 105
323 3
    /**
324 3
     * @param PropertyMetadataInterface $propertyMetadata
325 3
     * @param string|int                $maxDepth
326 2
     */
327
    private function loadPropertyMetadataMaxDepth(PropertyMetadataInterface $propertyMetadata, $maxDepth)
328
    {
329 102
        if (!is_int($maxDepth) && !is_string($maxDepth) && !ctype_digit($maxDepth)) {
330 102
            throw new \InvalidArgumentException(sprintf(
331 3
                'The mapping property max depth must be a positive integer, got "%s".',
332 3
                is_object($maxDepth) ? get_class($maxDepth) : gettype($maxDepth)
333 3
            ));
334 2
        }
335
336
        $maxDepth = (int) $maxDepth;
337 99
338
        if ($maxDepth <= 0) {
339 99
            throw new \InvalidArgumentException(sprintf(
340 3
                'The mapping property max depth must be a positive integer, got "%d".',
341 1
                $maxDepth
342 2
            ));
343
        }
344
345 96
        $propertyMetadata->setMaxDepth($maxDepth);
346 64
    }
347 96
348
    /**
349
     * @param PropertyMetadataInterface $propertyMetadata
350
     * @param string[]                  $groups
351
     */
352
    private function loadPropertyMetadataGroups(PropertyMetadataInterface $propertyMetadata, $groups)
353
    {
354 1197
        if (!is_array($groups)) {
355
            throw new \InvalidArgumentException(sprintf(
356 1197
                'The mapping property groups must be an array of non empty strings, got "%s".',
357 1116
                is_object($groups) ? get_class($groups) : gettype($groups)
358
            ));
359
        }
360 81
361
        foreach ($groups as $group) {
362 81
            if (!is_string($group)) {
363 3
                throw new \InvalidArgumentException(sprintf(
364 3
                    'The mapping property groups must be an array of non empty strings, got "%s".',
365 3
                    is_object($group) ? get_class($group) : gettype($group)
366 3
                ));
367 3
            }
368 2
369
            $group = trim($group);
370
371 78
            if (empty($group)) {
372
                throw new \InvalidArgumentException(
373 78
                    'The mapping property groups must be an array of non empty strings.'
374 6
                );
375 6
            }
376 6
377 6
            $propertyMetadata->addGroup($group);
378
        }
379 4
    }
380
381
    /**
382 72
     * @param PropertyMetadataInterface $propertyMetadata
383
     * @param bool                      $xmlAttribute
384
     */
385 View Code Duplication
    private function loadPropertyMetadataXmlAttribute(PropertyMetadataInterface $propertyMetadata, $xmlAttribute)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
386
    {
387
        if (!is_bool($xmlAttribute)) {
388
            throw new \InvalidArgumentException(sprintf(
389
                'The mapping property xml attribute must be a boolean, got "%s".',
390
                is_object($xmlAttribute) ? get_class($xmlAttribute) : gettype($xmlAttribute)
391 1188
            ));
392
        }
393 1188
394 1182
        $propertyMetadata->setXmlAttribute($xmlAttribute);
395
    }
396
397 87
    /**
398
     * @param PropertyMetadataInterface $propertyMetadata
399 87
     * @param bool                      $xmlValue
400 15
     */
401 15 View Code Duplication
    private function loadPropertyMetadataXmlValue(PropertyMetadataInterface $propertyMetadata, $xmlValue)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
402 15
    {
403 10
        if (!is_bool($xmlValue)) {
404
            throw new \InvalidArgumentException(sprintf(
405
                'The mapping property xml value must be a boolean, got "%s".',
406 72
                is_object($xmlValue) ? get_class($xmlValue) : gettype($xmlValue)
407
            ));
408
        }
409
410
        $propertyMetadata->setXmlValue($xmlValue);
411
    }
412
413
    /**
414
     * @param mixed[] $data
415 1182
     *
416
     * @return string|null
417 1182
     */
418 1176
    private function getExclusionPolicy(array $data)
419
    {
420
        if (!isset($data['exclusion_policy'])) {
421 81
            return ExclusionPolicy::NONE;
422
        }
423 81
424 9
        $policy = $data['exclusion_policy'];
425 9
426 9
        if (!is_string($policy)) {
427 6
            throw new \InvalidArgumentException(sprintf(
428
                'The mapping exclusion policy must be "%s" or "%s", got "%s".',
429
                ExclusionPolicy::ALL,
430 72
                ExclusionPolicy::NONE,
431
                is_object($policy) ? get_class($policy) : gettype($policy)
432
            ));
433
        }
434
435
        $policy = strtolower(trim($policy));
436
437
        if ($policy !== ExclusionPolicy::ALL && $policy !== ExclusionPolicy::NONE) {
438
            throw new \InvalidArgumentException(sprintf(
439 1083
                'The mapping exclusion policy must be "%s" or "%s", got "%s".',
440
                ExclusionPolicy::ALL,
441 1083
                ExclusionPolicy::NONE,
442 948
                $policy
443
            ));
444
        }
445 135
446
        return $policy;
447 135
    }
448 84
449
    /**
450 84
     * @param mixed[] $data
451 3
     * @param bool    $default
452 1
     *
453 2
     * @return bool|null
454
     */
455 View Code Duplication
    private function getReadable(array $data, $default = true)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
456 81
    {
457 72
        if (!array_key_exists('readable', $data)) {
458
            return $default;
459
        }
460 9
461 57
        $readable = $data['readable'];
462 3
463 1
        if (!is_bool($readable)) {
464 2
            throw new \InvalidArgumentException(sprintf(
465
                'The mapping readable must be a boolean, got "%s".',
466
                is_object($readable) ? get_class($readable) : gettype($readable)
467 57
            ));
468 6
        }
469 2
470 4
        return $readable;
471
    }
472
473 51
    /**
474 51
     * @param mixed[] $data
475 3
     * @param bool    $default
476 3
     *
477 3
     * @return bool|null
478 2
     */
479 View Code Duplication
    private function getWritable(array $data, $default = true)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
480
    {
481 48
        if (!array_key_exists('writable', $data)) {
482
            return $default;
483 48
        }
484 6
485 2
        $writable = $data['writable'];
486 4
487
        if (!is_bool($writable)) {
488
            throw new \InvalidArgumentException(sprintf(
489 42
                'The mapping readable must be a boolean, got "%s".',
490 6
                is_object($writable) ? get_class($writable) : gettype($writable)
491 18
            ));
492
        }
493 4
494
        return $writable;
495 24
    }
496
497 36
    /**
498
     * @param mixed[]                     $data
499
     * @param PropertyMetadataInterface[] $properties
500
     *
501
     * @return string|string[]|null
502
     */
503
    private function getOrder(array $data, array $properties)
504
    {
505
        if (!isset($data['order'])) {
506 108
            return;
507
        }
508 108
509 72
        $order = $data['order'];
510 36
511 24
        if (is_string($order)) {
512 48
            $order = trim($order);
513
514 84
            if (empty($order)) {
515 36
                throw new \InvalidArgumentException(
516 24
                    'The mapping order must be an non empty strings or an array of non empty strings.'
517
                );
518 108
            }
519
520
            if (strcasecmp($order, 'ASC') === 0 || strcasecmp($order, 'DESC') === 0) {
521
                return strtoupper($order);
522
            }
523
524 42
            $order = explode(',', $order);
525
        } elseif (!is_array($order)) {
526 42
            throw new \InvalidArgumentException(
527 6
                'The mapping order must be an non empty strings or an array of non empty strings.'
528 6
            );
529 6
        }
530 4
531
        if (empty($order)) {
532 36
            throw new \InvalidArgumentException(
533
                'The mapping order must be an non empty strings or an array of non empty strings.'
534
            );
535
        }
536
537 42
        foreach ($order as &$property) {
538
            if (!is_string($property)) {
539 42
                throw new \InvalidArgumentException(sprintf(
540 6
                    'The mapping order must be an non empty strings or an array of non empty strings, got "%s".',
541 6
                    is_object($property) ? get_class($property) : gettype($property)
542 6
                ));
543 4
            }
544
545 36
            $property = trim($property);
546
547
            if (empty($property)) {
548
                throw new \InvalidArgumentException(
549
                    'The mapping order must be an non empty strings or an array of non empty strings.'
550
                );
551
            }
552
553 1083
            if (!isset($properties[$property])) {
554
                throw new \InvalidArgumentException(sprintf(
555 1083
                    'The property "%s" defined in the mapping order does not exist.',
556 1083
                    $property
557
                ));
558 1083
            }
559
        }
560
561
        return $order;
562
    }
563
564
    /**
565
     * @param PropertyMetadataInterface[] $properties
566
     * @param string|string[]             $order
567
     *
568
     * @return PropertyMetadataInterface[]
569
     */
570
    private function sortProperties(array $properties, $order)
571
    {
572
        if (is_string($order)) {
573
            if ($order === 'ASC') {
574
                ksort($properties);
575
            } else {
576
                krsort($properties);
577
            }
578
        } elseif (is_array($order)) {
579
            $properties = array_merge(array_flip($order), $properties);
580
        }
581
582
        return $properties;
583
    }
584
585
    /**
586
     * @param bool $exclude
587
     */
588 View Code Duplication
    private function validatePropertyMetadataExclude($exclude)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
589
    {
590
        if (!is_bool($exclude)) {
591
            throw new \InvalidArgumentException(sprintf(
592
                'The mapping property exclude must be a boolean, got "%s".',
593
                is_object($exclude) ? get_class($exclude) : gettype($exclude)
594
            ));
595
        }
596
    }
597
598
    /**
599
     * @param bool $expose
600
     */
601 View Code Duplication
    private function validatePropertyMetadataExpose($expose)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
602
    {
603
        if (!is_bool($expose)) {
604
            throw new \InvalidArgumentException(sprintf(
605
                'The mapping property expose must be a boolean, got "%s".',
606
                is_object($expose) ? get_class($expose) : gettype($expose)
607
            ));
608
        }
609
    }
610
611
    /**
612
     * @param mixed[] $property
613
     * @param string  $policy
614
     *
615
     * @return bool
616
     */
617
    private function isPropertyMetadataExposed($property, $policy)
618
    {
619
        $expose = isset($property['expose']) && $property['expose'];
620
        $exclude = isset($property['exclude']) && $property['exclude'];
621
622
        return ($policy === ExclusionPolicy::ALL && $expose) || ($policy === ExclusionPolicy::NONE && !$exclude);
623
    }
624
}
625