Completed
Push — master ( 806f9c...5f26bc )
by Dominik
02:12
created

Denormalizer   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 200
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 96.83%

Importance

Changes 0
Metric Value
wmc 24
lcom 1
cbo 10
dl 0
loc 200
ccs 61
cts 63
cp 0.9683
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 2
A addObjectMapping() 0 4 1
C denormalize() 0 31 7
A getObjectMapping() 0 12 2
B denormalizeField() 0 24 3
A handleNotAllowedAddtionalFields() 0 10 1
A isWithinGroup() 0 16 4
A getSubPathByName() 0 4 2
A getSubPathsByNames() 0 9 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Chubbyphp\Deserialization\Denormalizer;
6
7
use Chubbyphp\Deserialization\DeserializerLogicException;
8
use Chubbyphp\Deserialization\DeserializerRuntimeException;
9
use Chubbyphp\Deserialization\Mapping\DenormalizationFieldMappingInterface;
10
use Chubbyphp\Deserialization\Mapping\DenormalizationObjectMappingInterface;
11
use Psr\Log\LoggerInterface;
12
use Psr\Log\NullLogger;
13
14
final class Denormalizer implements DenormalizerInterface
15
{
16
    /**
17
     * @var DenormalizationObjectMappingInterface[]
18
     */
19
    private $objectMappings;
20
21
    /**
22
     * @var array
23
     */
24
    private $classToTypeMappings;
25
26
    /**
27
     * @var LoggerInterface
28
     */
29
    private $logger;
30
31
    /**
32
     * @param array                $objectMappings
33
     * @param LoggerInterface|null $logger
34
     */
35 8
    public function __construct(array $objectMappings, LoggerInterface $logger = null)
36
    {
37 8
        $this->objectMappings = [];
38 8
        $this->classToTypeMappings = [];
39 8
        foreach ($objectMappings as $objectMapping) {
40 7
            $this->addObjectMapping($objectMapping);
41
        }
42
43 8
        $this->logger = $logger ?? new NullLogger();
44 8
    }
45
46
    /**
47
     * @param DenormalizationObjectMappingInterface $objectMapping
48
     */
49 7
    private function addObjectMapping(DenormalizationObjectMappingInterface $objectMapping)
50
    {
51 7
        $this->objectMappings[$objectMapping->getClass()] = $objectMapping;
52 7
    }
53
54
    /**
55
     * @param object|string                     $object
56
     * @param array                             $data
57
     * @param DenormalizerContextInterface|null $context
58
     * @param string                            $path
59
     *
60
     * @return object
61
     *
62
     * @throws DeserializerLogicException
63
     * @throws DeserializerRuntimeException
64
     */
65 8
    public function denormalize($object, array $data, DenormalizerContextInterface $context = null, string $path = '')
66
    {
67 8
        $context = $context ?? DenormalizerContextBuilder::create()->getContext();
68
69 8
        $class = is_object($object) ? get_class($object) : $object;
70 8
        $objectMapping = $this->getObjectMapping($class);
71
72 7
        $type = null;
73 7
        if (isset($data['_type'])) {
74
            $type = $data['_type'];
75
76
            unset($data['_type']);
77
        }
78
79 7
        if (!is_object($object)) {
80 6
            $factory = $objectMapping->getDenormalizationFactory($path, $type);
81 6
            $object = $factory();
82
        }
83
84 7
        foreach ($objectMapping->getDenormalizationFieldMappings($path, $type) as $denormalizationFieldMapping) {
85 7
            $this->denormalizeField($context, $denormalizationFieldMapping, $path, $data, $object);
86
87 7
            unset($data[$denormalizationFieldMapping->getName()]);
88
        }
89
90 7
        if ([] !== $data && !$context->isAllowedAdditionalFields()) {
91 1
            $this->handleNotAllowedAddtionalFields($path, array_keys($data));
92
        }
93
94 6
        return $object;
95
    }
96
97
    /**
98
     * @param string $class
99
     *
100
     * @return DenormalizationObjectMappingInterface
101
     *
102
     * @throws DeserializerLogicException
103
     */
104 8
    private function getObjectMapping(string $class): DenormalizationObjectMappingInterface
105
    {
106 8
        if (isset($this->objectMappings[$class])) {
107 7
            return $this->objectMappings[$class];
108
        }
109
110 1
        $exception = DeserializerLogicException::createMissingMapping($class);
111
112 1
        $this->logger->error('deserialize: {exception}', ['exception' => $exception->getMessage()]);
113
114 1
        throw $exception;
115
    }
116
117
    /**
118
     * @param DenormalizerContextInterface         $context
119
     * @param DenormalizationFieldMappingInterface $denormalizationFieldMapping
120
     * @param string                               $path
121
     * @param array                                $data
122
     * @param object                               $object
123
     */
124 7
    private function denormalizeField(
125
        DenormalizerContextInterface $context,
126
        DenormalizationFieldMappingInterface $denormalizationFieldMapping,
127
        string $path,
128
        array $data,
129
        $object
130
    ) {
131 7
        $name = $denormalizationFieldMapping->getName();
132 7
        if (!array_key_exists($name, $data)) {
133 1
            return;
134
        }
135
136 6
        $fieldDenormalizer = $denormalizationFieldMapping->getFieldDenormalizer();
137
138 6
        if (!$this->isWithinGroup($context, $denormalizationFieldMapping)) {
139 1
            return;
140
        }
141
142 5
        $subPath = $this->getSubPathByName($path, $name);
143
144 5
        $this->logger->info('deserialize: path {path}', ['path' => $subPath]);
145
146 5
        $fieldDenormalizer->denormalizeField($subPath, $object, $data[$name], $context, $this);
147 5
    }
148
149
    /**
150
     * @param string $path
151
     * @param array  $names
152
     */
153 1
    private function handleNotAllowedAddtionalFields(string $path, array $names)
154
    {
155 1
        $exception = DeserializerRuntimeException::createNotAllowedAddtionalFields(
156 1
            $this->getSubPathsByNames($path, $names)
157
        );
158
159 1
        $this->logger->notice('deserialize: {exception}', ['exception' => $exception->getMessage()]);
160
161 1
        throw $exception;
162
    }
163
164
    /**
165
     * @param DenormalizerContextInterface         $context
166
     * @param DenormalizationFieldMappingInterface $fieldMapping
167
     *
168
     * @return bool
169
     */
170 6
    private function isWithinGroup(
171
        DenormalizerContextInterface $context,
172
        DenormalizationFieldMappingInterface $fieldMapping
173
    ): bool {
174 6
        if ([] === $groups = $context->getGroups()) {
175 4
            return true;
176
        }
177
178 2
        foreach ($fieldMapping->getGroups() as $group) {
179 1
            if (in_array($group, $groups, true)) {
180 1
                return true;
181
            }
182
        }
183
184 1
        return false;
185
    }
186
187
    /**
188
     * @param string $path
189
     * @param string $name
190
     *
191
     * @return string
192
     */
193 5
    private function getSubPathByName(string $path, string $name): string
194
    {
195 5
        return '' === $path ? $name : $path.'.'.$name;
196
    }
197
198
    /**
199
     * @param string $path
200
     * @param array  $names
201
     *
202
     * @return array
203
     */
204 1
    private function getSubPathsByNames(string $path, array $names): array
205
    {
206 1
        $subPaths = [];
207 1
        foreach ($names as $name) {
208 1
            $subPaths[] = $this->getSubPathByName($path, $name);
209
        }
210
211 1
        return $subPaths;
212
    }
213
}
214