Test Failed
Push — master ( dc68d1...1f5199 )
by Mathieu
02:31
created

DateHelper::formatTimeFromCase()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 26
Code Lines 16

Duplication

Lines 6
Ratio 23.08 %

Importance

Changes 0
Metric Value
dl 6
loc 26
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 16
nc 4
nop 1
1
<?php
2
3
namespace Charcoal\Cms\Support\Helpers;
4
5
// Psr-7 dependencies
6
use DateTime;
7
use Exception;
8
9
use \Charcoal\Translator\TranslatorAwareTrait;
10
11
/**
12
 * Class DateHelper
13
 */
14
class DateHelper
15
{
16
    use TranslatorAwareTrait;
17
18
    /**
19
     * @var DateTime $from
20
     */
21
    protected $from;
22
23
    /**
24
     * @var DateTime $to
25
     */
26
    protected $to;
27
28
    /**
29
     * @var array $dateFormats The date formats options from config.
30
     */
31
    protected $dateFormats;
32
33
    /**
34
     * @var array $timeFormats The time formats options from config.
35
     */
36
    protected $timeFormats;
37
38
    /**
39
     * @var string $dateFormat The format from dateFormats to use for the date
40
     */
41
    protected $dateFormat;
42
43
    /**
44
     * @var string $dateFormat The format from dateFormats to use for the time
45
     */
46
    protected $timeFormat;
47
48
    /**
49
     * DateHelper constructor.
50
     * @param array $data DateHelper data.
51
     * @throws Exception When constructor's data missing.
52
     */
53
    public function __construct(array $data)
54
    {
55
        if (!isset($data['date_formats'])) {
56
            throw new Exception('date formats configuration must be defined in the DateHelper constructor.');
57
        }
58
        if (!isset($data['time_formats'])) {
59
            throw new Exception('time formats configuration must be defined in the DateHelper constructor.');
60
        }
61
        if (!isset($data['translator'])) {
62
            throw new Exception('Translator needs to be defined in the dateHelper class.');
63
        }
64
65
        $this->setTranslator($data['translator']);
66
        $this->dateFormats = $data['date_formats'];
67
        $this->timeFormats = $data['time_formats'];
68
    }
69
70
    /**
71
     * @param mixed  $date   The date
72
     *                       [startDate, endDate]
73
     *                       DateTimeInterface
74
     *                       string.
75
     * @param string $format The format to use.
76
     * @return string
77
     */
78 View Code Duplication
    public function formatDate($date, $format = 'default')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
79
    {
80
        $this->dateFormat = $format;
81
82
        if (is_array($date)) {
83
            $this->from = $this->parseAsDate($date[0]);
84
            $this->to = $this->parseAsDate($date[1]);
85
        } else {
86
            $this->from = $this->parseAsDate($date);
87
            $this->to = null;
88
        }
89
90
        return $this->formatDateFromCase($this->getDateCase());
91
    }
92
93
    /**
94
     * @param mixed  $date   The date
95
     *                       [startDate, endDate]
96
     *                       DateTimeInterface
97
     *                       string.
98
     * @param string $format The format to use.
99
     * @return string
100
     */
101 View Code Duplication
    public function formatTime($date, $format = 'default')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
102
    {
103
        $this->timeFormat = $format;
104
105
        if (is_array($date)) {
106
            $this->from = $this->parseAsDate($date[0]);
107
            $this->to = $this->parseAsDate($date[1]);
108
        } else {
109
            $this->from = $this->parseAsDate($date);
110
            $this->to = null;
111
        }
112
113
        return $this->formatTimeFromCase($this->getTimeCase());
114
    }
115
116
    /**
117
     * Get the usage case by comparing two dates.
118
     * @return string
119
     */
120
    private function getDateCase()
121
    {
122
        $from = $this->from;
123
        $to = $this->to;
124
125
        // single date event
126
        if (!$to || $to->format('Ymd') === $from->format('Ymd')) {
127
            return 'single';
128
        }
129
130
        $fromDate = [
131
            'day'   => $from->format('d'),
132
            'month' => $from->format('m'),
133
            'year'  => $from->format('y')
134
        ];
135
136
        $toDate = [
137
            'day'   => $to->format('d'),
138
            'month' => $to->format('m'),
139
            'year'  => $to->format('y')
140
        ];
141
142
        $case = null;
143
        $case = $fromDate['day'] !== $toDate['day'] ? 'different_day' : $case;
144
        $case = $fromDate['month'] !== $toDate['month'] ? 'different_month' : $case;
145
        $case = $fromDate['year'] !== $toDate['year'] ? 'different_year' : $case;
146
147
        return $case;
148
    }
149
150
    /**
151
     * Get the usage case by comparing two hours.
152
     * @return string
153
     */
154
    private function getTimeCase()
155
    {
156
        $from = $this->from;
157
        $to = $this->to;
158
159
        // Single hour event
160
        if (!$to || $to->format('Hi') === $from->format('Hi')) {
161
            if ($to->format('i') == 0) {
162
                return 'single_round';
163
            }
164
165
            return 'single';
166
        }
167
168
        $fromTime = [
169
            'hour'   => $from->format('H'),
170
            'minute' => $from->format('i'),
171
        ];
172
173
        $toTime = [
174
            'hour'   => $to->format('H'),
175
            'minute' => $to->format('i'),
176
        ];
177
178
        $case = null;
179
        $case = $fromTime['hour'] !== $toTime['hour'] ? 'different_time' : $case;
180
        $case = $fromTime['minute'] == 0 ? 'different_time_round' : $case;
181
        $case = $fromTime['minute'] != $toTime['minute'] ? 'different_time' : $case;
182
183
        return $case;
184
    }
185
186
    /**
187
     * @param string $case The use case.
188
     * @return string
189
     */
190
    private function formatDateFromCase($case)
191
    {
192
        $dateFormats = $this->dateFormats;
193
        $case = $dateFormats[$this->dateFormat][$case];
194
195
        $content = $this->translator()->translation($case['content']);
196
197
        $formats['from'] = $this->translator()->translation($case['formats']['from']);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$formats was never initialized. Although not strictly required by PHP, it is generally a good practice to add $formats = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
198
        $formats['to'] = isset($case['formats']['to']) ? $this->translator()->translation($case['formats']['to']) : null;
199
200
        $formats['from'] = $this->crossPlatformFormat((string)$formats['from']);
201
        $formats['to'] = $this->crossPlatformFormat((string)$formats['to']);
202
203 View Code Duplication
        if (!$this->to || !$formats['to']) {
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...
204
            return sprintf(
205
                (string)$content,
206
                strftime($formats['from'], $this->from->getTimestamp())
207
            );
208
        }
209
210
        return sprintf(
211
            (string)$content,
212
            strftime($formats['from'], $this->from->getTimestamp()),
213
            strftime($formats['to'], $this->to->getTimestamp())
214
        );
215
    }
216
217
    /**
218
     * @param string $case The use case.
219
     * @return string
220
     */
221
    private function formatTimeFromCase($case)
222
    {
223
        $timeFormats = $this->timeFormats;
224
        $case = $timeFormats[$this->timeFormat][$case];
225
226
        $content = $this->translator()->translation($case['content']);
227
228
        $formats['from'] = $case['formats']['from'];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$formats was never initialized. Although not strictly required by PHP, it is generally a good practice to add $formats = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
229
        $formats['to'] = isset($case['formats']['to']) ? $case['formats']['to'] : null;
230
231
        $formats['from'] = $this->translator()->translation($formats['from']);
232
        $formats['to'] = $this->translator()->translation($formats['to']);
233
234 View Code Duplication
        if (!$this->to || !$formats['to']) {
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...
235
            return sprintf(
236
                (string)$content,
237
                strftime($formats['from'], $this->from->getTimestamp())
238
            );
239
        }
240
241
        return sprintf(
242
            (string)$content,
243
            strftime($formats['from'], $this->from->getTimestamp()),
244
            strftime($formats['to'], $this->to->getTimestamp())
245
        );
246
    }
247
248
    // ==========================================================================
249
    // UTILS
250
    // ==========================================================================
251
252
    /**
253
     * @param mixed $date The date to convert.
254
     * @return DateTime
255
     */
256
    private function parseAsDate($date)
257
    {
258
        if ($date instanceof \DateTimeInterface) {
259
            return $date;
260
        }
261
262
        return new DateTime($date);
263
    }
264
265
    /**
266
     * @param mixed $format DateTime to be formatted.
267
     * @return mixed
268
     */
269
    private function crossPlatformFormat($format)
270
    {
271
        if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
272
            $format = preg_replace('#(?<!%)((?:%%)*)%e#', '\1%#d', $format);
273
        }
274
275
        return $format;
276
    }
277
}
278