Test Failed
Pull Request — master (#12)
by Vincent
12:16
created

Serializer::normalizationContext()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 3
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 7
ccs 0
cts 0
cp 0
crap 6
rs 10
1
<?php
2
3
namespace Bdf\Serializer;
4
5
use Bdf\Serializer\Context\DenormalizationContext;
6
use Bdf\Serializer\Context\NormalizationContext;
7
use Bdf\Serializer\Normalizer\NormalizerInterface;
8
use Bdf\Serializer\Normalizer\NormalizerLoaderInterface;
9
use Bdf\Serializer\Type\Type;
10
use Bdf\Serializer\Type\TypeFactory;
11
12
/**
13
 * Serializer
14
 *
15
 * @author  Seb
16
 *
17
 * @implements NormalizerInterface<mixed>
18
 */
19
class Serializer implements SerializerInterface, NormalizerInterface, BinarySerializerInterface
20
{
21
    /**
22
     * The loader of normalizers
23
     *
24
     * @var NormalizerLoaderInterface
25
     */
26
    private $loader;
27
28
    /**
29
     * @var DenormalizationContext|null
30
     */
31
    private $defaultDenormalizationContext;
32
33 190
    /**
34
     * @var NormalizationContext|null
35 190
     */
36
    private $defaultNormalizationContext;
37
38
    /**
39
     * @param NormalizerLoaderInterface $loader
40
     * @param array<string, mixed>|null $defaultDenormalizationOptions Default options to use when denormalizing (i.e. convert serialized data to PHP data).
41 26
     * @param array<string, mixed>|null $defaultNormalizationOptions Default options to use when normalizing (i.e. convert PHP data to serialized data).
42
     */
43
    public function __construct(NormalizerLoaderInterface $loader, ?array $defaultDenormalizationOptions = null, ?array $defaultNormalizationOptions = null)
44 26
    {
45 24
        $this->loader = $loader;
46
47 2
        if ($defaultDenormalizationOptions !== null) {
48
            $this->defaultDenormalizationContext = new DenormalizationContext($this, $defaultDenormalizationOptions);
49
        }
50
51 2
        if ($defaultNormalizationOptions !== null) {
52
            $this->defaultNormalizationContext = new NormalizationContext($this, $defaultNormalizationOptions);
53
        }
54
    }
55
56
    /**
57
     * {@inheritdoc}
58 30
     */
59
    public function serialize($data, $format, array $context = [])
60 30
    {
61
        switch ($format) {
62 30
            case 'json':
63
                return $this->toJson($data, $context);
64
65
            case 'binary':
66
                return $this->toBinary($data, $context);
67
68
            default:
69
                return $this->toArray($data, $context);
70
        }
71
    }
72
73
    /**
74
     * {@inheritdoc}
75
     */
76 82
    public function toJson($data, array $context = [])
77
    {
78 82
        $context = $this->normalizationContext($context);
79
80
        return json_encode($this->normalize($data, $context), $context->option('json_options', 0));
0 ignored issues
show
Bug introduced by
0 of type integer is incompatible with the type Bdf\Serializer\Context\T expected by parameter $default of Bdf\Serializer\Context\Context::option(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

80
        return json_encode($this->normalize($data, $context), $context->option('json_options', /** @scrutinizer ignore-type */ 0));
Loading history...
81
    }
82
83
    /**
84 112
     * {@inheritdoc}
85
     */
86 112
    public function toBinary($data, array $context = [])
87 100
    {
88
        return igbinary_serialize($this->normalize($data, $this->normalizationContext($context)));
89
    }
90 110
91 14
    /**
92
     * {@inheritdoc}
93 14
     */
94 14
    public function toArray($data, array $context = [])
95
    {
96
        return $this->normalize($data, $this->normalizationContext($context));
97 14
    }
98
99
    /**
100 108
     * {@inheritdoc}
101
     */
102 102
    public function normalize($data, NormalizationContext $context)
103 8
    {
104 8
        if (null === $data || is_scalar($data)) {
105 8
            return $data;
106 8
        }
107
108
        if (is_array($data)) {
109 94
            $normalized = [];
110
111
            foreach ($data as $key => $value) {
112
                $normalized[$key] = $this->normalize($value, $context);
113
            }
114
115 8
            return $normalized;
116
        }
117
118 8
        $normalized = $this->loader->getNormalizer($data)->normalize($data, $context);
119 6
120
        if ($context->includeMetaType()) {
121 2
            return [
122
                '@type' => get_class($data),
123
                'data'  => $normalized,
124
            ];
125 2
        }
126
127
        return $normalized;
128
    }
129
130
    /**
131
     * {@inheritdoc}
132 70
     */
133
    public function deserialize($data, $type, $format, array $context = [])
134 70
    {
135
        switch ($format) {
136
            case 'json':
137
                return $this->fromJson($data, $type, $context);
138
139
            case 'binary':
140 12
                return $this->fromBinary($data, $type, $context);
141
142 12
            default:
143
                return $this->fromArray($data, $type, $context);
144 12
        }
145
    }
146
147
    /**
148
     * {@inheritdoc}
149
     */
150
    public function fromArray(array $data, $type, array $context = [])
151
    {
152
        return $this->denormalize($data, TypeFactory::createType($type), $this->denormalizationContext($context));
153
    }
154
155
    /**
156
     * {@inheritdoc}
157
     */
158
    public function fromJson(string $json, $type, array $context = [])
159
    {
160
        $context = $this->denormalizationContext($context);
161
162
        return $this->denormalize(json_decode($json, true, 512, $context->option('json_options', 0)), TypeFactory::createType($type), $context);
0 ignored issues
show
Bug introduced by
0 of type integer is incompatible with the type Bdf\Serializer\Context\T expected by parameter $default of Bdf\Serializer\Context\Context::option(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

162
        return $this->denormalize(json_decode($json, true, 512, $context->option('json_options', /** @scrutinizer ignore-type */ 0)), TypeFactory::createType($type), $context);
Loading history...
163 82
    }
164
165 82
    /**
166 10
     * {@inheritdoc}
167
     */
168
    public function fromBinary(string $raw, $type, array $context = [])
169 82
    {
170
        return $this->denormalize(igbinary_unserialize($raw), TypeFactory::createType($type), $this->denormalizationContext($context));
171 82
    }
172 22
173
    /**
174 22
     * {@inheritdoc}
175 22
     *
176
     * @template T
177
     * @param mixed $data
178 22
     * @param Type<T> $type
179
     * @return T|T[]
180
     */
181 82
    public function denormalize($data, Type $type, DenormalizationContext $context)
182 64
    {
183
        if (!is_scalar($data) && !is_array($data)) {
184
            return $data;
185
        }
186
187
        $type = $type->hint($data);
188
189 78
        if ($type->isArray()) {
190
            $denormalized = [];
191 76
192
            foreach ((array)$data as $key => $value) {
193
                $denormalized[$key] = $this->denormalize($value, $type->subType() ?? TypeFactory::mixedType(), $context);
194
            }
195
196
            return $denormalized;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $denormalized returns the type array which is incompatible with the return type mandated by Bdf\Serializer\Normalize...nterface::denormalize() of Bdf\Serializer\Normalizer\T.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
197 2
        }
198
199 2
        if ($type->isBuildin()) {
200
            return $type->convert($data);
201
        }
202
203
        /**
204
         * @var NormalizerInterface<T> $normalizer
205
         * @psalm-suppress ArgumentTypeCoercion
206
         */
207 14
        $normalizer = $this->loader->getNormalizer($type->name());
208
209 14
        return $normalizer->denormalize($data, $type, $context);
210
    }
211
212
    /**
213
     * {@inheritdoc}
214
     */
215
    public function supports(string $className): bool
216
    {
217
        return true;
218
    }
219
220
    /**
221
     * Get the normalizer loader
222
     *
223
     * @return NormalizerLoaderInterface
224
     */
225
    public function getLoader(): NormalizerLoaderInterface
226
    {
227
        return $this->loader;
228
    }
229
230
    /**
231
     * Create a new denormalization context, overriding the default context
232
     *
233
     * @param array<string, mixed> $context
234
     * @return DenormalizationContext
235
     */
236
    private function denormalizationContext(array $context): DenormalizationContext
237
    {
238
        if ($this->defaultDenormalizationContext !== null) {
239
            return $this->defaultDenormalizationContext->duplicate($context);
240
        }
241
242
        return new DenormalizationContext($this, $context);
243
    }
244
245
    /**
246
     * Create a new normalization context, overriding the default context
247
     *
248
     * @param array<string, mixed> $context
249
     * @return NormalizationContext
250
     */
251
    private function normalizationContext(array $context): NormalizationContext
252
    {
253
        if ($this->defaultNormalizationContext !== null) {
254
            return $this->defaultNormalizationContext->duplicate($context);
255
        }
256
257
        return new NormalizationContext($this, $context);
258
    }
259
}
260