Completed
Pull Request — master (#829)
by Kevin
09:01
created

DateHandler::parseDateTime()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4.0119

Importance

Changes 0
Metric Value
dl 0
loc 19
c 0
b 0
f 0
ccs 10
cts 11
cp 0.9091
rs 9.2
cc 4
eloc 11
nc 8
nop 3
crap 4.0119
1
<?php
2
3
/*
4
 * Copyright 2016 Johannes M. Schmitt <[email protected]>
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
namespace JMS\Serializer\Handler;
20
21
use JMS\Serializer\Context;
22
use JMS\Serializer\Exception\RuntimeException;
23
use JMS\Serializer\GraphNavigator;
24
use JMS\Serializer\JsonDeserializationVisitor;
25
use JMS\Serializer\VisitorInterface;
26
use JMS\Serializer\XmlDeserializationVisitor;
27
use JMS\Serializer\XmlSerializationVisitor;
28
29
class DateHandler implements SubscribingHandlerInterface
30
{
31
    private $defaultFormat;
32
    private $defaultTimezone;
33
    private $xmlCData;
34
35 384
    public static function getSubscribingMethods()
36
    {
37 384
        $methods = array();
38 384
        $deserializationTypes = array('DateTime', 'DateTimeImmutable', 'DateInterval');
39 384
        $serialisationTypes = array('DateTime', 'DateTimeImmutable', 'DateInterval');
40
41 384
        foreach (array('json', 'xml', 'yml') as $format) {
42
43 384
            foreach ($deserializationTypes as $type) {
44 384
                $methods[] = [
45 384
                    'type' => $type,
46 384
                    'direction' => GraphNavigator::DIRECTION_DESERIALIZATION,
47 384
                    'format' => $format,
48
                ];
49 384
            }
50
51 384
            foreach ($serialisationTypes as $type) {
52 384
                $methods[] = array(
53 384
                    'type' => $type,
54 384
                    'format' => $format,
55 384
                    'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
56 384
                    'method' => 'serialize' . $type,
57
                );
58 384
            }
59 384
        }
60
61 384
        return $methods;
62
    }
63
64 391
    public function __construct($defaultFormat = \DateTime::ISO8601, $defaultTimezone = 'UTC', $xmlCData = true)
65
    {
66 391
        $this->defaultFormat = $defaultFormat;
67 391
        $this->defaultTimezone = new \DateTimeZone($defaultTimezone);
68 391
        $this->xmlCData = $xmlCData;
69 391
    }
70
71 30
    private function serializeDateTimeInterface(
72
        VisitorInterface $visitor,
73
        \DateTimeInterface $date,
74
        array $type,
75
        Context $context
76
    )
77
    {
78 30
        if ($visitor instanceof XmlSerializationVisitor && false === $this->xmlCData) {
79 2
            return $visitor->visitSimpleString($date->format($this->getFormat($type)), $type, $context);
80
        }
81
82 28
        $format = $this->getFormat($type);
83 28
        if ('U' === $format) {
84 3
            return $visitor->visitInteger($date->format($format), $type, $context);
85
        }
86
87 25
        return $visitor->visitString($date->format($this->getFormat($type)), $type, $context);
88
    }
89
90 24
    public function serializeDateTime(VisitorInterface $visitor, \DateTime $date, array $type, Context $context)
91
    {
92 24
        return $this->serializeDateTimeInterface($visitor, $date, $type, $context);
93
    }
94
95 6
    public function serializeDateTimeImmutable(
96
        VisitorInterface $visitor,
97
        \DateTimeImmutable $date,
98
        array $type,
99
        Context $context
100
    )
101
    {
102 6
        return $this->serializeDateTimeInterface($visitor, $date, $type, $context);
103
    }
104
105 3
    public function serializeDateInterval(VisitorInterface $visitor, \DateInterval $date, array $type, Context $context)
106
    {
107 3
        $iso8601DateIntervalString = $this->format($date);
108
109 3
        if ($visitor instanceof XmlSerializationVisitor && false === $this->xmlCData) {
110
            return $visitor->visitSimpleString($iso8601DateIntervalString, $type, $context);
111
        }
112
113 3
        return $visitor->visitString($iso8601DateIntervalString, $type, $context);
114
    }
115
116 7
    private function isDataXmlNull($data)
117
    {
118 7
        $attributes = $data->attributes('xsi', true);
119 7
        return isset($attributes['nil'][0]) && (string)$attributes['nil'][0] === 'true';
120
    }
121
122 5
    public function deserializeDateTimeFromXml(XmlDeserializationVisitor $visitor, $data, array $type)
0 ignored issues
show
Unused Code introduced by
The parameter $visitor is not used and could be removed.

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

Loading history...
123
    {
124 5
        if ($this->isDataXmlNull($data)) {
125
            return null;
126
        }
127
128 5
        return $this->parseDateTime($data, $type);
129
    }
130
131 1
    public function deserializeDateTimeImmutableFromXml(XmlDeserializationVisitor $visitor, $data, array $type)
0 ignored issues
show
Unused Code introduced by
The parameter $visitor is not used and could be removed.

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

Loading history...
132
    {
133 1
        if ($this->isDataXmlNull($data)) {
134
            return null;
135
        }
136
137 1
        return $this->parseDateTime($data, $type, true);
138
    }
139
140 1
    public function deserializeDateIntervalFromXml(XmlDeserializationVisitor $visitor, $data, array $type)
0 ignored issues
show
Unused Code introduced by
The parameter $visitor is not used and could be removed.

This check looks from 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.

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

Loading history...
141
    {
142 1
        if ($this->isDataXmlNull($data)) {
143
            return null;
144
        }
145
146 1
        return $this->parseDateInterval($data);
147
    }
148
149 10
    public function deserializeDateTimeFromJson(JsonDeserializationVisitor $visitor, $data, array $type)
0 ignored issues
show
Unused Code introduced by
The parameter $visitor is not used and could be removed.

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

Loading history...
150
    {
151 10
        if (null === $data) {
152 1
            return null;
153
        }
154
155 9
        return $this->parseDateTime($data, $type);
156
    }
157
158 2
    public function deserializeDateTimeImmutableFromJson(JsonDeserializationVisitor $visitor, $data, array $type)
0 ignored issues
show
Unused Code introduced by
The parameter $visitor is not used and could be removed.

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

Loading history...
159
    {
160 2
        if (null === $data) {
161
            return null;
162
        }
163
164 2
        return $this->parseDateTime($data, $type, true);
165
    }
166
167 1
    public function deserializeDateIntervalFromJson(JsonDeserializationVisitor $visitor, $data, array $type)
0 ignored issues
show
Unused Code introduced by
The parameter $visitor is not used and could be removed.

This check looks from 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.

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

Loading history...
168
    {
169 1
        if (null === $data) {
170
            return null;
171
        }
172
173 1
        return $this->parseDateInterval($data);
174
    }
175
176 17
    private function parseDateTime($data, array $type, $immutable = false)
177
    {
178 17
        $timezone = !empty($type['params'][1]) ? new \DateTimeZone($type['params'][1]) : $this->defaultTimezone;
179 17
        $format = $this->getDeserializationFormat($type);
180
181 17
        if ($immutable) {
182 3
            $datetime = \DateTimeImmutable::createFromFormat($format, (string)$data, $timezone);
183 3
        } else {
184 14
            $datetime = \DateTime::createFromFormat($format, (string)$data, $timezone);
185
        }
186
187 17
        if (false === $datetime) {
188
            throw new RuntimeException(sprintf('Invalid datetime "%s", expected format %s.', $data, $format));
189
        }
190
191 17
        $datetime->setTimezone($timezone);
192
193 17
        return $datetime;
194
    }
195
196 2
    private function parseDateInterval($data)
197
    {
198 2
        $dateInterval = null;
199
        try {
200 2
            $dateInterval = new \DateInterval($data);
201 2
        } catch (\Exception $e) {
202
            throw new RuntimeException(sprintf('Invalid dateinterval "%s", expected ISO 8601 format', $data), null, $e);
203
        }
204
205 2
        return $dateInterval;
206
    }
207
208
    /**
209
     * @param array $type
210
     *  @return string
211
     */
212 17
    private function getDeserializationFormat(array $type)
213
    {
214 17
        if (isset($type['params'][2])) {
215 2
            return $type['params'][2];
216
        }
217 16
        if (isset($type['params'][0])) {
218 8
            return $type['params'][0];
219
        }
220 10
        return $this->defaultFormat;
221
    }
222
223
    /**
224
     * @return string
225
     * @param array $type
226
     */
227 30
    private function getFormat(array $type)
228
    {
229 30
        return isset($type['params'][0]) ? $type['params'][0] : $this->defaultFormat;
230
    }
231
232
    /**
233
     * @param \DateInterval $dateInterval
234
     * @return string
235
     */
236 4
    public function format(\DateInterval $dateInterval)
237
    {
238 4
        $format = 'P';
239
240 4
        if (0 < $dateInterval->y) {
241 1
            $format .= $dateInterval->y . 'Y';
242 1
        }
243
244 4
        if (0 < $dateInterval->m) {
245
            $format .= $dateInterval->m . 'M';
246
        }
247
248 4
        if (0 < $dateInterval->d) {
249 1
            $format .= $dateInterval->d . 'D';
250 1
        }
251
252 4
        if (0 < $dateInterval->h || 0 < $dateInterval->i || 0 < $dateInterval->s) {
253 4
            $format .= 'T';
254 4
        }
255
256 4
        if (0 < $dateInterval->h) {
257 1
            $format .= $dateInterval->h . 'H';
258 1
        }
259
260 4
        if (0 < $dateInterval->i) {
261 4
            $format .= $dateInterval->i . 'M';
262 4
        }
263
264 4
        if (0 < $dateInterval->s) {
265 1
            $format .= $dateInterval->s . 'S';
266 1
        }
267
268 4
        if ($format === 'P') {
269 1
            $format = 'P0DT0S';
270 1
        }
271
272 4
        return $format;
273
    }
274
}
275