Completed
Push — master ( 4239b7...588c48 )
by Amine
04:20
created

DayAttendance::explodeDayAttendanceLineParts()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 11
ccs 6
cts 6
cp 1
rs 9.4285
cc 2
eloc 5
nc 2
nop 1
crap 2
1
<?php
2
3
namespace AmineBenHariz\Attendance;
4
5
/**
6
 * Class DayAttendance
7
 * @package AmineBenHariz\Attendance
8
 */
9
class DayAttendance
10
{
11
    /**
12
     * example: 2015-12-12|08:30 (10:00-10:30) (12:30-13:30) (16:00-16:30) 17:30
13
     */
14
    const DAY_ATTENDANCE_LINE_REGEX =
15
        '/^\d{4}-\d{2}-\d{2}\|\d{2}:\d{2}( \(\d{2}:\d{2}-\d{2}:\d{2}\))* \d{2}:\d{2}(\|[^\|]*)?$/';
16
17
    /**
18
     * @var \DateTime
19
     */
20
    private $arrival;
21
22
    /**
23
     * @var \DateTime
24
     */
25
    private $departure;
26
27
    /**
28
     * @var Pause[]
29
     */
30
    private $pauseList = [];
31
32
    /**
33
     * @var string
34
     */
35
    private $description = "";
36
37
    /**
38
     * DayAttendance constructor.
39
     * @param \DateTime $arrival
40
     * @param \DateTime $departure
41
     * @param Pause[] $pauseList
42
     */
43 33
    public function __construct(\DateTime $arrival, \DateTime $departure, array $pauseList = [])
44
    {
45 33
        if ($arrival > $departure or $arrival->format('Y-m-d') !== $departure->format('Y-m-d')) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
46 6
            throw new \InvalidArgumentException;
47
        }
48
49 27
        $this->arrival = $arrival;
50 27
        $this->departure = $departure;
51
52 27
        foreach ($pauseList as $pause) {
53 24
            $this->addPause($pause);
54 24
        }
55 18
    }
56
57
    /**
58
     * @return \DateTime
59
     */
60 48
    public function getArrival()
61
    {
62 48
        return $this->arrival;
63
    }
64
65
    /**
66
     * @return \DateTime
67
     */
68 42
    public function getDeparture()
69
    {
70 42
        return $this->departure;
71
    }
72
73
    /**
74
     * @return Pause[]
75
     */
76 42
    public function getPauseList()
77
    {
78 42
        return $this->pauseList;
79
    }
80
81
    /**
82
     * @return string
83
     */
84 18
    public function getDescription()
85
    {
86 18
        return $this->description;
87
    }
88
89
    /**
90
     * @param string $description
91
     */
92 18
    public function setDescription($description)
93
    {
94 18
        $this->description = $description;
95 18
    }
96
97
    /**
98
     * @param Pause $pause
99
     */
100 24
    private function addPause(Pause $pause)
101
    {
102 24
        if ($pause->getStart() < $this->getArrival()) {
103 3
            throw new \InvalidArgumentException;
104
        }
105
106 21
        if ($pause->getEnd() > $this->getDeparture()) {
107 3
            throw new \InvalidArgumentException;
108
        }
109
110 21
        if ($this->isPauseOverlapping($pause)) {
111 3
            throw new \InvalidArgumentException;
112
        }
113
114
115 21
        $this->pauseList[] = $pause;
116 21
    }
117
118
    /**
119
     * @param Pause $pause
120
     * @return bool
121
     */
122 21
    private function isPauseOverlapping(Pause $pause)
123
    {
124 21
        $existingPauseList = $this->getPauseList();
125 21
        if (empty($existingPauseList)) {
126 21
            return false;
127
        }
128
129 15
        foreach ($existingPauseList as $existingPause) {
130 15
            if ($pause->isOverlapping($existingPause)) {
131 3
                return true;
132
            }
133 12
        }
134
135 12
        return false;
136
    }
137
138
    /**
139
     * @return \DateInterval
140
     */
141 9
    public function getDuration()
142
    {
143 9
        $cursor = clone $this->getArrival();
144
145
        // PHP 5.4 : empty() can only handle variables
146 9
        $pauseList = $this->getPauseList();
147
148 9
        if (!empty($pauseList)) {
149 9
            foreach ($pauseList as $pause) {
150 9
                $cursor->add($pause->getDuration());
151 9
            }
152 9
        }
153
154 9
        return $cursor->diff($this->getDeparture());
155
    }
156
157
    /**
158
     * @return int
159
     */
160 6
    public function getTotalMinutes()
161
    {
162 6
        $duration = $this->getDuration();
163 6
        return intval($duration->format('%H')) * 60 + intval($duration->format('%I'));
164
    }
165
166
    /**
167
     * @param $dayAttendanceLine
168
     * @return DayAttendance
169
     */
170 18
    public static function parseDayAttendanceLine($dayAttendanceLine)
171
    {
172 18
        if (!self::isValidDayAttendanceLine($dayAttendanceLine)) {
173 3
            throw new \InvalidArgumentException;
174
        }
175
176 15
        list($date, $timeLine, $description) = self::explodeDayAttendanceLineParts($dayAttendanceLine);
177
178 15
        $times = explode(' ', $timeLine);
179
180 15
        $arrival = new \DateTime($date . ' ' . array_shift($times));
181 15
        $departure = new \DateTime($date . ' ' . array_pop($times));
182
183 15
        $pauseList = self::parsePauseBlocks($date, $times);
184
185 15
        $dayAttendance = new DayAttendance($arrival, $departure, $pauseList);
186 15
        $dayAttendance->setDescription($description);
187 15
        return $dayAttendance;
188
    }
189
190
    /**
191
     * @param string $dayAttendanceLine
192
     * @return array
193
     */
194 15
    private static function explodeDayAttendanceLineParts($dayAttendanceLine)
195
    {
196 15
        $parts = explode('|', $dayAttendanceLine);
197
198
        // if empty description
199 15
        if (!isset($parts[2])) {
200 12
            $parts[2] = '';
201 12
        }
202
203 15
        return $parts;
204
    }
205
206
    /**
207
     * @param string $date date in Y-m-d format, ex: 2016-03-29
208
     * @param array $pauseBlocks
209
     * @return Pause[]
210
     */
211 15
    private static function parsePauseBlocks($date, $pauseBlocks)
212
    {
213 15
        if (empty($pauseBlocks)) {
214 3
            return [];
215
        }
216
217 12
        $pauseList = [];
218 12
        foreach ($pauseBlocks as $pauseBlock) {
219 12
            $pauseList[] = self::parsePauseBlock($date, $pauseBlock);
220 12
        }
221
222 12
        return $pauseList;
223
    }
224
225
    /**
226
     * @param string $date date in Y-m-d format, ex: 2016-03-29
227
     * @param string $pauseBlock ex: (10:00-10:30)
228
     * @return Pause
229
     */
230 12
    private static function parsePauseBlock($date, $pauseBlock)
231
    {
232 12
        $pauseStart = new \DateTime($date . ' ' . substr($pauseBlock, 1, 5));
233 12
        $pauseEnd = new \DateTime($date . ' ' . substr($pauseBlock, 7, 5));
234 12
        return new Pause($pauseStart, $pauseEnd);
235
    }
236
237
    /**
238
     * @param $dayAttendanceLine
239
     * @return int
240
     */
241 45
    public static function isValidDayAttendanceLine($dayAttendanceLine)
242
    {
243 45
        return preg_match(self::DAY_ATTENDANCE_LINE_REGEX, $dayAttendanceLine) === 1;
244
    }
245
246
    /**
247
     * @return string
248
     */
249 6
    public function exportLine()
250
    {
251 6
        $line = $this->getDate() . '|' . $this->getTimeLine();
252
253 6
        if ($this->getDescription() !== '') {
254 3
            $line .= '|' . $this->getDescription();
255 3
        }
256
257 6
        return $line;
258
    }
259
260
    /**
261
     * @return string
262
     */
263 9
    public function getDate()
264
    {
265 9
        return $this->getArrival()->format('Y-m-d');
266
    }
267
268
    /**
269
     * @return string
270
     */
271 9
    public function getTimeLine()
272
    {
273 9
        $line = $this->getArrival()->format('H:i');
274
275 9
        foreach ($this->getPauseList() as $pause) {
276 9
            $line .= ' ' . $pause->exportBlock();
277 9
        }
278
279 9
        $line .= ' ' . $this->getDeparture()->format('H:i');
280
281 9
        return $line;
282
    }
283
}
284