Completed
Pull Request — master (#11)
by Eric
04:56
created

loadPropertyMetadataXmlAttribute()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 11
Ratio 100 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 11
loc 11
ccs 8
cts 8
cp 1
rs 9.4285
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 2196
    public function __construct(TypeParserInterface $typeParser = null)
40
    {
41 2196
        $this->typeParser = $typeParser ?: new TypeParser();
42 2196
    }
43
44
    /**
45
     * {@inheritdoc}
46
     */
47 1788
    public function loadClassMetadata(ClassMetadataInterface $classMetadata)
48
    {
49 1788
        $class = $classMetadata->getName();
50
51 1788
        if (!array_key_exists($class, $this->data)) {
52 1788
            $this->data[$class] = $this->loadData($class);
53 878
        }
54
55 1756
        if (!is_array($data = $this->data[$class])) {
56 24
            return false;
57
        }
58
59 1732
        $this->doLoadClassMetadata($classMetadata, $data);
60
61 1504
        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 1732
    private function doLoadClassMetadata(ClassMetadataInterface $classMetadata, array $data)
76
    {
77 1732
        if (!isset($data['properties']) || empty($data['properties'])) {
78 12
            throw new \InvalidArgumentException(sprintf(
79 12
                'No mapping properties found for "%s".',
80 12
                $classMetadata->getName()
81 6
            ));
82
        }
83
84 1720
        $policy = $this->getExclusionPolicy($data);
85 1708
        $readableClass = $this->getReadable($data);
86 1700
        $writableClass = $this->getWritable($data);
87 1692
        $properties = $classMetadata->getProperties();
88
89 1692
        foreach ($data['properties'] as $property => $value) {
90 1692
            $propertyMetadata = $classMetadata->getProperty($property) ?: new PropertyMetadata($property);
91 1692
            $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 1568
            if ($this->isPropertyMetadataExposed($value, $policy)) {
94 1568
                $properties[$property] = $propertyMetadata;
95 784
            }
96 784
        }
97
98 1552
        if (($order = $this->getOrder($data, $properties)) !== null) {
99 144
            $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 1516
        $classMetadata->setProperties($properties);
103
104 1516
        if (array_key_exists('xml_root', $data)) {
105 108
            $this->loadClassMetadataXmlRoot($classMetadata, $data['xml_root']);
106 48
        }
107 1504
    }
108
109
    /**
110
     * @param PropertyMetadataInterface $propertyMetadata
111
     * @param mixed                     $data
112
     * @param bool                      $classReadable
113
     * @param bool                      $classWritable
114
     */
115 1692
    private function loadPropertyMetadata(
116
        PropertyMetadataInterface $propertyMetadata,
117
        $data,
118
        $classReadable,
119
        $classWritable
120
    ) {
121 1692
        if (!is_array($data)) {
122 68
            $data = [];
123 34
        }
124
125 1692
        $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 1680
        $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 1676
        if (array_key_exists('exclude', $data)) {
129 56
            $this->validatePropertyMetadataExclude($data['exclude']);
130 24
        }
131
132 1668
        if (array_key_exists('expose', $data)) {
133 56
            $this->validatePropertyMetadataExpose($data['expose']);
134 24
        }
135
136 1660
        if (array_key_exists('alias', $data)) {
137 156
            $this->loadPropertyMetadataAlias($propertyMetadata, $data['alias']);
138 72
        }
139
140 1648
        if (array_key_exists('type', $data)) {
141 452
            $this->loadPropertyMetadataType($propertyMetadata, $data['type']);
142 220
        }
143
144 1636
        if (array_key_exists('accessor', $data)) {
145 60
            $this->loadPropertyMetadataAccessor($propertyMetadata, $data['accessor']);
146 24
        }
147
148 1624
        if (array_key_exists('mutator', $data)) {
149 60
            $this->loadPropertyMetadataMutator($propertyMetadata, $data['mutator']);
150 24
        }
151
152 1612
        if (array_key_exists('since', $data)) {
153 124
            $this->loadPropertyMetadataSinceVersion($propertyMetadata, $data['since']);
154 56
        }
155
156 1600
        if (array_key_exists('until', $data)) {
157 124
            $this->loadPropertyMetadataUntilVersion($propertyMetadata, $data['until']);
158 56
        }
159
160 1588
        if (array_key_exists('max_depth', $data)) {
161 88
            $this->loadPropertyMetadataMaxDepth($propertyMetadata, $data['max_depth']);
162 40
        }
163
164 1580
        if (array_key_exists('groups', $data)) {
165 140
            $this->loadPropertyMetadataGroups($propertyMetadata, $data['groups']);
166 64
        }
167
168 1568
        if (array_key_exists('xml_attribute', $data)) {
169 124
            $this->loadPropertyMetadataXmlAttribute($propertyMetadata, $data['xml_attribute']);
170 58
        }
171
172 1568
        if (array_key_exists('xml_value', $data)) {
173 56
            $this->loadPropertyMetadataXmlValue($propertyMetadata, $data['xml_value']);
174 24
        }
175 1568
    }
176
177
    /**
178
     * @param ClassMetadataInterface $classMetadata
179
     * @param string                 $xmlRoot
180
     */
181 108 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 108
        if (!is_string($xmlRoot)) {
184 8
            throw new \InvalidArgumentException(sprintf(
185 8
                'The mapping xml root must be a non empty string, got "%s".',
186 8
                is_object($xmlRoot) ? get_class($xmlRoot) : gettype($xmlRoot)
187 4
            ));
188
        }
189
190 100
        if (empty($xmlRoot)) {
191 4
            throw new \InvalidArgumentException('The mapping xml root must be a non empty string.');
192
        }
193
194 96
        $classMetadata->setXmlRoot($xmlRoot);
195 96
    }
196
197
    /**
198
     * @param PropertyMetadataInterface $propertyMetadata
199
     * @param string                    $alias
200
     */
201 156 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 156
        if (!is_string($alias)) {
204 8
            throw new \InvalidArgumentException(sprintf(
205 8
                'The mapping property alias must be a non empty string, got "%s".',
206 8
                is_object($alias) ? get_class($alias) : gettype($alias)
207 4
            ));
208
        }
209
210 148
        $alias = trim($alias);
211
212 148
        if (empty($alias)) {
213 4
            throw new \InvalidArgumentException('The mapping property alias must be a non empty string.');
214
        }
215
216 144
        $propertyMetadata->setAlias($alias);
217 144
    }
218
219
    /**
220
     * @param PropertyMetadataInterface $propertyMetadata
221
     * @param string                    $type
222
     */
223 452
    private function loadPropertyMetadataType(PropertyMetadataInterface $propertyMetadata, $type)
224
    {
225 452
        if (!is_string($type)) {
226 8
            throw new \InvalidArgumentException(sprintf(
227 8
                'The mapping property type must be a non empty string, got "%s".',
228 8
                is_object($type) ? get_class($type) : gettype($type)
229 4
            ));
230
        }
231
232 444
        $propertyMetadata->setType($this->typeParser->parse($type));
233 440
    }
234
235
    /**
236
     * @param PropertyMetadataInterface $propertyMetadata
237
     * @param string                    $accessor
238
     */
239 60 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
    {
241 60
        if (!is_string($accessor)) {
242 8
            throw new \InvalidArgumentException(sprintf(
243 8
                'The mapping property accessor must be a non empty string, got "%s".',
244 8
                is_object($accessor) ? get_class($accessor) : gettype($accessor)
245 4
            ));
246
        }
247
248 52
        $accessor = trim($accessor);
249
250 52
        if (empty($accessor)) {
251 4
            throw new \InvalidArgumentException('The mapping property accessor must be a non empty string.');
252
        }
253
254 48
        $propertyMetadata->setAccessor($accessor);
255 48
    }
256
257
    /**
258
     * @param PropertyMetadataInterface $propertyMetadata
259
     * @param string                    $mutator
260
     */
261 60 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
    {
263 60
        if (!is_string($mutator)) {
264 8
            throw new \InvalidArgumentException(sprintf(
265 8
                'The mapping property mutator must be a non empty string, got "%s".',
266 8
                is_object($mutator) ? get_class($mutator) : gettype($mutator)
267 4
            ));
268
        }
269
270 52
        $mutator = trim($mutator);
271
272 52
        if (empty($mutator)) {
273 4
            throw new \InvalidArgumentException('The mapping property mutator must be a non empty string.');
274
        }
275
276 48
        $propertyMetadata->setMutator($mutator);
277 48
    }
278
279
    /**
280
     * @param PropertyMetadataInterface $propertyMetadata
281
     * @param string                    $version
282
     */
283 124 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
    {
285 124
        if (!is_string($version)) {
286 8
            throw new \InvalidArgumentException(sprintf(
287 8
                'The mapping property since version must be a non empty string, got "%s".',
288 8
                is_object($version) ? get_class($version) : gettype($version)
289 4
            ));
290
        }
291
292 116
        $version = trim($version);
293
294 116
        if (empty($version)) {
295 4
            throw new \InvalidArgumentException('The mapping property since version must be a non empty string.');
296
        }
297
298 112
        $propertyMetadata->setSinceVersion($version);
299 112
    }
300
301
    /**
302
     * @param PropertyMetadataInterface $propertyMetadata
303
     * @param string                    $version
304
     */
305 124 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
    {
307 124
        if (!is_string($version)) {
308 8
            throw new \InvalidArgumentException(sprintf(
309 8
                'The mapping property until version must be a non empty string, got "%s".',
310 8
                is_object($version) ? get_class($version) : gettype($version)
311 4
            ));
312
        }
313
314 116
        $version = trim($version);
315
316 116
        if (empty($version)) {
317 4
            throw new \InvalidArgumentException('The mapping property until version must be a non empty string.');
318
        }
319
320 112
        $propertyMetadata->setUntilVersion($version);
321 112
    }
322
323
    /**
324
     * @param PropertyMetadataInterface $propertyMetadata
325
     * @param string|int                $maxDepth
326
     */
327 88
    private function loadPropertyMetadataMaxDepth(PropertyMetadataInterface $propertyMetadata, $maxDepth)
328
    {
329 88
        if (!is_int($maxDepth) && !is_string($maxDepth) && !ctype_digit($maxDepth)) {
330 4
            throw new \InvalidArgumentException(sprintf(
331 4
                'The mapping property max depth must be a positive integer, got "%s".',
332 4
                is_object($maxDepth) ? get_class($maxDepth) : gettype($maxDepth)
333 2
            ));
334
        }
335
336 84
        $maxDepth = (int) $maxDepth;
337
338 84
        if ($maxDepth <= 0) {
339 4
            throw new \InvalidArgumentException(sprintf(
340 4
                'The mapping property max depth must be a positive integer, got "%d".',
341
                $maxDepth
342 2
            ));
343
        }
344
345 80
        $propertyMetadata->setMaxDepth($maxDepth);
346 80
    }
347
348
    /**
349
     * @param PropertyMetadataInterface $propertyMetadata
350
     * @param string[]                  $groups
351
     */
352 140
    private function loadPropertyMetadataGroups(PropertyMetadataInterface $propertyMetadata, $groups)
353
    {
354 140
        if (!is_array($groups)) {
355 4
            throw new \InvalidArgumentException(sprintf(
356 4
                'The mapping property groups must be an array of non empty strings, got "%s".',
357 4
                is_object($groups) ? get_class($groups) : gettype($groups)
358 2
            ));
359
        }
360
361 136
        foreach ($groups as $group) {
362 136
            if (!is_string($group)) {
363 4
                throw new \InvalidArgumentException(sprintf(
364 4
                    'The mapping property groups must be an array of non empty strings, got "%s".',
365 4
                    is_object($group) ? get_class($group) : gettype($group)
366 2
                ));
367
            }
368
369 132
            $group = trim($group);
370
371 132
            if (empty($group)) {
372 4
                throw new \InvalidArgumentException(
373 2
                    'The mapping property groups must be an array of non empty strings.'
374 2
                );
375
            }
376
377 128
            $propertyMetadata->addGroup($group);
378 64
        }
379 128
    }
380
381
    /**
382
     * @param PropertyMetadataInterface $propertyMetadata
383
     * @param bool                      $xmlAttribute
384
     */
385 124 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 124
        if (!is_bool($xmlAttribute)) {
388 8
            throw new \InvalidArgumentException(sprintf(
389 8
                'The mapping property xml attribute must be a boolean, got "%s".',
390 8
                is_object($xmlAttribute) ? get_class($xmlAttribute) : gettype($xmlAttribute)
391 4
            ));
392
        }
393
394 116
        $propertyMetadata->setXmlAttribute($xmlAttribute);
395 116
    }
396
397
    /**
398
     * @param PropertyMetadataInterface $propertyMetadata
399
     * @param bool                      $xmlValue
400
     */
401 56 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
    {
403 56
        if (!is_bool($xmlValue)) {
404 8
            throw new \InvalidArgumentException(sprintf(
405 8
                'The mapping property xml value must be a boolean, got "%s".',
406 8
                is_object($xmlValue) ? get_class($xmlValue) : gettype($xmlValue)
407 4
            ));
408
        }
409
410 48
        $propertyMetadata->setXmlValue($xmlValue);
411 48
    }
412
413
    /**
414
     * @param mixed[] $data
415
     *
416
     * @return string|null
417
     */
418 1720
    private function getExclusionPolicy(array $data)
419
    {
420 1720
        if (!isset($data['exclusion_policy'])) {
421 1612
            return ExclusionPolicy::NONE;
422
        }
423
424 108
        $policy = $data['exclusion_policy'];
425
426 108
        if (!is_string($policy)) {
427 4
            throw new \InvalidArgumentException(sprintf(
428 4
                'The mapping exclusion policy must be "%s" or "%s", got "%s".',
429 4
                ExclusionPolicy::ALL,
430 4
                ExclusionPolicy::NONE,
431 4
                is_object($policy) ? get_class($policy) : gettype($policy)
432 2
            ));
433
        }
434
435 104
        $policy = strtolower(trim($policy));
436
437 104
        if ($policy !== ExclusionPolicy::ALL && $policy !== ExclusionPolicy::NONE) {
438 8
            throw new \InvalidArgumentException(sprintf(
439 8
                'The mapping exclusion policy must be "%s" or "%s", got "%s".',
440 8
                ExclusionPolicy::ALL,
441 8
                ExclusionPolicy::NONE,
442
                $policy
443 4
            ));
444
        }
445
446 96
        return $policy;
447
    }
448
449
    /**
450
     * @param mixed[] $data
451
     * @param bool    $default
452
     *
453
     * @return bool|null
454
     */
455 1708 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
    {
457 1708
        if (!array_key_exists('readable', $data)) {
458 1700
            return $default;
459
        }
460
461 116
        $readable = $data['readable'];
462
463 116
        if (!is_bool($readable)) {
464 20
            throw new \InvalidArgumentException(sprintf(
465 20
                'The mapping readable must be a boolean, got "%s".',
466 20
                is_object($readable) ? get_class($readable) : gettype($readable)
467 10
            ));
468
        }
469
470 96
        return $readable;
471
    }
472
473
    /**
474
     * @param mixed[] $data
475
     * @param bool    $default
476
     *
477
     * @return bool|null
478
     */
479 1700 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 1700
        if (!array_key_exists('writable', $data)) {
482 1692
            return $default;
483
        }
484
485 108
        $writable = $data['writable'];
486
487 108
        if (!is_bool($writable)) {
488 12
            throw new \InvalidArgumentException(sprintf(
489 12
                'The mapping readable must be a boolean, got "%s".',
490 12
                is_object($writable) ? get_class($writable) : gettype($writable)
491 6
            ));
492
        }
493
494 96
        return $writable;
495
    }
496
497
    /**
498
     * @param mixed[]                     $data
499
     * @param PropertyMetadataInterface[] $properties
500
     *
501
     * @return string|string[]|null
502
     */
503 1552
    private function getOrder(array $data, array $properties)
504
    {
505 1552
        if (!isset($data['order'])) {
506 1372
            return;
507
        }
508
509 180
        $order = $data['order'];
510
511 180
        if (is_string($order)) {
512 112
            $order = trim($order);
513
514 112
            if (empty($order)) {
515 4
                throw new \InvalidArgumentException(
516 2
                    'The mapping order must be an non empty strings or an array of non empty strings.'
517 2
                );
518
            }
519
520 108
            if (strcasecmp($order, 'ASC') === 0 || strcasecmp($order, 'DESC') === 0) {
521 96
                return strtoupper($order);
522
            }
523
524 12
            $order = explode(',', $order);
525 74
        } elseif (!is_array($order)) {
526 4
            throw new \InvalidArgumentException(
527 2
                'The mapping order must be an non empty strings or an array of non empty strings.'
528 2
            );
529
        }
530
531 76
        if (empty($order)) {
532 8
            throw new \InvalidArgumentException(
533 4
                'The mapping order must be an non empty strings or an array of non empty strings.'
534 4
            );
535
        }
536
537 68
        foreach ($order as &$property) {
538 68
            if (!is_string($property)) {
539 4
                throw new \InvalidArgumentException(sprintf(
540 4
                    'The mapping order must be an non empty strings or an array of non empty strings, got "%s".',
541 4
                    is_object($property) ? get_class($property) : gettype($property)
542 2
                ));
543
            }
544
545 64
            $property = trim($property);
546
547 64
            if (empty($property)) {
548 8
                throw new \InvalidArgumentException(
549 4
                    'The mapping order must be an non empty strings or an array of non empty strings.'
550 4
                );
551
            }
552
553 56
            if (!isset($properties[$property])) {
554 8
                throw new \InvalidArgumentException(sprintf(
555 32
                    'The property "%s" defined in the mapping order does not exist.',
556
                    $property
557 4
                ));
558
            }
559 24
        }
560
561 48
        return $order;
562
    }
563
564
    /**
565
     * @param PropertyMetadataInterface[] $properties
566
     * @param string|string[]             $order
567
     *
568
     * @return PropertyMetadataInterface[]
569
     */
570 144
    private function sortProperties(array $properties, $order)
571
    {
572 144
        if (is_string($order)) {
573 96
            if ($order === 'ASC') {
574 48
                ksort($properties);
575 24
            } else {
576 72
                krsort($properties);
577
            }
578 96
        } elseif (is_array($order)) {
579 48
            $properties = array_merge(array_flip($order), $properties);
580 24
        }
581
582 144
        return $properties;
583
    }
584
585
    /**
586
     * @param bool $exclude
587
     */
588 56 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 56
        if (!is_bool($exclude)) {
591 8
            throw new \InvalidArgumentException(sprintf(
592 8
                'The mapping property exclude must be a boolean, got "%s".',
593 8
                is_object($exclude) ? get_class($exclude) : gettype($exclude)
594 4
            ));
595
        }
596 48
    }
597
598
    /**
599
     * @param bool $expose
600
     */
601 56 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 56
        if (!is_bool($expose)) {
604 8
            throw new \InvalidArgumentException(sprintf(
605 8
                'The mapping property expose must be a boolean, got "%s".',
606 8
                is_object($expose) ? get_class($expose) : gettype($expose)
607 4
            ));
608
        }
609 48
    }
610
611
    /**
612
     * @param mixed[] $property
613
     * @param string  $policy
614
     *
615
     * @return bool
616
     */
617 1568
    private function isPropertyMetadataExposed($property, $policy)
618
    {
619 1568
        $expose = isset($property['expose']) && $property['expose'];
620 1568
        $exclude = isset($property['exclude']) && $property['exclude'];
621
622 1568
        return ($policy === ExclusionPolicy::ALL && $expose) || ($policy === ExclusionPolicy::NONE && !$exclude);
623
    }
624
}
625