Completed
Pull Request — master (#11)
by Eric
21:21 queued 18:00
created

loadClassMetadataXmlRoot()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 15
Code Lines 8

Duplication

Lines 15
Ratio 100 %

Code Coverage

Tests 10
CRAP Score 4

Importance

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