Skip to content

Commit 0efdebc

Browse files
feat: add new methods in timetracker (#9)
new methods: - lap - getLaps - pause - resume - inspect
1 parent 68e482e commit 0efdebc

File tree

5 files changed

+410
-1
lines changed

5 files changed

+410
-1
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace NAL\TimeTracker\Exception;
4+
5+
class NoActivePausedTimerToResume extends \BadMethodCallException
6+
{
7+
public function __construct($id)
8+
{
9+
parent::__construct("The timer with ID '{$id}' has no active pause to resume");
10+
}
11+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace NAL\TimeTracker\Exception;
4+
5+
use BadMethodCallException;
6+
7+
class TimerAlreadyPaused extends BadMethodCallException
8+
{
9+
public function __construct($id)
10+
{
11+
parent::__construct("The timer with ID '{$id}' is already paused and not yet resumed");
12+
}
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace NAL\TimeTracker\Exception;
4+
5+
use RuntimeException;
6+
7+
class UnmatchedPauseWithoutResume extends RuntimeException
8+
{
9+
public function __construct($id)
10+
{
11+
parent::__construct("Unmatched pause without resume for timer with ID '{$id}'");
12+
}
13+
}

src/TimeTracker.php

Lines changed: 170 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
use Illuminate\Container\Container;
66
use InvalidArgumentException;
77
use NAL\TimeTracker\Exception\InvalidUnitName;
8+
use NAL\TimeTracker\Exception\NoActivePausedTimerToResume;
89
use NAL\TimeTracker\Exception\NoActiveTimerToStopException;
10+
use NAL\TimeTracker\Exception\TimerAlreadyPaused;
911
use NAL\TimeTracker\Exception\TimerNotStarted;
12+
use NAL\TimeTracker\Exception\UnmatchedPauseWithoutResume;
1013
use NAL\TimeTracker\Exception\UnsupportedLogic;
1114

1215
class TimeTracker
@@ -25,6 +28,27 @@ class TimeTracker
2528
*/
2629
private array $end = [];
2730

31+
/**
32+
* Stores pause times of tracked operations by ID.
33+
*
34+
* @var array
35+
*/
36+
private array $pause = [];
37+
38+
/**
39+
* Stores resume times of tracked operations by ID.
40+
*
41+
* @var array
42+
*/
43+
private array $resume = [];
44+
45+
/**
46+
* Stores laps of tracked operations by ID.
47+
*
48+
* @var array
49+
*/
50+
private array $laps = [];
51+
2852
private Unit $unit;
2953

3054
/**
@@ -96,6 +120,106 @@ public function stop(?string $id = null): void
96120
$this->end[$id] = microtime(true);
97121
}
98122

123+
/**
124+
* Records a lap time for the specified timer.
125+
*
126+
* A lap represents a checkpoint within an ongoing timer session. Each lap is recorded
127+
* with its timestamp and an optional description for context.
128+
*
129+
* @param string $id The identifier of the timer.
130+
* @param string $description An optional label or note for this lap (default: empty string).
131+
*
132+
* @throws TimerNotStarted If the timer with the given ID has not been started.
133+
*
134+
* @return void
135+
*/
136+
public function lap(string $id, string $description = ''): void
137+
{
138+
if (!isset($this->start[$id])) {
139+
throw new TimerNotStarted($id);
140+
}
141+
142+
$this->laps[$id][] = [
143+
'description' => $description,
144+
'time' => microtime(true)
145+
];
146+
}
147+
148+
/**
149+
* Retrieves all recorded laps for a given timer.
150+
*
151+
* Each lap contains its timestamp and optional description.
152+
* If no laps have been recorded, an empty array is returned.
153+
*
154+
* @param string $id The identifier of the timer.
155+
*
156+
* @return array<int, array{description: string, time: float}> A list of laps with their details.
157+
*/
158+
public function getLaps(string $id): array
159+
{
160+
return $this->laps[$id] ?? [];
161+
}
162+
163+
/**
164+
* Pauses the specified timer.
165+
*
166+
* If the timer is already paused and not yet resumed, this method throws an exception.
167+
* Each pause event is recorded with a timestamp and an optional description.
168+
*
169+
* @param string $id The identifier of the timer.
170+
* @param string $description An optional label or note for this pause (default: empty string).
171+
*
172+
* @throws TimerNotStarted If the timer with the given ID has not been started.
173+
* @throws TimerAlreadyPaused If the timer is already paused.
174+
*
175+
* @return void
176+
*/
177+
public function pause(string $id, string $description = ''): void
178+
{
179+
if (!isset($this->start[$id])) {
180+
throw new TimerNotStarted($id);
181+
}
182+
183+
if (isset($this->pause[$id]) && count($this->pause[$id]) > count($this->resume[$id] ?? [])) {
184+
throw new TimerAlreadyPaused($id);
185+
}
186+
187+
$this->pause[$id][] = [
188+
'description' => $description,
189+
'time' => microtime(true)
190+
];
191+
}
192+
193+
/**
194+
* Resumes a previously paused timer.
195+
*
196+
* A timer can only be resumed if it has an active pause entry that has not yet been resumed.
197+
* Each resume event is recorded with a timestamp and an optional description.
198+
*
199+
* @param string $id The identifier of the timer.
200+
* @param string $description An optional label or note for this resume (default: empty string).
201+
*
202+
* @throws TimerNotStarted If the timer with the given ID has not been started.
203+
* @throws NoActivePausedTimerToResume If there is no active pause to resume.
204+
*
205+
* @return void
206+
*/
207+
public function resume(string $id, string $description = ''): void
208+
{
209+
if (!isset($this->start[$id])) {
210+
throw new TimerNotStarted($id);
211+
}
212+
213+
if (!isset($this->pause[$id]) || count($this->pause[$id]) === count($this->resume[$id] ?? [])) {
214+
throw new NoActivePausedTimerToResume($id);
215+
}
216+
217+
$this->resume[$id][] = [
218+
'description' => $description,
219+
'time' => microtime(true)
220+
];
221+
}
222+
99223
/**
100224
* Check whether timer with given id is started or not
101225
*
@@ -195,16 +319,35 @@ public static function watch(callable $callback, array $params = [], string $uni
195319
*
196320
* @param string $id The identifier for the timer.
197321
* @return Result|null The Result instance or null if the timer does not exist.
322+
*
323+
* @throws UnmatchedPauseWithoutResume
198324
*/
199325
public function calculate(string $id): ?Result
200326
{
201327
if (!$this->exists($id)) {
202328
return null;
203329
}
204330

205-
return new Result($this->unit, $this->end[$id] - $this->start[$id], 's');
331+
$calculate = $this->end[$id] - $this->start[$id];
332+
333+
$pausedTime = 0;
334+
335+
if (!empty($this->pause[$id]) && !empty($this->resume[$id])) {
336+
foreach ($this->pause[$id] as $index => $pauseTime) {
337+
if (isset($this->resume[$id][$index])) {
338+
$pausedTime += $this->resume[$id][$index]['time'] - $pauseTime['time'];
339+
} else {
340+
throw new UnmatchedPauseWithoutResume($id);
341+
}
342+
}
343+
}
344+
345+
$calculate -= $pausedTime;
346+
347+
return new Result($this->unit, $calculate, 's');
206348
}
207349

350+
208351
/**
209352
* Adds a custom unit definition based on seconds.
210353
*
@@ -297,4 +440,30 @@ public function getUnit(): Unit
297440
{
298441
return $this->unit;
299442
}
443+
444+
/**
445+
* Inspects and retrieves detailed timing data for a specific timer.
446+
*
447+
* @param string $id The identifier of the timer to inspect.
448+
*
449+
* @return array{
450+
* start: float|null,
451+
* end: float|null,
452+
* paused: array<int, array{description: string, time: float}>,
453+
* resumed: array<int, array{description: string, time: float}>,
454+
* status: string,
455+
* laps: array<int, array{description: string, time: float}>
456+
* } A structured array containing all tracked data for the specified timer.
457+
*/
458+
public function inspect(string $id): array
459+
{
460+
return [
461+
'start' => $this->start[$id] ?? null,
462+
'end' => $this->end[$id] ?? null,
463+
'paused' => $this->pause[$id] ?? [],
464+
'resumed' => $this->resume[$id] ?? [],
465+
'status' => $this->status($id),
466+
'laps' => $this->laps[$id] ?? []
467+
];
468+
}
300469
}

0 commit comments

Comments
 (0)