Passed
Push — master ( 5f26bc...fdb2cd )
by Dominik
01:56
created

Denormalizer   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 182
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 22
lcom 1
cbo 11
dl 0
loc 182
ccs 56
cts 56
cp 1
rs 10
c 0
b 0
f 0

8 Methods

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