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
|
|||||||
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
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
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||
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
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
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||
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
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
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||
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
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
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||
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
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
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() 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
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||
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
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
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||
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
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
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||
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
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
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() 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
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||
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
|
|||||||
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 |
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:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths