Stopwatch::getEvent()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 8
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 16
ccs 9
cts 9
cp 1
crap 2
rs 10
1
<?php
2
3
/**
4
 * For the full copyright and license information, please view
5
 * the LICENSE file that was distributed with this source code.
6
 */
7
8
declare(strict_types=1);
9
10
namespace loophp\nanobench\Time;
11
12
use InvalidArgumentException;
13
use LogicException;
14
15
use function count;
16
use function in_array;
17
18
final class Stopwatch implements StopwatchInterface
19
{
20
    private ClockInterface $clock;
21
22
    private array $events;
23
24 17
    public function __construct(ClockInterface $clock)
25
    {
26 17
        $clock = $clock;
27 17
        $time = $clock->time();
28
29 17
        $this->clock = $clock;
30 17
        $this->events = [[$time, 'construct']];
31 17
    }
32
33
    /**
34
     * @psalm-param mixed $id
35
     *
36
     * @param mixed|null $id
37
     */
38 7
    public function checkpoint($id = null): StopwatchInterface
39
    {
40 7
        $time = $this->clock->time();
41
42 7
        if (true === in_array($id, ['start', 'construct', 'stop'], true)) {
43 1
            throw new InvalidArgumentException('Invalid checkpoint id.');
44
        }
45
46 7
        if (false === $this->isStarted()) {
47 1
            throw new LogicException('Cannot add a checkpoint in a stopwatch that has not started yet.');
48
        }
49
50 7
        if (true === $this->isStopped()) {
51 1
            throw new LogicException('Cannot add a checkpoint in a stopped stopwatch.');
52
        }
53
54 7
        $this->events[] = [$time, $id];
55
56 7
        return $this;
57
    }
58
59 2
    public function getAll(): array
60
    {
61 2
        return $this->getEvents();
62
    }
63
64 1
    public function getConstructTime(): TimeInterface
65
    {
66 1
        $event = $this->getEvent('construct');
67
68 1
        return $event[0];
69
    }
70
71 1
    public function getDiff(): TimeInterface
72
    {
73 1
        $diffs = $this->getDiffs();
74
75 1
        return end($diffs);
76
    }
77
78
    /**
79
     * @psalm-param mixed $from
80
     * @psalm-param mixed $to
81
     *
82
     * @param mixed $from
83
     * @param mixed $to
84
     */
85 8
    public function getDiffFromTo($from, $to): TimeInterface
86
    {
87 8
        $events = array_filter(
88 8
            $this->getEvents(),
89
            static function (array $event) use ($from, $to): bool {
90 8
                return in_array($event[1], [$from, $to], true);
91 8
            }
92
        );
93
94 8
        if (2 !== count($events)) {
95 1
            throw new InvalidArgumentException('Unable to find the events.');
96
        }
97
98
        $fromKey = $this->arrayFind($events, static function (array $event) use ($from): bool {
99 8
            return $event[1] === $from;
100 8
        });
101
        $toKey = $this->arrayFind($events, static function (array $event) use ($to): bool {
102 8
            return $event[1] === $to;
103 8
        });
104
105 8
        return new Duration($events[$fromKey][0], $events[$toKey][0]);
106
    }
107
108 2
    public function getDiffs(): array
109
    {
110 2
        $events = $this->getEvents();
111
        // Remove created event.
112 2
        array_shift($events);
113
114 2
        $events = array_map(
115
            static function (array $event): TimeInterface {
116 2
                return $event[0];
117 2
            },
118
            $events
119
        );
120
121 2
        $first = array_shift($events);
122
123 2
        $diffs = [];
124
125 2
        foreach ($events as $event) {
126 2
            $diffs[] = new Duration($first, $event);
127
128 2
            $first = $event;
129
        }
130
131 2
        return $diffs;
132
    }
133
134 1
    public function getElapsed(): TimeInterface
135
    {
136 1
        $time = $this->clock->time();
137
138 1
        if (true === $this->isStopped()) {
139 1
            throw new LogicException('Cannot get elapsed time an already stopped stopwatch.');
140
        }
141
142 1
        $events = $this->getEvents();
143 1
        $event = end($events);
144
145 1
        return new Duration(
146 1
            $event[0],
147
            $time
148
        );
149
    }
150
151
    /**
152
     * @psalm-param mixed $id
153
     *
154
     * @param mixed $id
155
     */
156 4
    public function getEvent($id): array
157
    {
158 4
        $events = $this->getEvents();
159
160 4
        $events = array_filter(
161 4
            $events,
162
            static function (array $event) use ($id): bool {
163 4
                return $event[1] === $id;
164 4
            }
165
        );
166
167 4
        if ([] === $events) {
168 1
            throw new InvalidArgumentException(sprintf('Unable to find event: %s', $id));
169
        }
170
171 4
        return current($events);
172
    }
173
174 1
    public function getLastDiff(): TimeInterface
175
    {
176 1
        $diffs = $this->getDiffs();
177
178 1
        return end($diffs);
179
    }
180
181 1
    public function getStartTime(): TimeInterface
182
    {
183 1
        if (false === $this->isStarted()) {
184 1
            throw new LogicException('Stopwatch has not started yet.');
185
        }
186
187 1
        return $this->getEvent('start')[0];
188
    }
189
190 5
    public function getStartToStopDuration(): TimeInterface
191
    {
192 5
        return $this->getDiffFromTo('start', 'stop');
193
    }
194
195 1
    public function getStopTime(): TimeInterface
196
    {
197 1
        if (false === $this->isStopped()) {
198 1
            throw new LogicException('Stopwatch has not stopped yet.');
199
        }
200
201 1
        return $this->getEvent('stop')[0];
202
    }
203
204 15
    public function isStarted(): bool
205
    {
206
        return null !== $this
207 15
            ->arrayFind(
208 15
                $this->getEvents(),
209
                static function (array $v): bool {
210 15
                    return 'start' === $v[1];
211 15
                }
212
            );
213
    }
214
215 14
    public function isStopped(): bool
216
    {
217
        return null !== $this
218 14
            ->arrayFind(
219 14
                $this->getEvents(),
220
                static function (array $v): bool {
221 14
                    return 'stop' === $v[1];
222 14
                }
223
            );
224
    }
225
226 3
    public function reset(): StopwatchInterface
227
    {
228 3
        $this->events = [array_shift($this->events)];
229
230 3
        return $this;
231
    }
232
233 14
    public function start(): StopwatchInterface
234
    {
235 14
        $time = $this->clock->time();
236
237 14
        if (true === $this->isStarted()) {
238 1
            throw new LogicException('Cannot start an already started stopwatch.');
239
        }
240
241 14
        $this->events[] = [$time, 'start'];
242
243 14
        return $this;
244
    }
245
246 10
    public function stop(): void
247
    {
248 10
        $time = $this->clock->time();
249
250 10
        if (true === $this->isStopped()) {
251 1
            throw new LogicException('Cannot stop an already stopped stopwatch.');
252
        }
253
254 10
        if (false === $this->isStarted()) {
255 1
            throw new LogicException('Cannot stop a stopwatch that has not started yet.');
256
        }
257
258 10
        $this->events[] = [$time, 'stop'];
259 10
    }
260
261 15
    private function arrayFind(array $xs, callable $f): ?int
262
    {
263 15
        foreach ($xs as $key => $x) {
264 15
            if ($f($x)) {
265 14
                return $key;
266
            }
267
        }
268
269 15
        return null;
270
    }
271
272 15
    private function getEvents(): array
273
    {
274 15
        $events = [];
275
276 15
        foreach ($this->events as $key => $event) {
277 15
            $event[1] = $event[1] ?? $key;
278 15
            $events[] = $event;
279
        }
280
281 15
        return $events;
282
    }
283
}
284