Serializer   B
last analyzed

Complexity

Total Complexity 45

Size/Duplication

Total Lines 234
Duplicated Lines 0 %

Test Coverage

Coverage 87.7%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 112
c 2
b 0
f 0
dl 0
loc 234
ccs 107
cts 122
cp 0.877
rs 8.8
wmc 45

9 Methods

Rating   Name   Duplication   Size   Complexity  
A fromJsonTransfer() 0 8 2
D fromArrayTransfer() 0 67 19
A toJsonTransfer() 0 8 2
B toArray() 0 38 7
A getPropertyType() 0 7 2
A toJson() 0 8 2
A serializeDateTimeObject() 0 3 1
B toArrayTransfer() 0 59 9
A createSelf() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Serializer 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.

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

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 *  This file is part of the Micro framework package.
7
 *
8
 *  (c) Stanislau Komar <[email protected]>
9
 *
10
 *  For the full copyright and license information, please view the LICENSE
11
 *  file that was distributed with this source code.
12
 */
13
14
namespace Micro\Library\DTO\Serializer;
15
16
use Micro\Library\DTO\Exception\SerializeException;
17
use Micro\Library\DTO\Exception\UnserializeException;
18
use Micro\Library\DTO\Object\AbstractDto;
19
use Micro\Library\DTO\Object\Collection;
20
use Micro\Library\DTO\Preparation\PreparationProcessorInterface;
21
22
class Serializer implements SerializerInterface
23
{
24
    /**
25
     * {@inheritDoc}
26
     */
27 3
    public function fromJsonTransfer(string $jsonDto): AbstractDto
28
    {
29 3
        $arrayDto = json_decode($jsonDto, true);
30 3
        if (!$arrayDto) {
31
            throw new UnserializeException(sprintf('Invalid DTO JSON data: %s', $jsonDto));
32
        }
33
34 3
        return $this->fromArrayTransfer($arrayDto);
35
    }
36
37
    /**
38
     * {@inheritDoc}
39
     */
40 4
    public function fromArrayTransfer(array $itemData): AbstractDto
41
    {
42
        /** @var class-string<AbstractDto>|null $t */
43 4
        $t = $itemData[self::SECTION_TYPE] ?? false;
44 4
        if (!$t || !is_a($t, AbstractDto::class, true)) {
45
            throw new UnserializeException(sprintf('Invalid type. Data %s', json_encode($itemData, \JSON_PRETTY_PRINT)));
46
        }
47
        /**
48
         * @var AbstractDto $object
49
         *
50
         * @psalm-suppress UnsafeInstantiation
51
         */
52 4
        $object = new $t();
53
54 4
        $d = $itemData[self::SECTION_D] ?? [];
55 4
        foreach ($d as $propertyName => $propertyMeta) {
56 4
            $propertyType = $propertyMeta[self::SECTION_TYPE] ?? false;
57 4
            $propertyData = $propertyMeta[self::SECTION_D] ?? null;
58 4
            $isPropertyObject = !(false === $propertyType) && class_exists($propertyType);
59 4
            if (!$propertyType) {
60
                throw new UnserializeException(sprintf('Invalid type. Data %s', json_encode($itemData, \JSON_PRETTY_PRINT)));
61
            }
62
63 4
            if ($isPropertyObject && is_a($propertyType, \DateTimeInterface::class, true)) {
64 2
                $tmpV = new $propertyType($propertyData);
65 2
                $object->offsetSet($propertyName, $tmpV);
66
67 2
                continue;
68
            }
69
70 4
            if ($propertyData && $isPropertyObject && is_a($propertyType, AbstractDto::class, true)) {
71 3
                $tmpV = $this->createSelf()->fromArrayTransfer($propertyData);
72 3
                $object->offsetSet($propertyName, $tmpV);
73
74 3
                continue;
75
            }
76
77 4
            if ($propertyData && $isPropertyObject && is_a($propertyType, Collection::class, true)) {
78 2
                $collectionItems = [];
79
80 2
                foreach ($propertyData as $collectionItem) {
81 2
                    $collectionItemType = $collectionItem[self::SECTION_TYPE] ?? false;
82 2
                    if (!$collectionItemType) {
83
                        throw new UnserializeException(sprintf('Invalid collection item type "%s"', \is_object($propertyType) ? \get_class($propertyType) : $propertyType));
84
                    }
85
86 2
                    if (is_a($collectionItemType, \DateTimeInterface::class, true)) {
87
                        $collectionItems[] = new $collectionItemType($collectionItem);
88
89
                        continue;
90
                    }
91
92 2
                    if (is_a($collectionItemType, AbstractDto::class, true)) {
93 2
                        $collectionItems[] = $this->createSelf()->fromArrayTransfer($collectionItem);
94
95 2
                        continue;
96
                    }
97
                    $collectionItems[] = $collectionItem[self::SECTION_D];
98
                }
99
100 2
                $propertyData = $collectionItems;
101
            }
102
103 4
            $object->offsetSet($propertyName, $propertyData);
104
        }
105
106 4
        return $object;
107
    }
108
109 5
    public function toArray(AbstractDto $abstractDto, bool $serializeEmptyValues = true): array
110
    {
111 5
        $reflectionDto = new \ReflectionClass($abstractDto);
112 5
        $attributesMetadataReflection = $reflectionDto->getMethod(PreparationProcessorInterface::CLASS_PROPS_META_METHOD);
113 5
        $attributesMetadataReflection->setAccessible(true);
114 5
        $metadata = $attributesMetadataReflection->invoke($reflectionDto);
115 5
        $attributesMetadataReflection->setAccessible(false);
116 5
        $resultArray = [];
117 5
        foreach ($metadata as $propertyName => $propertyMeta) {
118 5
            $value = $abstractDto->offsetGet($propertyName);
119 5
            $tmp = $value;
120 5
            if ($value instanceof Collection) {
121 3
                $tmpColVal = null;
122 3
                foreach ($value as $item) {
123 3
                    if (!$tmpColVal) {
124 3
                        $tmpColVal = [];
125
                    }
126
127 3
                    if ($item instanceof AbstractDto) {
128 3
                        $tmpColVal[] = $this->createSelf()->toArray($item, $serializeEmptyValues);
129
130 3
                        continue;
131
                    }
132
133
                    $tmpColVal[] = $item;
134
                }
135
136 3
                $tmp = $tmpColVal;
137
            }
138
139 5
            if ($value instanceof AbstractDto) {
140 4
                $tmp = $this->createSelf()->toArray($value, $serializeEmptyValues);
141
            }
142
143 5
            $resultArray[$propertyName] = $tmp;
144
        }
145
146 5
        return $resultArray;
147
    }
148
149 3
    public function toJson(AbstractDto $abstractDto, bool $serializeEmptyValues = true, int $flags = 0): string
150
    {
151 3
        $json = json_encode($this->toArray($abstractDto, $serializeEmptyValues), $flags);
152 3
        if (!$json) {
153
            throw new SerializeException();
154
        }
155
156 3
        return $json;
157
    }
158
159 5
    public function toArrayTransfer(AbstractDto $abstractDto): array
160
    {
161 5
        $reflectionDto = new \ReflectionClass($abstractDto);
162 5
        $attributesMetadataReflection = $reflectionDto->getMethod(PreparationProcessorInterface::CLASS_PROPS_META_METHOD);
163 5
        $attributesMetadataReflection->setAccessible(true);
164 5
        $result = $attributesMetadataReflection->invoke($abstractDto);
165 5
        $attributesMetadataReflection->setAccessible(false);
166 5
        $resultProperties = [];
167
168 5
        foreach ($result as $propName => $meta) {
169 5
            $propertyConfig = [];
170 5
            $reflectionProperty = $reflectionDto->getProperty($propName);
171 5
            $reflectionProperty->setAccessible(true);
172 5
            $value = $reflectionProperty->getValue($abstractDto);
173 5
            $reflectionProperty->setAccessible(false);
174
175 5
            $propertyConfig[self::SECTION_TYPE] = $this->getPropertyType($value);
176 5
            if ($value instanceof AbstractDto) {
177 4
                $value = $this->createSelf()->toArrayTransfer($value);
178
            }
179
180 5
            if ($value instanceof \DateTimeInterface) {
181 3
                $value = $this->serializeDateTimeObject($value);
182
            }
183
184 5
            if ($value instanceof Collection) {
185 3
                $tmpValue = null;
186
187 3
                foreach ($value as $item) {
188 3
                    if (!$tmpValue) {
189 3
                        $tmpValue = [];
190
                    }
191
192 3
                    if ($item instanceof \DateTimeInterface) {
193
                        $item = $this->serializeDateTimeObject($item);
194
                    }
195
196 3
                    if ($item instanceof AbstractDto) {
197 3
                        $tmpValue[] = $this->createSelf()->toArrayTransfer($item);
198
199 3
                        continue;
200
                    }
201
202
                    $tmpValue[] = [
203
                        self::SECTION_TYPE => $this->getPropertyType($item),
204
                        self::SECTION_D => $item,
205
                    ];
206
                }
207
208 3
                $value = $tmpValue;
209
            }
210
211 5
            $propertyConfig[self::SECTION_D] = $value;
212 5
            $resultProperties[$propName] = $propertyConfig;
213
        }
214
215 5
        return [
216 5
            self::SECTION_TYPE => $reflectionDto->getName(),
217 5
            self::SECTION_D => $resultProperties,
218 5
        ];
219
    }
220
221
    /**
222
     * {@inheritDoc}
223
     */
224 4
    public function toJsonTransfer(AbstractDto $abstractDto, int $flags = 0): string
225
    {
226 4
        $result = json_encode($this->toArrayTransfer($abstractDto), $flags);
227 4
        if (!$result) {
228
            throw new SerializeException();
229
        }
230
231 4
        return $result;
232
    }
233
234
    /**
235
     * @param \DateTimeInterface $dateTime
236
     *
237
     * @return string
238
     */
239 3
    protected function serializeDateTimeObject(\DateTimeInterface $dateTime): string
240
    {
241 3
        return $dateTime->format('c');
242
    }
243
244 7
    protected function createSelf(): self
245
    {
246 7
        return new self();
247
    }
248
249 5
    protected function getPropertyType(mixed $value): string
250
    {
251 5
        if (\is_object($value)) {
252 4
            return \get_class($value);
253
        }
254
255 5
        return \gettype($value);
256
    }
257
}
258