Completed
Push — master ( 454f7d...fc2d7f )
by Eric
02:39
created

AbstractClassMetadataLoader   B

Complexity

Total Complexity 46

Size/Duplication

Total Lines 244
Duplicated Lines 20.9 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
wmc 46
lcom 1
cbo 5
dl 51
loc 244
rs 8.3999
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 2
A loadClassMetadata() 0 16 3
loadData() 0 1 ?
B doLoadClassMetadata() 0 15 5
C loadPropertyMetadata() 0 30 8
A loadPropertyMetadataAlias() 17 17 4
A loadPropertyMetadataType() 0 11 3
A loadPropertyMetadataSinceVersion() 17 17 4
A loadPropertyMetadataUntilVersion() 17 17 4
B loadPropertyMetadataMaxDepth() 0 20 6
C loadPropertyMetadataGroups() 0 28 7

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like AbstractClassMetadataLoader often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AbstractClassMetadataLoader, and based on these observations, apply Extract Interface, too.

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\Mapping\ClassMetadataInterface;
15
use Ivory\Serializer\Mapping\PropertyMetadata;
16
use Ivory\Serializer\Mapping\PropertyMetadataInterface;
17
use Ivory\Serializer\Type\Parser\TypeParser;
18
use Ivory\Serializer\Type\Parser\TypeParserInterface;
19
20
/**
21
 * @author GeLo <[email protected]>
22
 */
23
abstract class AbstractClassMetadataLoader implements ClassMetadataLoaderInterface
24
{
25
    /**
26
     * @var TypeParserInterface
27
     */
28
    private $typeParser;
29
30
    /**
31
     * @var mixed[][]
32
     */
33
    private $data = [];
34
35
    /**
36
     * @param TypeParserInterface|null $typeParser
37
     */
38
    public function __construct(TypeParserInterface $typeParser = null)
39
    {
40
        $this->typeParser = $typeParser ?: new TypeParser();
41
    }
42
43
    /**
44
     * {@inheritdoc}
45
     */
46
    public function loadClassMetadata(ClassMetadataInterface $classMetadata)
47
    {
48
        $class = $classMetadata->getName();
49
50
        if (!array_key_exists($class, $this->data)) {
51
            $this->data[$class] = $this->loadData($class);
52
        }
53
54
        if (!is_array($data = $this->data[$class])) {
55
            return false;
56
        }
57
58
        $this->doLoadClassMetadata($classMetadata, $data);
59
60
        return true;
61
    }
62
63
    /**
64
     * @param string $class
65
     *
66
     * @return mixed[]|null
67
     */
68
    abstract protected function loadData($class);
69
70
    /**
71
     * @param ClassMetadataInterface $classMetadata
72
     * @param mixed[]                $data
73
     *
74
     * @return bool
75
     */
76
    private function doLoadClassMetadata(ClassMetadataInterface $classMetadata, array $data)
77
    {
78
        if (!isset($data['properties']) || empty($data['properties'])) {
79
            throw new \InvalidArgumentException(sprintf(
80
                'No mapping properties found for "%s".',
81
                $classMetadata->getName()
82
            ));
83
        }
84
85
        foreach ($data['properties'] as $property => $value) {
86
            $propertyMetadata = $classMetadata->getProperty($property) ?: new PropertyMetadata($property);
87
            $this->loadPropertyMetadata($propertyMetadata, $value);
88
            $classMetadata->addProperty($propertyMetadata);
89
        }
90
    }
91
92
    /**
93
     * @param PropertyMetadataInterface $propertyMetadata
94
     * @param mixed                     $data
95
     */
96
    private function loadPropertyMetadata(PropertyMetadataInterface $propertyMetadata, $data)
97
    {
98
        if (!is_array($data)) {
99
            return;
100
        }
101
102
        if (array_key_exists('alias', $data)) {
103
            $this->loadPropertyMetadataAlias($propertyMetadata, $data['alias']);
104
        }
105
106
        if (array_key_exists('type', $data)) {
107
            $this->loadPropertyMetadataType($propertyMetadata, $data['type']);
108
        }
109
110
        if (array_key_exists('since', $data)) {
111
            $this->loadPropertyMetadataSinceVersion($propertyMetadata, $data['since']);
112
        }
113
114
        if (array_key_exists('until', $data)) {
115
            $this->loadPropertyMetadataUntilVersion($propertyMetadata, $data['until']);
116
        }
117
118
        if (array_key_exists('max_depth', $data)) {
119
            $this->loadPropertyMetadataMaxDepth($propertyMetadata, $data['max_depth']);
120
        }
121
122
        if (array_key_exists('groups', $data)) {
123
            $this->loadPropertyMetadataGroups($propertyMetadata, $data['groups']);
124
        }
125
    }
126
127
    /**
128
     * @param PropertyMetadataInterface $propertyMetadata
129
     * @param string                    $alias
130
     */
131 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...
132
    {
133
        if (!is_string($alias)) {
134
            throw new \InvalidArgumentException(sprintf(
135
                'The mapping property alias must be a non empty string, got "%s".',
136
                is_object($alias) ? get_class($alias) : gettype($alias)
137
            ));
138
        }
139
140
        $alias = trim($alias);
141
142
        if (empty($alias)) {
143
            throw new \InvalidArgumentException('The mapping property alias must be a non empty string.');
144
        }
145
146
        $propertyMetadata->setAlias($alias);
147
    }
148
149
    /**
150
     * @param PropertyMetadataInterface $propertyMetadata
151
     * @param string                    $type
152
     */
153
    private function loadPropertyMetadataType(PropertyMetadataInterface $propertyMetadata, $type)
154
    {
155
        if (!is_string($type)) {
156
            throw new \InvalidArgumentException(sprintf(
157
                'The mapping property type must be a non empty string, got "%s".',
158
                is_object($type) ? get_class($type) : gettype($type)
159
            ));
160
        }
161
162
        $propertyMetadata->setType($this->typeParser->parse($type));
163
    }
164
165
    /**
166
     * @param PropertyMetadataInterface $propertyMetadata
167
     * @param string                    $version
168
     */
169 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...
170
    {
171
        if (!is_string($version)) {
172
            throw new \InvalidArgumentException(sprintf(
173
                'The mapping property since version must be a non empty string, got "%s".',
174
                is_object($version) ? get_class($version) : gettype($version)
175
            ));
176
        }
177
178
        $version = trim($version);
179
180
        if (empty($version)) {
181
            throw new \InvalidArgumentException('The mapping property since version must be a non empty string.');
182
        }
183
184
        $propertyMetadata->setSinceVersion($version);
185
    }
186
187
    /**
188
     * @param PropertyMetadataInterface $propertyMetadata
189
     * @param string                    $version
190
     */
191 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...
192
    {
193
        if (!is_string($version)) {
194
            throw new \InvalidArgumentException(sprintf(
195
                'The mapping property until version must be a non empty string, got "%s".',
196
                is_object($version) ? get_class($version) : gettype($version)
197
            ));
198
        }
199
200
        $version = trim($version);
201
202
        if (empty($version)) {
203
            throw new \InvalidArgumentException('The mapping property until version must be a non empty string.');
204
        }
205
206
        $propertyMetadata->setUntilVersion($version);
207
    }
208
209
    /**
210
     * @param PropertyMetadataInterface $propertyMetadata
211
     * @param string|int                $maxDepth
212
     */
213
    private function loadPropertyMetadataMaxDepth(PropertyMetadataInterface $propertyMetadata, $maxDepth)
214
    {
215
        if (!is_int($maxDepth) && !is_string($maxDepth) && !ctype_digit($maxDepth)) {
216
            throw new \InvalidArgumentException(sprintf(
217
                'The mapping property max depth must be a positive integer, got "%s".',
218
                is_object($maxDepth) ? get_class($maxDepth) : gettype($maxDepth)
219
            ));
220
        }
221
222
        $maxDepth = (int) $maxDepth;
223
224
        if ($maxDepth <= 0) {
225
            throw new \InvalidArgumentException(sprintf(
226
                'The mapping property max depth must be a positive integer, got "%d".',
227
                $maxDepth
228
            ));
229
        }
230
231
        $propertyMetadata->setMaxDepth($maxDepth);
232
    }
233
234
    /**
235
     * @param PropertyMetadataInterface $propertyMetadata
236
     * @param string[]                  $groups
237
     */
238
    private function loadPropertyMetadataGroups(PropertyMetadataInterface $propertyMetadata, $groups)
239
    {
240
        if (!is_array($groups)) {
241
            throw new \InvalidArgumentException(sprintf(
242
                'The mapping property groups must be an array of non empty strings, got "%s".',
243
                is_object($groups) ? get_class($groups) : gettype($groups)
244
            ));
245
        }
246
247
        foreach ($groups as $group) {
248
            if (!is_string($group)) {
249
                throw new \InvalidArgumentException(sprintf(
250
                    'The mapping property groups must be an array of non empty strings, got "%s".',
251
                    is_object($group) ? get_class($group) : gettype($group)
252
                ));
253
            }
254
255
            $group = trim($group);
256
257
            if (empty($group)) {
258
                throw new \InvalidArgumentException(
259
                    'The mapping property groups must be an array of non empty strings.'
260
                );
261
            }
262
263
            $propertyMetadata->addGroup($group);
264
        }
265
    }
266
}
267