Test Setup Failed
Pull Request — master (#19)
by
unknown
07:19
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 0
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 48
    /**
14
     * Class constructor
15 48
     */
16
    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
    }
22
23
24
    /**
25
     * Return extension name
26 48
     *
27
     * @return string
28 48
     */
29
    public function getName()
30
    {
31
        return 'jasny/date';
32
    }
33
34
    /**
35
     * Callback for Twig to get all the filters.
36 34
     *
37
     * @return \Twig\TwigFilter[]
38
     */
39 34
    public function getFilters()
40 34
    {
41 34
        return [
42 34
            new TwigFilter('localdate', [$this, 'localDate']),
43 34
            new TwigFilter('localtime', [$this, 'localTime']),
44 34
            new TwigFilter('localdatetime', [$this, 'localDateTime']),
45
            new TwigFilter('duration', [$this, 'duration']),
46
            new TwigFilter('age', [$this, 'age']),
47
        ];
48
    }
49
50
    /**
51
     * Turn a value into a DateTime object
52
     *
53 30
     * @param string|int|\DateTime $date
54
     * @return \DateTime
55 30
     */
56 30
    protected function valueToDateTime($date)
57 30
    {
58
        if (!$date instanceof \DateTime) {
59 30
            $date = is_int($date) ? \DateTime::createFromFormat('U', $date) : new \DateTime((string)$date);
60
        }
61
62
        return $date;
63
    }
64
65
    /**
66
     * Get configured intl date formatter.
67
     *
68
     * @param string|null $dateFormat
69
     * @param string|null $timeFormat
70 28
     * @param string      $calendar
71
     * @return \IntlDateFormatter
72 28
     */
73 28
    protected function getDateFormatter($dateFormat, $timeFormat, $calendar)
74
    {
75 28
        $datetype = isset($dateFormat) ? $this->getFormat($dateFormat) : null;
76
        $timetype = isset($timeFormat) ? $this->getFormat($timeFormat) : null;
77 28
78 28
        $calendarConst = $calendar === 'traditional' ? \IntlDateFormatter::TRADITIONAL : \IntlDateFormatter::GREGORIAN;
79 28
80
        $pattern = $this->getDateTimePattern(
81 28
            isset($datetype) ? $datetype : $dateFormat,
82
            isset($timetype) ? $timetype : $timeFormat,
83 28
            $calendarConst
84
        );
85
86
        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 28
     * @param string|false $format  'short', 'medium', 'long', 'full', 'none' or false
93
     * @return int|null
94 28
     */
95 22
    protected function getFormat($format)
96 22
    {
97
        if ($format === false) {
98
            $format = 'none';
99 28
        }
100 28
101 28
        $types = [
102 28
            'none' => \IntlDateFormatter::NONE,
103
            'short' => \IntlDateFormatter::SHORT,
104 28
            'medium' => \IntlDateFormatter::MEDIUM,
105
            'long' => \IntlDateFormatter::LONG,
106 28
            'full' => \IntlDateFormatter::FULL
107
        ];
108
109
        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 28
     * @param int        $calendar
118
     * @return string
119 28
     */
120 16
    protected function getDateTimePattern($datetype, $timetype, $calendar = \IntlDateFormatter::GREGORIAN)
121
    {
122
        if (is_int($datetype) && is_int($timetype)) {
123 12
            return null;
124 12
        }
125 12
126
        return $this->getDatePattern(
127 12
            isset($datetype) ? $datetype : \IntlDateFormatter::SHORT,
128
            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 6
     * @param int        $calendar
139
     * @return \IntlDateFormatter
140 6
     */
141 6
    protected function getDatePatternFormatter($datetype, $timetype, $calendar = \IntlDateFormatter::GREGORIAN)
142 6
    {
143 6
        return \IntlDateFormatter::create(
144 6
            \Locale::getDefault(),
145
            is_int($datetype) ? $datetype : \IntlDateFormatter::NONE,
146 6
            is_int($timetype) ? $timetype : \IntlDateFormatter::NONE,
147
            \IntlTimeZone::getGMT(),
148
            $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 12
     * @param int        $calendar
159
     * @return string
160
     */
161 12
    protected function getDatePattern($datetype, $timetype, $calendar = \IntlDateFormatter::GREGORIAN)
162 12
    {
163
        $createPattern =
164 12
            (is_int($datetype) && $datetype !== \IntlDateFormatter::NONE) ||
165
            (is_int($timetype) && $timetype !== \IntlDateFormatter::NONE);
166 12
167 12
        $pattern = $createPattern ? $this->getDatePatternFormatter($datetype, $timetype, $calendar)->getPattern() : '';
168 12
169 12
        return trim(
170 12
            (is_string($datetype) ? $datetype . ' ' : '') .
171
            preg_replace('/\byy?\b/', 'yyyy', $pattern) .
172
            (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 31
     * @param string               $calendar    'gregorian' or 'traditional'
183
     * @return string
184 31
     */
185 3
    protected function formatLocal($value, $dateFormat, $timeFormat, $calendar = 'gregorian')
186
    {
187
        if (!isset($value)) {
188 28
            return null;
189 28
        }
190
191 28
        $date = $this->valueToDateTime($value);
192
        $formatter = $this->getDateFormatter($dateFormat, $timeFormat, $calendar);
193
194
        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 11
     * @param string              $calendar  'gregorian' or 'traditional'
203
     * @return string
204 11
     */
205
    public function localDate($date, $format = null, $calendar = 'gregorian')
206
    {
207
        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 11
     * @param string              $calendar  'gregorian' or 'traditional'
216
     * @return string
217 11
     */
218
    public function localTime($date, $format = 'short', $calendar = 'gregorian')
219
    {
220
        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 9
     * @param string              $calendar  'gregorian' or 'traditional'
229
     * @return string
230 9
     */
231 6
    public function localDateTime($date, $format = null, $calendar = 'gregorian')
232 6
    {
233 6
        if (is_array($format) || $format instanceof \stdClass || !isset($format)) {
234 3
            $formatDate = isset($format['date']) ? $format['date'] : null;
235 3
            $formatTime = isset($format['time']) ? $format['time'] : 'short';
236
        } else {
237
            $formatDate = $format;
238 9
            $formatTime = false;
239
        }
240
241
        return $this->formatLocal($date, $formatDate, $formatTime, $calendar);
242
    }
243
244
245
    /**
246
     * Split duration into seconds, minutes, hours, days, weeks and years.
247
     *
248 13
     * @param int $seconds
249
     * @return array
250 13
     */
251 1
    protected function splitDuration($seconds, $max)
252
    {
253
        if ($max < 1 || $seconds < 60) {
254 12
            return [$seconds];
255 12
        }
256 12
257 2
        $minutes = floor($seconds / 60);
258
        $seconds = $seconds % 60;
259
        if ($max < 2 || $minutes < 60) {
260 10
            return [$seconds, $minutes];
261 10
        }
262 10
263 4
        $hours = floor($minutes / 60);
264
        $minutes = $minutes % 60;
265 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 6
            return [$seconds, $minutes, $hours];
267 6
        }
268 6
269 2
        $days = floor($hours / 24);
270
        $hours = $hours % 24;
271 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 4
            return [$seconds, $minutes, $hours, $days];
273 4
        }
274 4
275 2
        $weeks = floor($days / 7);
276
        $days = $days % 7;
277 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 2
        }
280 2
281
        $years = floor($weeks / 52);
282
        $weeks = $weeks % 52;
283
        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 14
     * @param string $separator
295
     * @return string
296 14
     */
297 1
    public function duration($value, $units = ['s', 'm', 'h', 'd', 'w', 'y'], $separator = ' ')
298
    {
299
        if (!isset($value)) {
300
            return null;
301 13
        }
302
303 13
        list($seconds, $minutes, $hours, $days, $weeks, $years) =
304 13
            $this->splitDuration($value, count($units) - 1) + array_fill(0, 6, null);
305 2
306 2
        $duration = '';
307 View Code Duplication
        if (isset($years) && isset($units[5])) {
308 13
            $duration .= $separator . $years . $units[5];
309 3
        }
310 3
311 View Code Duplication
        if (isset($weeks) && isset($units[4])) {
312 13
            $duration .= $separator . $weeks . $units[4];
313 4
        }
314 4
315 View Code Duplication
        if (isset($days) && isset($units[3])) {
316 13
            $duration .= $separator . $days . $units[3];
317 7
        }
318 7
319 View Code Duplication
        if (isset($hours) && isset($units[2])) {
320 13
            $duration .= $separator . $hours . $units[2];
321 8
        }
322 8
323 View Code Duplication
        if (isset($minutes) && isset($units[1])) {
324 13
            $duration .= $separator . $minutes . $units[1];
325 6
        }
326 6
327 View Code Duplication
        if (isset($seconds) && isset($units[0])) {
328 13
            $duration .= $separator . $seconds . $units[0];
329
        }
330
331
        return trim($duration, $separator);
332
    }
333
334
    /**
335
     * Get the age (in years) based on a date.
336
     *
337 3
     * @param DateTime|string $value
338
     * @return int
339 3
     */
340 1
    public function age($value)
341
    {
342
        if (!isset($value)) {
343 2
            return null;
344
        }
345 2
346
        $date = $this->valueToDateTime($value);
347
348
        return $date->diff(new \DateTime())->format('%y');
349
    }
350
}
351