Failed Conditions
Push — master ( 174394...bfbf85 )
by Arnold
14s queued 12s
created

DateExtension::getName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2
1
<?php
2
3
namespace Jasny\Twig;
4
5
use Twig\Extension\AbstractExtension;
6
use Twig\TwigFilter;
7
8
/**
9
 * Format a date based on the current locale in Twig
10
 */
11
class DateExtension extends AbstractExtension
12
{
13
    /**
14
     * Class constructor
15
     */
16 48
    public function __construct()
17
    {
18 48
        if (!extension_loaded('intl')) {
19
            throw new \Exception("The Date Twig extension requires the 'intl' PHP extension."); // @codeCoverageIgnore
20
        }
21 48
    }
22
23
24
    /**
25
     * Return extension name
26
     *
27
     * @return string
28
     */
29
    public function getName()
30
    {
31
        return 'jasny/date';
32
    }
33
34
    /**
35
     * Callback for Twig to get all the filters.
36
     *
37
     * @return \Twig\TwigFilter[]
38
     */
39 34
    public function getFilters()
40
    {
41
        return [
42 34
            new TwigFilter('localdate', [$this, 'localDate']),
43 34
            new TwigFilter('localtime', [$this, 'localTime']),
44 34
            new TwigFilter('localdatetime', [$this, 'localDateTime']),
45 34
            new TwigFilter('duration', [$this, 'duration']),
46 34
            new TwigFilter('age', [$this, 'age']),
47
        ];
48
    }
49
50
    /**
51
     * Turn a value into a DateTime object
52
     *
53
     * @param string|int|\DateTime $date
54
     * @return \DateTime
55
     */
56 30
    protected function valueToDateTime($date)
57
    {
58 30
        if (!$date instanceof \DateTime) {
59 30
            $date = is_int($date) ? \DateTime::createFromFormat('U', $date) : new \DateTime((string)$date);
60
        }
61
62 30
        return $date;
63
    }
64
65
    /**
66
     * Get configured intl date formatter.
67
     *
68
     * @param string|null $dateFormat
69
     * @param string|null $timeFormat
70
     * @param string      $calendar
71
     * @return \IntlDateFormatter
72
     */
73 28
    protected function getDateFormatter($dateFormat, $timeFormat, $calendar)
74
    {
75 28
        $datetype = isset($dateFormat) ? $this->getFormat($dateFormat) : null;
76 28
        $timetype = isset($timeFormat) ? $this->getFormat($timeFormat) : null;
77
78 28
        $calendarConst = $calendar === 'traditional' ? \IntlDateFormatter::TRADITIONAL : \IntlDateFormatter::GREGORIAN;
79
80 28
        $pattern = $this->getDateTimePattern(
81 28
            isset($datetype) ? $datetype : $dateFormat,
82 28
            isset($timetype) ? $timetype : $timeFormat,
83
            $calendarConst
84
        );
85
86 28
        return new \IntlDateFormatter(\Locale::getDefault(), $datetype, $timetype, null, $calendarConst, $pattern);
87
    }
88
89
    /**
90
     * Format the date/time value as a string based on the current locale
91
     *
92
     * @param string|false $format  'short', 'medium', 'long', 'full', 'none' or false
93
     * @return int|null
94
     */
95 28
    protected function getFormat($format)
96
    {
97 28
        if ($format === false) {
98 22
            $format = 'none';
99
        }
100
101
        $types = [
102 28
            'none' => \IntlDateFormatter::NONE,
103
            'short' => \IntlDateFormatter::SHORT,
104
            'medium' => \IntlDateFormatter::MEDIUM,
105
            'long' => \IntlDateFormatter::LONG,
106
            'full' => \IntlDateFormatter::FULL
107
        ];
108
109 28
        return isset($types[$format]) ? $types[$format] : null;
110
    }
111
112
    /**
113
     * Get the date/time pattern.
114
     *
115
     * @param int|string $datetype
116
     * @param int|string $timetype
117
     * @param int        $calendar
118
     * @return string
119
     */
120 28
    protected function getDateTimePattern($datetype, $timetype, $calendar = \IntlDateFormatter::GREGORIAN)
121
    {
122 28
        if (is_int($datetype) && is_int($timetype)) {
123 16
            return null;
124
        }
125
126 12
        return $this->getDatePattern(
127 12
            isset($datetype) ? $datetype : \IntlDateFormatter::SHORT,
128 12
            isset($timetype) ? $timetype : \IntlDateFormatter::SHORT,
129
            $calendar
130
        );
131
    }
132
133
    /**
134
     * Get the formatter to create a date and/or time pattern
135
     *
136
     * @param int|string $datetype
137
     * @param int|string $timetype
138
     * @param int        $calendar
139
     * @return \IntlDateFormatter
140
     */
141 6
    protected function getDatePatternFormatter($datetype, $timetype, $calendar = \IntlDateFormatter::GREGORIAN)
142
    {
143 6
        return \IntlDateFormatter::create(
144 6
            \Locale::getDefault(),
145 6
            is_int($datetype) ? $datetype : \IntlDateFormatter::NONE,
146 6
            is_int($timetype) ? $timetype : \IntlDateFormatter::NONE,
147 6
            \IntlTimeZone::getGMT(),
148 6
            $calendar
149
        );
150
    }
151
152
    /**
153
     * Get the date and/or time pattern
154
     * Default date pattern is short date pattern with 4 digit year.
155
     *
156
     * @param int|string $datetype
157
     * @param int|string $timetype
158
     * @param int        $calendar
159
     * @return string
160
     */
161 12
    protected function getDatePattern($datetype, $timetype, $calendar = \IntlDateFormatter::GREGORIAN)
162
    {
163
        $createPattern =
164 12
            (is_int($datetype) && $datetype !== \IntlDateFormatter::NONE) ||
165 12
            (is_int($timetype) && $timetype !== \IntlDateFormatter::NONE);
166
167 12
        $pattern = $createPattern ? $this->getDatePatternFormatter($datetype, $timetype, $calendar)->getPattern() : '';
168
169 12
        return trim(
170 12
            (is_string($datetype) ? $datetype . ' ' : '') .
171 12
            preg_replace('/\byy?\b/', 'yyyy', $pattern) .
172 12
            (is_string($timetype) ? ' ' . $timetype : '')
173
        );
174
    }
175
176
    /**
177
     * Format the date and/or time value as a string based on the current locale
178
     *
179
     * @param \DateTime|int|string $value
180
     * @param string               $dateFormat  null, 'short', 'medium', 'long', 'full' or pattern
181
     * @param string               $timeFormat  null, 'short', 'medium', 'long', 'full' or pattern
182
     * @param string               $calendar    'gregorian' or 'traditional'
183
     * @return string
184
     */
185 31
    protected function formatLocal($value, $dateFormat, $timeFormat, $calendar = 'gregorian')
186
    {
187 31
        if (!isset($value)) {
188 3
            return null;
189
        }
190
191 28
        $date = $this->valueToDateTime($value);
192 28
        $formatter = $this->getDateFormatter($dateFormat, $timeFormat, $calendar);
193
194 28
        return $formatter->format($date->getTimestamp());
195
    }
196
197
    /**
198
     * Format the date value as a string based on the current locale
199
     *
200
     * @param DateTime|int|string $date
201
     * @param string              $format    null, 'short', 'medium', 'long', 'full' or pattern
202
     * @param string              $calendar  'gregorian' or 'traditional'
203
     * @return string
204
     */
205 11
    public function localDate($date, $format = null, $calendar = 'gregorian')
206
    {
207 11
        return $this->formatLocal($date, $format, false, $calendar);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
208
    }
209
210
    /**
211
     * Format the time value as a string based on the current locale
212
     *
213
     * @param DateTime|int|string $date
214
     * @param string              $format    'short', 'medium', 'long', 'full' or pattern
215
     * @param string              $calendar  'gregorian' or 'traditional'
216
     * @return string
217
     */
218 11
    public function localTime($date, $format = 'short', $calendar = 'gregorian')
219
    {
220 11
        return $this->formatLocal($date, false, $format, $calendar);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
221
    }
222
223
    /**
224
     * Format the date/time value as a string based on the current locale
225
     *
226
     * @param DateTime|int|string $date
227
     * @param string              $format    date format, pattern or ['date'=>format, 'time'=>format)
228
     * @param string              $calendar  'gregorian' or 'traditional'
229
     * @return string
230
     */
231 9
    public function localDateTime($date, $format = null, $calendar = 'gregorian')
232
    {
233 9
        if (is_array($format) || $format instanceof \stdClass || !isset($format)) {
234 6
            $formatDate = isset($format['date']) ? $format['date'] : null;
235 6
            $formatTime = isset($format['time']) ? $format['time'] : 'short';
236
        } else {
237 3
            $formatDate = $format;
238 3
            $formatTime = false;
239
        }
240
241 9
        return $this->formatLocal($date, $formatDate, $formatTime, $calendar);
242
    }
243
244
245
    /**
246
     * Split duration into seconds, minutes, hours, days, weeks and years.
247
     *
248
     * @param int $seconds
249
     * @return array
250
     */
251 13
    protected function splitDuration($seconds, $max)
252
    {
253 13
        if ($max < 1 || $seconds < 60) {
254 1
            return [$seconds];
255
        }
256
257 12
        $minutes = floor($seconds / 60);
258 12
        $seconds = $seconds % 60;
259 12
        if ($max < 2 || $minutes < 60) {
260 2
            return [$seconds, $minutes];
261
        }
262
263 10
        $hours = floor($minutes / 60);
264 10
        $minutes = $minutes % 60;
265 10 View Code Duplication
        if ($max < 3 || $hours < 24) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
266 4
            return [$seconds, $minutes, $hours];
267
        }
268
269 6
        $days = floor($hours / 24);
270 6
        $hours = $hours % 24;
271 6 View Code Duplication
        if ($max < 4 || $days < 7) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
272 2
            return [$seconds, $minutes, $hours, $days];
273
        }
274
275 4
        $weeks = floor($days / 7);
276 4
        $days = $days % 7;
277 4 View Code Duplication
        if ($max < 5 || $weeks < 52) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
278 2
            return [$seconds, $minutes, $hours, $days, $weeks];
279
        }
280
281 2
        $years = floor($weeks / 52);
282 2
        $weeks = $weeks % 52;
283 2
        return [$seconds, $minutes, $hours, $days, $weeks, $years];
284
    }
285
286
    /**
287
     * Calculate duration from seconds.
288
     * One year is seen as exactly 52 weeks.
289
     *
290
     * Use null to skip a unit.
291
     *
292
     * @param int    $value     Time in seconds
293
     * @param array  $units     Time units (seconds, minutes, hours, days, weeks, years)
294
     * @param string $separator
295
     * @return string
296
     */
297 14
    public function duration($value, $units = ['s', 'm', 'h', 'd', 'w', 'y'], $separator = ' ')
298
    {
299 14
        if (!isset($value)) {
300 1
            return null;
301
        }
302
303
        list($seconds, $minutes, $hours, $days, $weeks, $years) =
304 13
            $this->splitDuration($value, count($units) - 1) + array_fill(0, 6, null);
305
306 13
        $duration = '';
307 13 View Code Duplication
        if (isset($years) && isset($units[5])) {
308 2
            $duration .= $separator . $years . $units[5];
309
        }
310
311 13 View Code Duplication
        if (isset($weeks) && isset($units[4])) {
312 3
            $duration .= $separator . $weeks . $units[4];
313
        }
314
315 13 View Code Duplication
        if (isset($days) && isset($units[3])) {
316 4
            $duration .= $separator . $days . $units[3];
317
        }
318
319 13 View Code Duplication
        if (isset($hours) && isset($units[2])) {
320 7
            $duration .= $separator . $hours . $units[2];
321
        }
322
323 13 View Code Duplication
        if (isset($minutes) && isset($units[1])) {
324 8
            $duration .= $separator . $minutes . $units[1];
325
        }
326
327 13 View Code Duplication
        if (isset($seconds) && isset($units[0])) {
328 6
            $duration .= $separator . $seconds . $units[0];
329
        }
330
331 13
        return trim($duration, $separator);
332
    }
333
334
    /**
335
     * Get the age (in years) based on a date.
336
     *
337
     * @param DateTime|string $value
338
     * @return int
339
     */
340 3
    public function age($value)
341
    {
342 3
        if (!isset($value)) {
343 1
            return null;
344
        }
345
346 2
        $date = $this->valueToDateTime($value);
347
348 2
        return $date->diff(new \DateTime())->format('%y');
349
    }
350
}
351