The ExecutionTimer class measures elapsed wall-clock time using PHP's high-resolution microtime(true). It supports start/stop/reset/restart cycles, live reading while the timer is still running, and a static measure() helper for timing any callable in one call.
- Class:
JiFramework\Core\Utilities\ExecutionTimer - Access:
$app->executionTimer - Lazy-loaded — instantiated on first access
// Time a block of code
$app->executionTimer->start();
doSomethingExpensive();
$app->executionTimer->stop();
echo $app->executionTimer->getElapsedTimeInMilliseconds() . " ms";
// One-liner with the static helper
$result = ExecutionTimer::measure(function () {
return fetchDataFromApi();
});
echo "API call took " . round($result['elapsed_ms'], 2) . " ms";
echo "Response: " . json_encode($result['result']);
start(): void
Start the timer. Records the current high-resolution timestamp and marks the timer as running. Any previous measurement is discarded — calling start() on a stopped timer is equivalent to reset() + start().
$timer = new ExecutionTimer();
$timer->start();
// do work …
$timer->stop();
echo $timer->getElapsedTime() . " s";
While the timer is running, getElapsedTime() returns the live elapsed time since start() was called — you do not need to stop it first to take a reading.
stop(): void
Stop the timer and record the final elapsed time. Safe to call multiple times — subsequent calls are no-ops that leave the recorded time unchanged. Also safe to call before start() — does nothing in that case.
$timer->start();
usleep(50000); // 50 ms
$timer->stop();
echo $timer->getElapsedTimeInMilliseconds() . " ms"; // ~50 ms
$timer->stop(); // second call — no effect
echo $timer->getElapsedTimeInMilliseconds() . " ms"; // same value
reset(): void
Reset all state to the initial zero values. The timer is stopped and all elapsed time is cleared. Use this when you want to reuse the same instance for a new measurement without starting immediately.
$timer->start();
usleep(30000);
$timer->stop();
echo $timer->getElapsedTimeInMilliseconds(); // ~30 ms
$timer->reset();
echo $timer->getElapsedTime(); // 0.0
echo $timer->isRunning() ? "running" : "stopped"; // stopped
restart(): void
Reset and immediately start a fresh measurement. Equivalent to reset() + start() in one call. Use this when you want to discard the current measurement and begin timing a new operation right away.
$timer->start();
usleep(50000); // 50 ms measurement
$timer->stop();
// Begin a completely new measurement
$timer->restart();
// timer is now running — previous 50 ms is gone
doMoreWork();
$timer->stop();
echo $timer->getElapsedTimeInMilliseconds(); // only the new duration
isRunning(): bool
Returns true when the timer has been started and not yet stopped, false otherwise. Useful for conditional logic and assertions.
$timer = new ExecutionTimer();
$timer->isRunning(); // false — never started
$timer->start();
$timer->isRunning(); // true
$timer->stop();
$timer->isRunning(); // false
$timer->reset();
$timer->isRunning(); // false
getElapsedTime(): float
getElapsedTimeInMilliseconds(): float
getElapsedTimeInMicroseconds(): float
Return the elapsed duration in seconds, milliseconds, and microseconds respectively. All three methods share the same behaviour:
- Before
start()— returns0.0(never a Unix timestamp). - While running — returns the live elapsed time since
start()was called. You do not need to stop the timer to read it. - After
stop()— returns the recorded duration (frozen at stop time).
$timer = new ExecutionTimer();
// Safe zero before start
$timer->getElapsedTime(); // 0.0
$timer->getElapsedTimeInMilliseconds(); // 0.0
$timer->getElapsedTimeInMicroseconds(); // 0.0
$timer->start();
// Live reading — no need to stop
usleep(10000);
echo $timer->getElapsedTimeInMilliseconds(); // ~10 ms (live)
$timer->stop();
// Recorded values
echo $timer->getElapsedTime(); // e.g. 0.010312 s
echo $timer->getElapsedTimeInMilliseconds(); // e.g. 10.312 ms
echo $timer->getElapsedTimeInMicroseconds(); // e.g. 10312 µs
static measure(callable $callback, mixed ...$args): array
Time any callable in one call. Creates a fresh timer internally, executes the callback, stops the timer, and returns an array with the callback's return value and the elapsed duration.
$callback— (callable) The function to time....$args— (mixed) Arguments forwarded to the callback.
Returns: array with three keys:
result— The return value of the callback.elapsed_time— (float) Elapsed time in seconds.elapsed_ms— (float) Elapsed time in milliseconds.
// Time an anonymous function
$result = ExecutionTimer::measure(function () {
return fetchDataFromExternalApi();
});
echo round($result['elapsed_ms'], 2) . " ms";
echo json_encode($result['result']);
// Pass arguments to the callback
$result = ExecutionTimer::measure(fn($id) => User::find($id), 42);
echo "User: " . $result['result']['name'];
echo round($result['elapsed_ms'], 2) . " ms";
// Compare two implementations
$a = ExecutionTimer::measure(fn() => slowSort($data));
$b = ExecutionTimer::measure(fn() => fastSort($data));
echo "slow: " . round($a['elapsed_ms'], 2) . " ms
";
echo "fast: " . round($b['elapsed_ms'], 2) . " ms
";
echo "speedup: " . round($a['elapsed_ms'] / $b['elapsed_ms'], 1) . "x";
Each measure() call is fully independent — it creates its own timer and does not affect any shared state.
Profile a database query
$app->executionTimer->start();
$users = $app->db->table('users')
->where('active', 1)
->orderBy('name')
->get();
$app->executionTimer->stop();
$app->logger->debug("User query took " . round($app->executionTimer->getElapsedTimeInMilliseconds(), 2) . " ms");
Live progress reporting
$timer = new ExecutionTimer();
$timer->start();
foreach ($largeDataset as $row) {
processRow($row);
// Log every 1 000 rows without stopping the timer
if ($i % 1000 === 0) {
$elapsed = $timer->getElapsedTimeInMilliseconds();
echo "Processed $i rows in " . round($elapsed) . " ms
";
}
}
$timer->stop();
echo "Done in " . round($timer->getElapsedTimeInMilliseconds()) . " ms";
Reuse a single timer across multiple operations
$timer = $app->executionTimer;
$timer->start();
$data = fetchData();
$timer->stop();
$app->logger->debug("fetch: " . round($timer->getElapsedTimeInMilliseconds(), 2) . " ms");
$timer->restart(); // discards previous measurement, starts fresh
$processed = processData($data);
$timer->stop();
$app->logger->debug("process: " . round($timer->getElapsedTimeInMilliseconds(), 2) . " ms");
Benchmark two approaches
$a = ExecutionTimer::measure(fn() => approachA($input));
$b = ExecutionTimer::measure(fn() => approachB($input));
$winner = $a['elapsed_ms'] < $b['elapsed_ms'] ? 'A' : 'B';
echo "Winner: approach $winner ("
. round(min($a['elapsed_ms'], $b['elapsed_ms']), 2) . " ms vs "
. round(max($a['elapsed_ms'], $b['elapsed_ms']), 2) . " ms)";