Failed Conditions
Push — master ( 28d8ad...89ab07 )
by Arnold
03:34
created

DateExtension::getDateFormatter()   D

Complexity

Conditions 10
Paths 16

Size

Total Lines 25
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 10

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 25
ccs 12
cts 12
cp 1
rs 4.8196
c 1
b 0
f 0
cc 10
eloc 16
nc 16
nop 3
crap 10

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Jasny\Twig;
4
5
/**
6
 * Format a date based on the current locale in Twig
7
 */
8
class DateExtension extends \Twig_Extension
9
{
10
    /**
11
     * Class constructor
12
     */
13 48
    public function __construct()
14
    {
15 48
        if (!extension_loaded('intl')) {
16
            throw new \Exception("The Date Twig extension requires the 'intl' PHP extension."); // @codeCoverageIgnore
17
        }
18 48
    }
19
20
    
21
    /**
22
     * Return extension name
23
     * 
24
     * @return string
25
     */
26 48
    public function getName()
27
    {
28 48
        return 'jasny/date';
29
    }
30
31
    /**
32
     * Callback for Twig to get all the filters.
33
     * 
34
     * @return \Twig_Filter[]
35
     */
36 34
    public function getFilters()
37
    {
38
        return [
39 34
            new \Twig_SimpleFilter('localdate', [$this, 'localDate']),
40 34
            new \Twig_SimpleFilter('localtime', [$this, 'localTime']),
41 34
            new \Twig_SimpleFilter('localdatetime', [$this, 'localDateTime']),
42 34
            new \Twig_SimpleFilter('duration', [$this, 'duration']),
43 34
            new \Twig_SimpleFilter('age', [$this, 'age']),
44
        ];
45
    }
46
47
    /**
48
     * Turn a value into a DateTime object
49
     * 
50
     * @param string|int|\DateTime $date
51
     * @return \DateTime
52
     */
53 30
    protected function valueToDateTime($date)
54
    {
55 30
        if (!$date instanceof \DateTime) {
56 30
            $date = is_int($date) ? \DateTime::createFromFormat('U', $date) : new \DateTime((string)$date);
57
        }
58
        
59 30
        return $date;
60
    }
61
    
62
    /**
63
     * Get configured intl date formatter.
64
     * 
65
     * @param string|null $dateFormat
66
     * @param string|null $timeFormat
67
     * @param string      $calendar
68
     * @return \IntlDateFormatter
69
     */
70 28
    protected function getDateFormatter($dateFormat, $timeFormat, $calendar)
71
    {
72 28
        $datetype = isset($dateFormat) ? $this->getFormat($dateFormat) : null;
73 28
        $timetype = isset($timeFormat) ? $this->getFormat($timeFormat) : null;
74
        
75 28
        $pattern = null;
76
        
77 28
        if ($datetype === null || $timetype === null) {
78 12
            $pattern = $this->getDatePattern(
79 12
                isset($datetype) ? $datetype : ($dateFormat ?: \IntlDateFormatter::SHORT),
80 12
                isset($timetype) ? $timetype : ($timeFormat ?: \IntlDateFormatter::SHORT)
81
            );
82
        }
83
        
84 28
        $calendarConst = $calendar === 'traditional' ? \IntlDateFormatter::TRADITIONAL : \IntlDateFormatter::GREGORIAN;
85
        
86 28
        return new \IntlDateFormatter(
87 28
            \Locale::getDefault(),
88
            $datetype,
89
            $timetype,
90 28
            null,
91
            $calendarConst,
92
            $pattern
93
        );
94
    }
95
    
96
    /**
97
     * Format the date/time value as a string based on the current locale
98
     * 
99
     * @param string|false $format  'short', 'medium', 'long', 'full', 'none' or false
100
     * @return int|null
101
     */
102 28
    protected function getFormat($format)
103
    {
104 28
        if ($format === false) {
105 22
            $format = 'none';
106
        }
107
        
108
        $types = [
109 28
            'none' => \IntlDateFormatter::NONE,
110
            'short' => \IntlDateFormatter::SHORT,
111
            'medium' => \IntlDateFormatter::MEDIUM,
112
            'long' => \IntlDateFormatter::LONG,
113
            'full' => \IntlDateFormatter::FULL
114
        ];
115
        
116 28
        return isset($types[$format]) ? $types[$format] : null;
117
    }
118
    
119
    /**
120
     * Default date pattern is short date pattern with 4 digit year
121
     * 
122
     * @param int|string $datetype
123
     * @param int|string $timetype
124
     * @param int        $calendar
125
     * @return string
126
     */
127 12
    protected function getDatePattern($datetype, $timetype, $calendar = \IntlDateFormatter::GREGORIAN)
128
    {
129
        if (
130 12
            (is_int($datetype) && $datetype !== \IntlDateFormatter::NONE) ||
131 12
            (is_int($timetype) && $timetype !== \IntlDateFormatter::NONE)
132
        ){
133 6
            $pattern = \IntlDateFormatter::create(
134 6
                \Locale::getDefault(),
135 6
                is_int($datetype) ? $datetype : \IntlDateFormatter::NONE,
136 6
                is_int($timetype) ? $timetype : \IntlDateFormatter::NONE,
137 6
                \IntlTimeZone::getGMT(),
138
                $calendar
139 6
            )->getPattern();
140
        } else {
141 6
            $pattern = null;
142
        }
143
        
144 12
        if (is_string($datetype)) {
145 6
            $pattern = trim($datetype . ' ' . $pattern);
146
        }
147
148 12
        if (is_string($timetype)) {
149 2
            $pattern = trim($pattern . ' ' . $timetype);
150
        }
151
        
152 12
        return preg_replace('/\byy?\b/', 'yyyy', $pattern);
153
    }
154
155
    /**
156
     * Format the date and/or time value as a string based on the current locale
157
     * 
158
     * @param \DateTime|int|string $value
159
     * @param string               $dateFormat  null, 'short', 'medium', 'long', 'full' or pattern
160
     * @param string               $timeFormat  null, 'short', 'medium', 'long', 'full' or pattern
161
     * @param string               $calendar    'gregorian' or 'traditional'
162
     * @return string
163
     */
164 31
    protected function formatLocal($value, $dateFormat, $timeFormat, $calendar = 'gregorian')
165
    {
166 31
        if (!isset($value)) {
167 3
            return null;
168
        }
169
        
170 28
        $date = $this->valueToDateTime($value);
171 28
        $formatter = $this->getDateFormatter($dateFormat, $timeFormat, $calendar);
172
        
173 28
        return $formatter->format($date->getTimestamp());
174
    }
175
176
    /**
177
     * Format the date value as a string based on the current locale
178
     * 
179
     * @param DateTime|int|string $date
180
     * @param string              $format    null, 'short', 'medium', 'long', 'full' or pattern
181
     * @param string              $calendar  'gregorian' or 'traditional'
182
     * @return string
183
     */
184 11
    public function localDate($date, $format = null, $calendar = 'gregorian')
185
    {
186 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...
187
    }
188
    
189
    /**
190
     * Format the time value as a string based on the current locale
191
     * 
192
     * @param DateTime|int|string $date
193
     * @param string              $format    'short', 'medium', 'long', 'full' or pattern
194
     * @param string              $calendar  'gregorian' or 'traditional'
195
     * @return string
196
     */
197 11
    public function localTime($date, $format = 'short', $calendar = 'gregorian')
198
    {
199 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...
200
    }
201
202
    /**
203
     * Format the date/time value as a string based on the current locale
204
     * 
205
     * @param DateTime|int|string $date
206
     * @param string              $format    date format, pattern or ['date'=>format, 'time'=>format)
207
     * @param string              $calendar  'gregorian' or 'traditional'
208
     * @return string
209
     */
210 9
    public function localDateTime($date, $format = null, $calendar = 'gregorian')
211
    {
212 9
        if (is_array($format) || $format instanceof \stdClass || !isset($format)) {
213 6
            $formatDate = isset($format['date']) ? $format['date'] : null;
214 6
            $formatTime = isset($format['time']) ? $format['time'] : 'short';
215
        } else {
216 3
            $formatDate = $format;
217 3
            $formatTime = false;
218
        }
219
        
220 9
        return $this->formatLocal($date, $formatDate, $formatTime, $calendar);
221
    }
222
    
223
224
    /**
225
     * Split duration into seconds, minutes, hours, days, weeks and years.
226
     * 
227
     * @param int $seconds
228
     * @return array
229
     */
230 13
    protected function splitDuration($seconds, $max)
231
    {
232 13
        if ($max < 1 || $seconds < 60) {
233 1
            return [$seconds];
234
        }
235
        
236 12
        $minutes = floor($seconds / 60);
237 12
        $seconds = $seconds % 60;
238 12
        if ($max < 2 || $minutes < 60) {
239 2
            return [$seconds, $minutes];
240
        }
241
        
242 10
        $hours = floor($minutes / 60);
243 10
        $minutes = $minutes % 60;
244 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...
245 4
            return [$seconds, $minutes, $hours];
246
        }
247
        
248 6
        $days = floor($hours / 24);
249 6
        $hours = $hours % 24;
250 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...
251 2
            return [$seconds, $minutes, $hours, $days]; 
252
        }
253
        
254 4
        $weeks = floor($days / 7);
255 4
        $days = $days % 7;
256 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...
257 2
            return [$seconds, $minutes, $hours, $days, $weeks];
258
        }
259
        
260 2
        $years = floor($weeks / 52);
261 2
        $weeks = $weeks % 52;
262 2
        return [$seconds, $minutes, $hours, $days, $weeks, $years];
263
    }
264
    
265
    /**
266
     * Calculate duration from seconds.
267
     * One year is seen as exactly 52 weeks.
268
     * 
269
     * Use null to skip a unit.
270
     * 
271
     * @param int    $value     Time in seconds
272
     * @param array  $units     Time units (seconds, minutes, hours, days, weeks, years)
273
     * @param string $seperator
274
     * @return string
275
     */
276 14
    public function duration($value, $units = ['s', 'm', 'h', 'd', 'w', 'y'], $seperator = ' ')
277
    {
278 14
        if (!isset($value)) {
279 1
            return null;
280
        }
281
        
282
        list($seconds, $minutes, $hours, $days, $weeks, $years) =
283 13
            $this->splitDuration($value, count($units) - 1) + array_fill(0, 6, null);
284
        
285 13
        $duration = '';
286 13 View Code Duplication
        if (isset($years) && isset($units[5])) {
1 ignored issue
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...
287 2
            $duration .= $seperator . $years . $units[5];
288
        }
289
        
290 13 View Code Duplication
        if (isset($weeks) && isset($units[4])) {
1 ignored issue
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...
291 3
            $duration .= $seperator . $weeks . $units[4];
292
        }
293
        
294 13 View Code Duplication
        if (isset($days) && isset($units[3])) {
1 ignored issue
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...
295 4
            $duration .= $seperator . $days . $units[3];
296
        }
297
        
298 13 View Code Duplication
        if (isset($hours) && isset($units[2])) {
1 ignored issue
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...
299 7
            $duration .= $seperator . $hours . $units[2];
300
        }
301
        
302 13 View Code Duplication
        if (isset($minutes) && isset($units[1])) {
1 ignored issue
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...
303 8
            $duration .= $seperator . $minutes . $units[1];
304
        }
305
        
306 13 View Code Duplication
        if (isset($seconds) && isset($units[0])) {
1 ignored issue
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...
307 6
            $duration .= $seperator . $seconds . $units[0];
308
        }
309
        
310 13
        return trim($duration, $seperator);
311
    }
312
313
    /**
314
     * Get the age (in years) based on a date.
315
     * 
316
     * @param DateTime|string $value
317
     * @return int
318
     */
319 3
    public function age($value)
320
    {
321 3
        if (!isset($value)) {
322 1
            return null;
323
        }
324
        
325 2
        $date = $this->valueToDateTime($value);
326
        
327 2
        return $date->diff(new \DateTime())->format('%y');
328
    }
329
}
330