Passed
Push — master ( cc6a18...73e738 )
by Dominik
01:53
created

Denormalizer   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 189
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 23
lcom 1
cbo 10
dl 0
loc 189
ccs 60
cts 60
cp 1
rs 10
c 0
b 0
f 0

9 Methods

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