Completed
Branch master (63f127)
by Dominik
03:47 queued 01:52
created

Denormalizer   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 184
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 0%

Importance

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