Completed
Pull Request — master (#12)
by Dominik
07:37 queued 14s
created

Normalizer::isAllowed()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3.0416

Importance

Changes 0
Metric Value
dl 0
loc 12
c 0
b 0
f 0
ccs 5
cts 6
cp 0.8333
rs 9.8666
cc 3
nc 3
nop 3
crap 3.0416
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Chubbyphp\Serialization\Normalizer;
6
7
use Chubbyphp\Serialization\Mapping\NormalizationFieldMappingInterface;
8
use Chubbyphp\Serialization\Mapping\NormalizationLinkMappingInterface;
9
use Chubbyphp\Serialization\Mapping\NormalizationObjectMappingInterface;
10
use Chubbyphp\Serialization\SerializerLogicException;
11
use Psr\Log\LoggerInterface;
12
use Psr\Log\NullLogger;
13
use Chubbyphp\Serialization\Authorization\AuthorizationCheckerInterface;
14
use Chubbyphp\Serialization\Authorization\NullAuthorizationChecker;
15
16
final class Normalizer implements NormalizerInterface
17
{
18
    /**
19
     * @var NormalizerObjectMappingRegistryInterface
20
     */
21
    private $normalizerObjectMappingRegistry;
22
23
    /**
24
     * @var LoggerInterface
25
     */
26
    private $logger;
27
28
    /**
29
     * @var AuthorizationCheckerInterface
30
     */
31
    private $authorizationChecker;
32
33
    /**
34
     * @param NormalizerObjectMappingRegistryInterface $normalizerObjectMappingRegistry
35
     * @param LoggerInterface|null                     $logger
36
     * @param AuthorizationCheckerInterface|null       $authorizationChecker
37
     */
38 5
    public function __construct(
39
        NormalizerObjectMappingRegistryInterface $normalizerObjectMappingRegistry,
40
        LoggerInterface $logger = null,
41
        AuthorizationCheckerInterface $authorizationChecker = null
42
    ) {
43 5
        $this->normalizerObjectMappingRegistry = $normalizerObjectMappingRegistry;
44 5
        $this->logger = $logger ?? new NullLogger();
45 5
        $this->authorizationChecker = $authorizationChecker ?? new NullAuthorizationChecker();
46 5
    }
47
48
    /**
49
     * @param object                          $object
50
     * @param NormalizerContextInterface|null $context
51
     * @param string                          $path
52
     *
53
     * @return array
54
     */
55 5
    public function normalize(
56
        $object,
57
        NormalizerContextInterface $context = null,
58
        string $path = ''
59
    ): array {
60 5
        $this->validateDataType($object, $path);
61
62 4
        $context = $context ?? NormalizerContextBuilder::create()->getContext();
63
64 4
        $class = is_object($object) ? get_class($object) : $object;
65 4
        $objectMapping = $this->getObjectMapping($class);
66
67 3
        $fieldMappings = $objectMapping->getNormalizationFieldMappings($path);
68
69 3
        $data = $this->getFieldsByFieldNormalizationMappings($context, $fieldMappings, $path, $object);
70
71 3
        $embeddedMappings = $objectMapping->getNormalizationEmbeddedFieldMappings($path);
72 3
        $embedded = $this->getFieldsByFieldNormalizationMappings($context, $embeddedMappings, $path, $object);
73
74 3
        $linkMappings = $objectMapping->getNormalizationLinkMappings($path);
75 3
        $links = $this->getLinksByLinkNormalizationMappings($context, $linkMappings, $path, $object);
76
77 3
        if ([] !== $embedded) {
78 2
            $data['_embedded'] = $embedded;
79
        }
80
81 3
        if ([] !== $links) {
82 1
            $data['_links'] = $links;
83
        }
84
85 3
        if (null !== $type = $objectMapping->getNormalizationType()) {
86 3
            $data['_type'] = $type;
87
        }
88
89 3
        return $data;
90
    }
91
92
    /**
93
     * @param object $object
94
     * @param string $path
95
     *
96
     * @throws SerializerLogicException
97
     */
98 5
    private function validateDataType($object, string $path)
99
    {
100 5
        if (!is_object($object)) {
101 1
            $exception = SerializerLogicException::createWrongDataType(gettype($object), $path);
102
103 1
            $this->logger->error('serialize: {exception}', ['exception' => $exception->getMessage()]);
104
105 1
            throw $exception;
106
        }
107 4
    }
108
109
    /**
110
     * @param string $class
111
     *
112
     * @return NormalizationObjectMappingInterface
113
     *
114
     * @throws SerializerLogicException
115
     */
116 4
    private function getObjectMapping(string $class): NormalizationObjectMappingInterface
117
    {
118
        try {
119 4
            return $this->normalizerObjectMappingRegistry->getObjectMapping($class);
120 1
        } catch (SerializerLogicException $exception) {
121 1
            $this->logger->error('serialize: {exception}', ['exception' => $exception->getMessage()]);
122
123 1
            throw $exception;
124
        }
125
    }
126
127
    /**
128
     * @param NormalizerContextInterface           $context
129
     * @param NormalizationFieldMappingInterface[] $normalizationFieldMappings
130
     * @param string                               $path
131
     * @param object                               $object
132
     *
133
     * @return array
134
     */
135 3
    private function getFieldsByFieldNormalizationMappings(
136
        NormalizerContextInterface $context,
137
        array $normalizationFieldMappings,
138
        string $path,
139
        $object
140
    ): array {
141 3
        $data = [];
142 3
        foreach ($normalizationFieldMappings as $normalizationFieldMapping) {
143 3
            if (!$this->isAllowed($context, $normalizationFieldMapping, $object)) {
144
                continue;
145
            }
146
147 3
            if (!$this->isWithinGroup($context, $normalizationFieldMapping)) {
148 1
                continue;
149
            }
150
151 3
            $fieldNormalizer = $normalizationFieldMapping->getFieldNormalizer();
152
153 3
            $name = $normalizationFieldMapping->getName();
154
155 3
            $subPath = $this->getSubPathByName($path, $name);
156
157 3
            $this->logger->info('serialize: path {path}', ['path' => $subPath]);
158
159 3
            $data[$name] = $fieldNormalizer->normalizeField($subPath, $object, $context, $this);
160
        }
161
162 3
        return $data;
163
    }
164
165
    /**
166
     * @param NormalizerContextInterface          $context
167
     * @param NormalizationLinkMappingInterface[] $normalizationLinkMappings
168
     * @param string                              $path
169
     * @param object                              $object
170
     *
171
     * @return array
172
     */
173 3
    private function getLinksByLinkNormalizationMappings(
174
        NormalizerContextInterface $context,
175
        array $normalizationLinkMappings,
176
        string $path,
177
        $object
178
    ): array {
179 3
        $links = [];
180 3
        foreach ($normalizationLinkMappings as $normalizationLinkMapping) {
181 3
            if (!$this->isAllowed($context, $normalizationLinkMapping, $object)) {
182
                continue;
183
            }
184
185 3
            if (!$this->isWithinGroup($context, $normalizationLinkMapping)) {
186 1
                continue;
187
            }
188
189 2
            $linkNormalizer = $normalizationLinkMapping->getLinkNormalizer();
190
191 2
            if (null === $link = $linkNormalizer->normalizeLink($path, $object, $context)) {
192 1
                continue;
193
            }
194
195 1
            $links[$normalizationLinkMapping->getName()] = $link;
196
        }
197
198 3
        return $links;
199
    }
200
201
    /**
202
     * @param NormalizerContextInterface                                           $context
203
     * @param NormalizationFieldMappingInterface|NormalizationLinkMappingInterface $mapping
204
     * @param object                                                               $object
205
     *
206
     * @return bool
207
     */
208 3
    private function isAllowed(NormalizerContextInterface $context, $mapping, $object)
209
    {
210 3
        if (!is_callable([$context, 'getRole'])) {
211 1
            return true;
212
        }
213
214 2
        if (!is_callable([$mapping, 'getPermission'])) {
215 2
            return true;
216
        }
217
218
        return $this->authorizationChecker->isAllowed($context->getRole(), $object, $mapping->getPermission());
219
    }
220
221
    /**
222
     * @param NormalizerContextInterface                                           $context
223
     * @param NormalizationFieldMappingInterface|NormalizationLinkMappingInterface $mapping
224
     *
225
     * @return bool
226
     */
227 3
    private function isWithinGroup(NormalizerContextInterface $context, $mapping): bool
228
    {
229 3
        if ([] === $groups = $context->getGroups()) {
230 2
            return true;
231
        }
232
233 1
        foreach ($mapping->getGroups() as $group) {
234 1
            if (in_array($group, $groups, true)) {
235 1
                return true;
236
            }
237
        }
238
239 1
        return false;
240
    }
241
242
    /**
243
     * @param string $path
244
     * @param string $name
245
     *
246
     * @return string
247
     */
248 3
    private function getSubPathByName(string $path, string $name): string
249
    {
250 3
        return '' === $path ? $name : $path.'.'.$name;
251
    }
252
}
253