Serializer::fromArrayTransfer()   D
last analyzed

Complexity

Conditions 19
Paths 14

Size

Total Lines 67
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 30
CRAP Score 20.6722

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 19
eloc 35
c 1
b 0
f 0
nc 14
nop 1
dl 0
loc 67
ccs 30
cts 36
cp 0.8333
crap 20.6722
rs 4.5166

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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