Transformer   B
last analyzed

Complexity

Total Complexity 49

Size/Duplication

Total Lines 372
Duplicated Lines 4.84 %

Coupling/Cohesion

Components 4
Dependencies 4

Importance

Changes 5
Bugs 0 Features 1
Metric Value
wmc 49
c 5
b 0
f 1
lcom 4
cbo 4
dl 18
loc 372
rs 8.5455

27 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
serialize() 0 1 ?
A unserialize() 0 4 1
A addMeta() 0 4 1
A setMeta() 0 6 1
A addHrefToLinks() 0 10 3
A noMappingGuard() 0 8 3
A recursiveSetKeysToUnderScore() 0 13 3
A buildLinks() 0 14 1
A getSelfUrl() 0 4 1
A setSelfUrl() 0 6 1
A getFirstUrl() 0 4 1
A setFirstUrl() 0 4 1
A getLastUrl() 0 4 1
A setLastUrl() 0 4 1
A getPrevUrl() 0 4 1
A setPrevUrl() 0 4 1
A getNextUrl() 0 4 1
A setNextUrl() 0 4 1
B getResponseAdditionalLinks() 0 22 6
A formatScalarValues() 8 8 3
A arrayToScalarValue() 0 8 2
A loopScalarValues() 0 8 4
A flattenObjectsWithSingleKeyScalars() 10 10 4
A getMappings() 0 4 1
A getMappingByAlias() 0 10 3
A getMappingByClassName() 0 6 2

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 Transformer 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 Transformer, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace NilPortugues\Api\Transformer;
4
5
use NilPortugues\Api\Mapping\Mapper;
6
use NilPortugues\Api\Mapping\Mapping;
7
use NilPortugues\Api\Transformer\Helpers\RecursiveFormatterHelper;
8
use NilPortugues\Serializer\Serializer;
9
use NilPortugues\Serializer\Strategy\StrategyInterface;
10
11
abstract class Transformer implements StrategyInterface
12
{
13
    const SELF_LINK = 'self';
14
    const FIRST_LINK = 'first';
15
    const LAST_LINK = 'last';
16
    const PREV_LINK = 'prev';
17
    const NEXT_LINK = 'next';
18
    const LINKS_HREF = 'href';
19
    const LINKS_KEY = 'links';
20
21
    /**
22
     * @var Mapping[]
23
     */
24
    protected $mappings = [];
25
    /**
26
     * @var string
27
     */
28
    protected $firstUrl = '';
29
    /**
30
     * @var string
31
     */
32
    protected $lastUrl = '';
33
    /**
34
     * @var string
35
     */
36
    protected $prevUrl = '';
37
    /**
38
     * @var string
39
     */
40
    protected $nextUrl = '';
41
42
    /**
43
     * @var string
44
     */
45
    protected $selfUrl = '';
46
47
    /**
48
     * @var array
49
     */
50
    protected $meta = [];
51
52
    /**
53
     * @param Mapper $mapper
54
     */
55
    public function __construct(Mapper $mapper)
56
    {
57
        $this->mappings = $mapper->getClassMap();
58
    }
59
60
    /**
61
     * Represents the provided $value as a serialized value in string format.
62
     *
63
     * @param mixed $value
64
     *
65
     * @return string
66
     */
67
    abstract public function serialize($value);
68
69
    /**
70
     * Unserialization will fail. This is a transformer.
71
     *
72
     * @param string $value
73
     *
74
     * @throws TransformerException
75
     *
76
     * @return array
77
     */
78
    public function unserialize($value)
79
    {
80
        throw new TransformerException(\sprintf('%s does not perform unserializations.', __CLASS__));
81
    }
82
83
    /**
84
     * @param string       $key
85
     * @param array|string $value
86
     */
87
    public function addMeta($key, $value)
88
    {
89
        $this->meta[$key] = $value;
90
    }
91
92
    /**
93
     * @param array $meta
94
     *
95
     * @return $this
96
     */
97
    public function setMeta(array $meta)
98
    {
99
        $this->meta = $meta;
100
101
        return $this;
102
    }
103
104
    /**
105
     * @param array $links
106
     *
107
     * @return array
108
     */
109
    protected function addHrefToLinks(array $links)
110
    {
111
        if (!empty($links)) {
112
            foreach ($links as &$link) {
113
                $link = [self::LINKS_HREF => $link];
114
            }
115
        }
116
117
        return $links;
118
    }
119
120
    /**
121
     * @throws TransformerException
122
     */
123
    protected function noMappingGuard()
124
    {
125
        if (empty($this->mappings) || !is_array($this->mappings)) {
126
            throw new TransformerException(
127
                'No mappings were found. Mappings are required by the transformer to work.'
128
            );
129
        }
130
    }
131
132
    /**
133
     * Changes all array keys to under_score format using recursion.
134
     *
135
     * @param array $array
136
     */
137
    protected function recursiveSetKeysToUnderScore(array &$array)
138
    {
139
        $newArray = [];
140
        foreach ($array as $key => &$value) {
141
            $underscoreKey = RecursiveFormatterHelper::camelCaseToUnderscore($key);
142
            $newArray[$underscoreKey] = $value;
143
144
            if (\is_array($value)) {
145
                $this->recursiveSetKeysToUnderScore($newArray[$underscoreKey]);
146
            }
147
        }
148
        $array = $newArray;
149
    }
150
151
    /**
152
     * @return array
153
     */
154
    protected function buildLinks()
155
    {
156
        $links = \array_filter(
157
            [
158
                self::SELF_LINK => $this->getSelfUrl(),
159
                self::FIRST_LINK => $this->getFirstUrl(),
160
                self::LAST_LINK => $this->getLastUrl(),
161
                self::PREV_LINK => $this->getPrevUrl(),
162
                self::NEXT_LINK => $this->getNextUrl(),
163
            ]
164
        );
165
166
        return $links;
167
    }
168
169
    /**
170
     * @return string
171
     */
172
    public function getSelfUrl()
173
    {
174
        return $this->selfUrl;
175
    }
176
177
    /**
178
     * @param string $selfUrl
179
     *
180
     * @return $this
181
     */
182
    public function setSelfUrl($selfUrl)
183
    {
184
        $this->selfUrl = $selfUrl;
185
186
        return $this;
187
    }
188
189
    /**
190
     * @return string
191
     */
192
    public function getFirstUrl()
193
    {
194
        return $this->firstUrl;
195
    }
196
197
    /**
198
     * @param string $firstUrl
199
     *
200
     * @throws \InvalidArgumentException
201
     */
202
    public function setFirstUrl($firstUrl)
203
    {
204
        $this->firstUrl = (string) $firstUrl;
205
    }
206
207
    /**
208
     * @return string
209
     */
210
    public function getLastUrl()
211
    {
212
        return $this->lastUrl;
213
    }
214
215
    /**
216
     * @param string $lastUrl
217
     *
218
     * @throws \InvalidArgumentException
219
     */
220
    public function setLastUrl($lastUrl)
221
    {
222
        $this->lastUrl = (string) $lastUrl;
223
    }
224
225
    /**
226
     * @return string
227
     */
228
    public function getPrevUrl()
229
    {
230
        return $this->prevUrl;
231
    }
232
233
    /**
234
     * @param string $prevUrl
235
     *
236
     * @throws \InvalidArgumentException
237
     */
238
    public function setPrevUrl($prevUrl)
239
    {
240
        $this->prevUrl = (string) $prevUrl;
241
    }
242
243
    /**
244
     * @return string
245
     */
246
    public function getNextUrl()
247
    {
248
        return $this->nextUrl;
249
    }
250
251
    /**
252
     * @param string $nextUrl
253
     *
254
     * @throws \InvalidArgumentException
255
     */
256
    public function setNextUrl($nextUrl)
257
    {
258
        $this->nextUrl = (string) $nextUrl;
259
    }
260
261
    /**
262
     * @param array  $copy
263
     * @param string $type
264
     *
265
     * @return array
266
     */
267
    protected function getResponseAdditionalLinks(array $copy, $type)
268
    {
269
        if (\is_scalar($type) && !empty($this->mappings[$type])) {
270
            $otherUrls = $this->mappings[$type]->getUrls();
271
            list($idValues, $idProperties) = RecursiveFormatterHelper::getIdPropertyAndValues(
272
                $this->mappings,
273
                $copy,
274
                $type
275
            );
276
277
            $replacedUrls = \str_replace($idProperties, $idValues, $otherUrls);
278
            foreach ($replacedUrls as $key => $value) {
279
                if ($otherUrls[$key] === $value && false !== strpos($value, '{')) {
280
                    unset($replacedUrls[$key]);
281
                }
282
            }
283
284
            return $replacedUrls;
285
        }
286
287
        return [];
288
    }
289
290
    /**
291
     * Replaces the Serializer array structure representing scalar values to the actual scalar value using recursion.
292
     *
293
     * @param array $array
294
     */
295 View Code Duplication
    protected static function formatScalarValues(array &$array)
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...
296
    {
297
        $array = self::arrayToScalarValue($array);
298
299
        if (\is_array($array) && !array_key_exists(Serializer::SCALAR_VALUE, $array)) {
300
            self::loopScalarValues($array, 'formatScalarValues');
301
        }
302
    }
303
304
    /**
305
     * @param array $array
306
     *
307
     * @return array
308
     */
309
    protected static function arrayToScalarValue(array &$array)
310
    {
311
        if (\array_key_exists(Serializer::SCALAR_VALUE, $array)) {
312
            $array = $array[Serializer::SCALAR_VALUE];
313
        }
314
315
        return $array;
316
    }
317
318
    /**
319
     * @param array  $array
320
     * @param string $method
321
     */
322
    protected static function loopScalarValues(array &$array, $method)
323
    {
324
        foreach ($array as $propertyName => &$value) {
325
            if (\is_array($value) && self::LINKS_KEY !== $propertyName) {
326
                self::$method($value);
327
            }
328
        }
329
    }
330
331
    /**
332
     * Simplifies the data structure by removing an array level if data is scalar and has one element in array.
333
     *
334
     * @param array $array
335
     */
336 View Code Duplication
    protected static function flattenObjectsWithSingleKeyScalars(array &$array)
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...
337
    {
338
        if (1 === \count($array) && \is_scalar(\end($array))) {
339
            $array = \array_pop($array);
340
        }
341
342
        if (\is_array($array)) {
343
            self::loopScalarValues($array, 'flattenObjectsWithSingleKeyScalars');
344
        }
345
    }
346
347
    /**
348
     * @return \NilPortugues\Api\Mapping\Mapping[]
349
     */
350
    public function getMappings()
351
    {
352
        return $this->mappings;
353
    }
354
355
    /**
356
     * @param string $alias
357
     *
358
     * @return Mapping|null
359
     */
360
    public function getMappingByAlias($alias)
361
    {
362
        foreach ($this->mappings as $mapping) {
363
            if (0 === strcasecmp($alias, $mapping->getClassAlias())) {
364
                return $mapping;
365
            }
366
        }
367
368
        return;
369
    }
370
371
    /**
372
     * @param string $className
373
     *
374
     * @return Mapping|null
375
     */
376
    public function getMappingByClassName($className)
377
    {
378
        $className = ltrim($className, '\\');
379
380
        return (!empty($this->mappings[$className])) ? $this->mappings[$className] : null;
381
    }
382
}
383