-
Notifications
You must be signed in to change notification settings - Fork 981
/
StructNormalizer.php
166 lines (133 loc) · 4.69 KB
/
StructNormalizer.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
<?php declare(strict_types=1);
namespace Shopware\Core\Framework\Struct\Serializer;
use Shopware\Core\Framework\Struct\Struct;
use Symfony\Component\Serializer\Encoder\JsonDecode;
use Symfony\Component\Serializer\Encoder\JsonEncode;
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
class StructNormalizer implements DenormalizerInterface, NormalizerInterface
{
/**
* Internal cache property which contains created reflection classes
*
* @var \ReflectionClass[]
*/
private $classes = [];
/**
* {@inheritdoc}
*/
public function normalize($data, $format = null, array $context = [])
{
$encoder = new JsonEncode();
return (new JsonDecode([JsonDecode::ASSOCIATIVE => true]))->decode($encoder->encode($data, 'json'), 'json');
}
/**
* {@inheritdoc}
*/
public function supportsNormalization($data, $format = null): bool
{
return $data instanceof Struct;
}
/**
* {@inheritdoc}
*/
public function denormalize($data, $type = null, $format = null, array $context = [])
{
if (\is_string($data) && $date = $this->createDate($data)) {
return $date;
}
if (!\is_array($data)) {
return $data;
}
if (!$this->isObject($data)) {
return array_map([$this, 'denormalize'], $data);
}
$class = $data['_class'];
unset($data['_class']);
//iterate arguments to resolve other serialized objects
$arguments = array_map(function ($argument) {
return $this->denormalize($argument);
}, $data);
//create object instance
return $this->createInstance($class, $arguments);
}
/**
* {@inheritdoc}
*/
public function supportsDenormalization($data, $type, $format = null): bool
{
return \is_array($data) && \array_key_exists('_class', $data);
}
private function isObject(array $argument): bool
{
return isset($argument['_class']);
}
private function createInstance(string $class, array $arguments): Struct
{
try {
$reflectionClass = $this->getReflectionClass($class);
} catch (\ReflectionException $exception) {
throw new InvalidArgumentException($exception->getMessage());
}
$struct = $reflectionClass->newInstanceWithoutConstructor();
if (!$struct instanceof Struct) {
throw new InvalidArgumentException(
sprintf('Unable to unserialize a non-struct class: %s', $reflectionClass->getName())
);
}
if (!$reflectionClass->getConstructor()) {
$struct->assign($arguments);
return $struct;
}
$constructorParams = $reflectionClass->getConstructor()->getParameters();
if (\count($constructorParams) <= 0) {
$struct->assign($arguments);
return $struct;
}
$params = [];
foreach ($constructorParams as $constructorParam) {
$name = $constructorParam->getName();
if (!\array_key_exists($name, $arguments)) {
if (!$constructorParam->isOptional()) {
throw new InvalidArgumentException(
sprintf(
'Required constructor parameter missing: "$%s". Please check if the property is protected and not private.',
$name
)
);
}
$params[] = $constructorParam->getDefaultValue();
continue;
}
$params[] = $arguments[$name];
unset($arguments[$name]);
}
$struct = $reflectionClass->newInstanceArgs($params);
if (!$struct instanceof Struct) {
throw new InvalidArgumentException(
sprintf('Unable to unserialize a non-struct class: %s', $reflectionClass->getName())
);
}
$struct->assign($arguments);
return $struct;
}
/**
* @throws \ReflectionException
*/
private function getReflectionClass(string $class): \ReflectionClass
{
if (!isset($this->classes[$class])) {
$this->classes[$class] = new \ReflectionClass($class);
}
return $this->classes[$class];
}
private function createDate(string $date): ?\DateTimeInterface
{
$d = \DateTime::createFromFormat(\DateTime::ATOM, $date);
if ($d && $d->format(\DateTime::ATOM) === $date) {
return $d;
}
return null;
}
}