DateHandler   C
last analyzed

Complexity

Total Complexity 53

Size/Duplication

Total Lines 344
Duplicated Lines 0 %

Test Coverage

Coverage 90.83%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 117
dl 0
loc 344
ccs 99
cts 109
cp 0.9083
rs 6.96
c 2
b 0
f 0
wmc 53

18 Methods

Rating   Name   Duplication   Size   Complexity  
A serializeDateInterval() 0 9 3
A serializeDateTimeImmutable() 0 7 1
A deserializeDateTimeImmutableFromJson() 0 7 2
A getSubscribingMethods() 0 36 3
A deserializeDateIntervalFromJson() 0 7 2
A parseDateInterval() 0 17 3
A deserializeDateTimeFromJson() 0 7 2
A getSerializationFormat() 0 3 1
A getDeserializationFormats() 0 11 4
A __construct() 0 10 2
A serializeDateTime() 0 3 1
A deserializeDateIntervalFromXml() 0 7 2
A deserializeDateTimeImmutableFromXml() 0 7 2
A isDataXmlNull() 0 5 2
A serializeDateTimeInterface() 0 16 4
A deserializeDateTimeFromXml() 0 7 2
C format() 0 37 11
A parseDateTime() 0 28 6

How to fix   Complexity   

Complex Class

Complex classes like DateHandler often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DateHandler, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace JMS\Serializer\Handler;
6
7
use JMS\Serializer\Exception\RuntimeException;
8
use JMS\Serializer\GraphNavigatorInterface;
9
use JMS\Serializer\SerializationContext;
10
use JMS\Serializer\Type\Type;
11
use JMS\Serializer\Visitor\DeserializationVisitorInterface;
12
use JMS\Serializer\Visitor\SerializationVisitorInterface;
13
use JMS\Serializer\XmlSerializationVisitor;
14
15
/**
16
 * @phpstan-import-type TypeArray from Type
17
 */
18
final class DateHandler implements SubscribingHandlerInterface
19
{
20
    /**
21 329
     * @var string
22
     */
23 329
    private $defaultSerializationFormat;
24 329
25 329
    /**
26
     * @var array<string>
27 329
     */
28
    private $defaultDeserializationFormats;
29 329
30 329
    /**
31 329
     * @var \DateTimeZone
32 329
     */
33 329
    private $defaultTimezone;
34
35
    /**
36
     * @var bool
37 329
     */
38 329
    private $xmlCData;
39 329
40 329
    /**
41 329
     * {@inheritdoc}
42 329
     */
43
    public static function getSubscribingMethods()
44
    {
45
        $methods = [];
46
        $types = [\DateTime::class, \DateTimeImmutable::class, \DateInterval::class];
47 329
48
        foreach (['json', 'xml'] as $format) {
49
            foreach ($types as $type) {
50 334
                $methods[] = [
51
                    'type' => $type,
52 334
                    'direction' => GraphNavigatorInterface::DIRECTION_DESERIALIZATION,
53 334
                    'format' => $format,
54 334
                ];
55 334
                $methods[] = [
56
                    'type' => $type,
57 19
                    'format' => $format,
58
                    'direction' => GraphNavigatorInterface::DIRECTION_SERIALIZATION,
59
                    'method' => 'serialize' . $type,
60
                ];
61
            }
62
63 19
            $methods[] = [
64 2
                'type' => \DateTimeInterface::class,
65
                'direction' => GraphNavigatorInterface::DIRECTION_DESERIALIZATION,
66
                'format' => $format,
67 17
                'method' => 'deserializeDateTimeFrom' . ucfirst($format),
68 17
            ];
69 2
70
            $methods[] = [
71
                'type' => \DateTimeInterface::class,
72 15
                'direction' => GraphNavigatorInterface::DIRECTION_SERIALIZATION,
73
                'format' => $format,
74
                'method' => 'serializeDateTimeInterface',
75 15
            ];
76
        }
77 15
78
        return $methods;
79
    }
80 4
81
    /**
82
     * @param array<string> $defaultDeserializationFormats
83
     */
84
    public function __construct(
85
        string $defaultFormat = \DateTime::ATOM,
86 4
        string $defaultTimezone = 'UTC',
87
        bool $xmlCData = true,
88
        array $defaultDeserializationFormats = []
89 2
    ) {
90
        $this->defaultSerializationFormat = $defaultFormat;
91 2
        $this->defaultDeserializationFormats = [] === $defaultDeserializationFormats ? [$defaultFormat] : $defaultDeserializationFormats;
92
        $this->defaultTimezone = new \DateTimeZone($defaultTimezone);
93 2
        $this->xmlCData = $xmlCData;
94
    }
95
96
    /**
97 2
     * @param TypeArray $type
0 ignored issues
show
Bug introduced by
The type JMS\Serializer\Handler\TypeArray was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
98
     *
99
     * @return \DOMCdataSection|\DOMText|mixed
100 7
     */
101
    public function serializeDateTimeInterface(
102 7
        SerializationVisitorInterface $visitor,
103 7
        \DateTimeInterface $date,
104
        array $type,
105
        SerializationContext $context
0 ignored issues
show
Unused Code introduced by
The parameter $context is not used and could be removed. ( Ignorable by Annotation )

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

105
        /** @scrutinizer ignore-unused */ SerializationContext $context

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
106 5
    ) {
107
        if ($visitor instanceof XmlSerializationVisitor && false === $this->xmlCData) {
108 5
            return $visitor->visitSimpleString($date->format($this->getSerializationFormat($type)), $type);
109
        }
110
111
        $format = $this->getSerializationFormat($type);
112 5
        if ('U' === $format) {
113
            return $visitor->visitInteger((int) $date->format($format), $type);
114
        }
115 1
116
        return $visitor->visitString($date->format($this->getSerializationFormat($type)), $type);
117 1
    }
118
119
    /**
120
     * @param TypeArray $type
121 1
     *
122
     * @return \DOMCdataSection|\DOMText|mixed
123
     */
124 1
    public function serializeDateTime(SerializationVisitorInterface $visitor, \DateTime $date, array $type, SerializationContext $context)
125
    {
126 1
        return $this->serializeDateTimeInterface($visitor, $date, $type, $context);
127
    }
128
129
    /**
130 1
     * @param TypeArray $type
131
     *
132
     * @return \DOMCdataSection|\DOMText|mixed
133 11
     */
134
    public function serializeDateTimeImmutable(
135 11
        SerializationVisitorInterface $visitor,
136 2
        \DateTimeImmutable $date,
137
        array $type,
138
        SerializationContext $context
139 9
    ) {
140
        return $this->serializeDateTimeInterface($visitor, $date, $type, $context);
141
    }
142 4
143
    /**
144 4
     * @param TypeArray $type
145
     *
146
     * @return \DOMCdataSection|\DOMText|mixed
147
     */
148 4
    public function serializeDateInterval(SerializationVisitorInterface $visitor, \DateInterval $date, array $type, SerializationContext $context)
0 ignored issues
show
Unused Code introduced by
The parameter $context is not used and could be removed. ( Ignorable by Annotation )

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

148
    public function serializeDateInterval(SerializationVisitorInterface $visitor, \DateInterval $date, array $type, /** @scrutinizer ignore-unused */ SerializationContext $context)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
149
    {
150
        $iso8601DateIntervalString = $this->format($date);
151 1
152
        if ($visitor instanceof XmlSerializationVisitor && false === $this->xmlCData) {
153 1
            return $visitor->visitSimpleString($iso8601DateIntervalString, $type);
154
        }
155
156
        return $visitor->visitString($iso8601DateIntervalString, $type);
157 1
    }
158
159
    /**
160 19
     * @param mixed $data
161
     */
162 19
    private function isDataXmlNull($data): bool
163 19
    {
164
        $attributes = $data->attributes('xsi', true);
165 19
166 5
        return isset($attributes['nil'][0]) && 'true' === (string) $attributes['nil'][0];
167
    }
168 14
169
    /**
170
     * @param mixed $data
171 19
     * @param TypeArray $type
172
     */
173
    public function deserializeDateTimeFromXml(DeserializationVisitorInterface $visitor, $data, array $type): ?\DateTimeInterface
0 ignored issues
show
Unused Code introduced by
The parameter $visitor is not used and could be removed. ( Ignorable by Annotation )

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

173
    public function deserializeDateTimeFromXml(/** @scrutinizer ignore-unused */ DeserializationVisitorInterface $visitor, $data, array $type): ?\DateTimeInterface

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
174
    {
175 19
        if ($this->isDataXmlNull($data)) {
176 4
            return null;
177
        }
178
179 19
        return $this->parseDateTime($data, $type);
180
    }
181
182 2
    /**
183
     * @param mixed $data
184 2
     * @param TypeArray $type
185
     */
186 2
    public function deserializeDateTimeImmutableFromXml(DeserializationVisitorInterface $visitor, $data, array $type): ?\DateTimeInterface
0 ignored issues
show
Unused Code introduced by
The parameter $visitor is not used and could be removed. ( Ignorable by Annotation )

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

186
    public function deserializeDateTimeImmutableFromXml(/** @scrutinizer ignore-unused */ DeserializationVisitorInterface $visitor, $data, array $type): ?\DateTimeInterface

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
187
    {
188
        if ($this->isDataXmlNull($data)) {
189
            return null;
190
        }
191 2
192
        return $this->parseDateTime($data, $type, true);
193
    }
194
195
    /**
196
     * @param mixed $data
197
     * @param TypeArray $type
198 19
     */
199
    public function deserializeDateIntervalFromXml(DeserializationVisitorInterface $visitor, $data, array $type): ?\DateInterval
0 ignored issues
show
Unused Code introduced by
The parameter $visitor is not used and could be removed. ( Ignorable by Annotation )

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

199
    public function deserializeDateIntervalFromXml(/** @scrutinizer ignore-unused */ DeserializationVisitorInterface $visitor, $data, array $type): ?\DateInterval

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $type is not used and could be removed. ( Ignorable by Annotation )

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

199
    public function deserializeDateIntervalFromXml(DeserializationVisitorInterface $visitor, $data, /** @scrutinizer ignore-unused */ array $type): ?\DateInterval

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
200 19
    {
201 3
        if ($this->isDataXmlNull($data)) {
202
            return null;
203 17
        }
204 9
205
        return $this->parseDateInterval((string) $data);
206 10
    }
207
208
    /**
209
     * @param mixed $data
210
     * @param TypeArray $type
211
     */
212
    public function deserializeDateTimeFromJson(DeserializationVisitorInterface $visitor, $data, array $type): ?\DateTimeInterface
0 ignored issues
show
Unused Code introduced by
The parameter $visitor is not used and could be removed. ( Ignorable by Annotation )

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

212
    public function deserializeDateTimeFromJson(/** @scrutinizer ignore-unused */ DeserializationVisitorInterface $visitor, $data, array $type): ?\DateTimeInterface

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
213 19
    {
214
        if (null === $data) {
215 19
            return null;
216
        }
217
218
        return $this->parseDateTime($data, $type);
219
    }
220
221
    /**
222 3
     * @param mixed $data
223
     * @param TypeArray $type
224 3
     */
225
    public function deserializeDateTimeImmutableFromJson(DeserializationVisitorInterface $visitor, $data, array $type): ?\DateTimeInterface
0 ignored issues
show
Unused Code introduced by
The parameter $visitor is not used and could be removed. ( Ignorable by Annotation )

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

225
    public function deserializeDateTimeImmutableFromJson(/** @scrutinizer ignore-unused */ DeserializationVisitorInterface $visitor, $data, array $type): ?\DateTimeInterface

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
226 3
    {
227 1
        if (null === $data) {
228
            return null;
229
        }
230 3
231
        return $this->parseDateTime($data, $type, true);
232
    }
233
234 3
    /**
235 1
     * @param mixed $data
236
     * @param TypeArray $type
237
     */
238 3
    public function deserializeDateIntervalFromJson(DeserializationVisitorInterface $visitor, $data, array $type): ?\DateInterval
0 ignored issues
show
Unused Code introduced by
The parameter $visitor is not used and could be removed. ( Ignorable by Annotation )

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

238
    public function deserializeDateIntervalFromJson(/** @scrutinizer ignore-unused */ DeserializationVisitorInterface $visitor, $data, array $type): ?\DateInterval

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $type is not used and could be removed. ( Ignorable by Annotation )

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

238
    public function deserializeDateIntervalFromJson(DeserializationVisitorInterface $visitor, $data, /** @scrutinizer ignore-unused */ array $type): ?\DateInterval

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
239 3
    {
240
        if (null === $data) {
241
            return null;
242 3
        }
243 1
244
        return $this->parseDateInterval($data);
245
    }
246 3
247 3
    /**
248
     * @param mixed $data
249
     * @param TypeArray $type
250 3
     */
251 1
    private function parseDateTime($data, array $type, bool $immutable = false): \DateTimeInterface
252
    {
253
        $timezone = !empty($type['params'][1]) ? new \DateTimeZone($type['params'][1]) : $this->defaultTimezone;
254 3
        $formats = $this->getDeserializationFormats($type);
255 1
256
        $formatTried = [];
257
        foreach ($formats as $format) {
258 3
            if ($immutable) {
259
                $datetime = \DateTimeImmutable::createFromFormat($format, (string) $data, $timezone);
260
            } else {
261
                $datetime = \DateTime::createFromFormat($format, (string) $data, $timezone);
262
            }
263
264
            if (false !== $datetime) {
265
                if ('U' === $format) {
266
                    $datetime = $datetime->setTimezone($timezone);
267
                }
268
269
                return $datetime;
270
            }
271
272
            $formatTried[] = $format;
273
        }
274
275
        throw new RuntimeException(sprintf(
276
            'Invalid datetime "%s", expected one of the format %s.',
277
            $data,
278
            '"' . implode('", "', $formatTried) . '"',
279
        ));
280
    }
281
282
    private function parseDateInterval(string $data): \DateInterval
283
    {
284
        $dateInterval = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $dateInterval is dead and can be removed.
Loading history...
285
        try {
286
            $f = 0.0;
287
            if (preg_match('~\.\d+~', $data, $match)) {
288
                $data = str_replace($match[0], '', $data);
289
                $f = (float) $match[0];
290
            }
291
292
            $dateInterval = new \DateInterval($data);
293
            $dateInterval->f = $f;
294
        } catch (\Throwable $e) {
295
            throw new RuntimeException(sprintf('Invalid dateinterval "%s", expected ISO 8601 format', $data), 0, $e);
296
        }
297
298
        return $dateInterval;
299
    }
300
301
    /**
302
     * @param TypeArray $type
303
     */
304
    private function getDeserializationFormats(array $type): array
305
    {
306
        if (isset($type['params'][2])) {
307
            return is_array($type['params'][2]) ? $type['params'][2] : [$type['params'][2]];
308
        }
309
310
        if (isset($type['params'][0])) {
311
            return [$type['params'][0]];
312
        }
313
314
        return $this->defaultDeserializationFormats;
315
    }
316
317
    /**
318
     * @param TypeArray $type
319
     */
320
    private function getSerializationFormat(array $type): string
321
    {
322
        return $type['params'][0] ?? $this->defaultSerializationFormat;
323
    }
324
325
    public function format(\DateInterval $dateInterval): string
326
    {
327
        $format = 'P';
328
329
        if (0 < $dateInterval->y) {
330
            $format .= $dateInterval->y . 'Y';
331
        }
332
333
        if (0 < $dateInterval->m) {
334
            $format .= $dateInterval->m . 'M';
335
        }
336
337
        if (0 < $dateInterval->d) {
338
            $format .= $dateInterval->d . 'D';
339
        }
340
341
        if (0 < $dateInterval->h || 0 < $dateInterval->i || 0 < $dateInterval->s) {
342
            $format .= 'T';
343
        }
344
345
        if (0 < $dateInterval->h) {
346
            $format .= $dateInterval->h . 'H';
347
        }
348
349
        if (0 < $dateInterval->i) {
350
            $format .= $dateInterval->i . 'M';
351
        }
352
353
        if (0 < $dateInterval->s) {
354
            $format .= $dateInterval->s . 'S';
355
        }
356
357
        if ('P' === $format) {
358
            $format = 'P0DT0S';
359
        }
360
361
        return $format;
362
    }
363
}
364