PHPackages                             philiprehberger/php-stopwatch - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Debugging &amp; Profiling](/categories/debugging)
4. /
5. philiprehberger/php-stopwatch

ActiveLibrary[Debugging &amp; Profiling](/categories/debugging)

philiprehberger/php-stopwatch
=============================

Precise code execution timer with lap tracking and memory measurement

v1.3.0(2mo ago)138MITPHPPHP ^8.2CI passing

Since Mar 15Pushed 1mo agoCompare

[ Source](https://github.com/philiprehberger/php-stopwatch)[ Packagist](https://packagist.org/packages/philiprehberger/php-stopwatch)[ Docs](https://github.com/philiprehberger/php-stopwatch)[ GitHub Sponsors](https://github.com/philiprehberger)[ RSS](/packages/philiprehberger-php-stopwatch/feed)WikiDiscussions main Synced 3w ago

READMEChangelogDependencies (6)Versions (11)Used By (0)

PHP Stopwatch
=============

[](#php-stopwatch)

[![Tests](https://github.com/philiprehberger/php-stopwatch/actions/workflows/tests.yml/badge.svg)](https://github.com/philiprehberger/php-stopwatch/actions/workflows/tests.yml)[![Latest Version on Packagist](https://camo.githubusercontent.com/f93b76b601f4278433b361793a1c51c40587763a5725e80b253ef5dc329c14c6/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7068696c69707265686265726765722f7068702d73746f7077617463682e737667)](https://packagist.org/packages/philiprehberger/php-stopwatch)[![Last updated](https://camo.githubusercontent.com/04a691403d75db5362a99e79c09ab70d4c41531c51d1d7b89ac2c6b5f115097d/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6173742d636f6d6d69742f7068696c69707265686265726765722f7068702d73746f707761746368)](https://github.com/philiprehberger/php-stopwatch/commits/main)

Precise code execution timer with lap tracking and memory measurement.

Requirements
------------

[](#requirements)

- PHP 8.2+

Installation
------------

[](#installation)

```
composer require philiprehberger/php-stopwatch
```

Usage
-----

[](#usage)

### Quick measurement

[](#quick-measurement)

```
use PhilipRehberger\Stopwatch\Stopwatch;

$result = Stopwatch::measure(function () {
    // Code to measure
    file_get_contents('https://example.com');
});

echo $result->durationFormatted; // "123.45ms"
echo $result->memoryFormatted;   // "1.50KB"
```

### Measurement with return value

[](#measurement-with-return-value)

```
$outcome = Stopwatch::measureWithResult(function () {
    return User::query()->where('active', true)->get();
});

$users = $outcome['result'];
echo $outcome['measure']->durationFormatted; // "45.12ms"
```

### Manual start/stop with laps

[](#manual-startstop-with-laps)

```
$sw = Stopwatch::start('data-pipeline');

// Phase 1
$data = loadData();
$sw->lap('load');

// Phase 2
$transformed = transform($data);
$sw->lap('transform');

// Phase 3
save($transformed);
$sw->lap('save');

$result = $sw->stop();

echo $result->report();
// Stopwatch [data-pipeline]
// -------------------------
// Duration: 1.23s
// Memory:   3.25MB
// Peak:     5.10MB
//
// Laps:
//   load — 200.00ms (cumulative: 200.00ms)
//   transform — 800.00ms (cumulative: 1000.00ms)
//   save — 230.00ms (cumulative: 1230.00ms)
```

### Check elapsed time while running

[](#check-elapsed-time-while-running)

```
$sw = Stopwatch::start();

doSomeWork();

if ($sw->elapsed() > 5000) {
    // Already over 5 seconds, skip remaining work
}

$result = $sw->stop();
```

### Pause and resume

[](#pause-and-resume)

```
$sw = Stopwatch::start();

$data = fetchFromApi();
$sw->pause();  // Pause while waiting for user input

$input = readline('Continue? ');

$sw->resume(); // Resume timing
processData($data, $input);

echo $sw->getElapsedSoFar() . 's elapsed so far';

$result = $sw->stop();
echo $result->durationFormatted; // Excludes time spent paused
```

### Nested stopwatches

[](#nested-stopwatches)

```
$sw = Stopwatch::start('parent');

$child = $sw->child('database');
// ... database queries ...
$childResult = $child->stop();

$result = $sw->stop();

foreach ($result->children() as $child) {
    echo $child->name . ': ' . $child->durationFormatted;
}
```

### Benchmark comparisons

[](#benchmark-comparisons)

```
$result = Stopwatch::benchmark([
    fn () => array_map(fn ($x) => $x * 2, range(1, 1000)),
    fn () => array_walk($arr = range(1, 1000), fn (&$x) => $x *= 2),
], iterations: 100);

echo $result->report();
// Benchmark Results
// ------------------
// #0 — mean: 0.12ms | median: 0.11ms | min: 0.09ms | max: 0.25ms (100 iterations)
// #1 — mean: 0.15ms | median: 0.14ms | min: 0.11ms | max: 0.30ms (100 iterations)
```

### Threshold alerts

[](#threshold-alerts)

```
// Fire a callback when measurement exceeds a threshold
$result = Stopwatch::measureWithThreshold(
    callback: function () {
        processLargeDataset();
    },
    thresholdMs: 500.0,
    onExceeded: function (MeasureResult $result) {
        Log::warning("Slow operation: {$result->durationFormatted}");
    },
);

// Register thresholds on a running stopwatch
$sw = Stopwatch::start();
$sw->onThreshold(1000.0, function (StopwatchResult $result) {
    alert("Operation exceeded 1 second: {$result->durationFormatted}");
});
$sw->onThreshold(5000.0, function (StopwatchResult $result) {
    alert("Critical: operation exceeded 5 seconds!");
});
doWork();
$result = $sw->stop(); // Callbacks fire here if thresholds exceeded
```

### Comparison reports

[](#comparison-reports)

```
$report = Stopwatch::compare([
    'array_map' => fn () => array_map(fn ($x) => $x * 2, range(1, 1000)),
    'foreach' => fn () => array_walk($arr = range(1, 1000), fn (&$x) => $x *= 2),
], iterations: 100);

echo $report->toTable();
// | Name      | Mean    | Median  | Δ vs Best  |
// |-----------|---------|---------|------------|
// | array_map | 0.12ms  | 0.11ms  | baseline   |
// | foreach   | 0.18ms  | 0.17ms  | +50.00%    |

$report->fastest(); // BenchmarkEntry with lowest mean
$report->slowest(); // BenchmarkEntry with highest mean
$report->rankings(); // Sorted by mean ascending
```

### Profiling decorator

[](#profiling-decorator)

```
$proxy = Stopwatch::profile($myService);

// Use the proxy like the original object — all calls are timed
$proxy->fetchUsers();
$proxy->fetchUsers();
$proxy->processData($input);

$profile = Stopwatch::getProfile($proxy);
// ['fetchUsers' => StopwatchStats, 'processData' => StopwatchStats]

foreach ($profile as $method => $stats) {
    echo "{$method}: mean={$stats->mean()}ms, calls={$stats->min()}..{$stats->max()}ms\n";
}

$original = $proxy->getTarget(); // Access the wrapped object
```

### Statistical analysis

[](#statistical-analysis)

```
$sw = Stopwatch::start();
$sw->lap('step-1');
$sw->lap('step-2');
$sw->lap('step-3');
$result = $sw->stop();

$stats = $result->stats();
echo $stats->mean();              // Mean lap duration in ms
echo $stats->median();            // Median lap duration in ms
echo $stats->p95();               // 95th percentile
echo $stats->standardDeviation(); // Standard deviation
```

API
---

[](#api)

MethodReturnsDescription`Stopwatch::start(?string $name)``RunningStopwatch`Start a new stopwatch with an optional name`Stopwatch::measure(callable $fn)``MeasureResult`Measure execution time and memory of a callable`Stopwatch::measureWithResult(callable $fn)``array{result, measure}`Measure while preserving the return value`Stopwatch::benchmark(array $callables, int $iterations)``BenchmarkResult`Run each callable N times and compare performance`Stopwatch::measureWithThreshold(callable, float, callable)``MeasureResult`Measure and fire callback if threshold exceeded`Stopwatch::compare(array $benchmarks, int $iterations)``ComparisonReport`Compare named callables with rankings and ASCII table`Stopwatch::profile(object $target)``ProfilingProxy`Create a profiling proxy for automatic method timing`Stopwatch::getProfile(ProfilingProxy $proxy)``array`Get profiling data from a proxy`RunningStopwatch->onThreshold(float $ms, callable)``self`Register threshold callback fired on stop()`RunningStopwatch->lap(?string $name)``self`Record a lap with an optional name`RunningStopwatch->child(string $name)``RunningStopwatch`Create a nested child stopwatch`RunningStopwatch->stop()``StopwatchResult`Stop the timer and return results`RunningStopwatch->pause()``void`Pause timing without stopping the stopwatch`RunningStopwatch->resume()``void`Resume timing after a pause`RunningStopwatch->getElapsedSoFar()``float`Get elapsed seconds without stopping`RunningStopwatch->elapsed()``float`Get elapsed milliseconds while still running`RunningStopwatch->isRunning()``bool`Check if the stopwatch is still active`RunningStopwatch->isPaused()``bool`Check if the stopwatch is currently paused`StopwatchResult->report()``string`Generate a formatted report with all laps`StopwatchResult->children()``array`Get child stopwatch results`StopwatchResult->stats()``StopwatchStats`Get statistical analysis of lap durations### Value Objects

[](#value-objects)

**StopwatchResult** — `duration` (float, ms), `durationFormatted` (string), `memory` (int, bytes), `memoryFormatted` (string), `peakMemory` (int, bytes), `laps` (array), `name` (?string), `children` (array)

**MeasureResult** — `duration` (float, ms), `durationFormatted` (string), `memory` (int, bytes), `memoryFormatted` (string)

**Lap** — `name` (?string), `duration` (float, ms), `cumulativeDuration` (float, ms)

**BenchmarkResult** — `results()` (array of BenchmarkEntry), `report()` (string)

**BenchmarkEntry** — `mean` (float, ms), `median` (float, ms), `min` (float, ms), `max` (float, ms), `iterations` (int)

**StopwatchStats** — `mean()`, `median()`, `min()`, `max()`, `p95()`, `p99()`, `standardDeviation()` (all float, ms)

**ComparisonReport** — `toTable()` (string), `fastest()` (BenchmarkEntry), `slowest()` (BenchmarkEntry), `rankings()` (array), `toArray()` (array)

**ThresholdMonitor** — `addThreshold(float $ms, callable)` (self), `check(StopwatchResult|MeasureResult)` (void)

**ProfilingProxy** — `getProfile()` (array of StopwatchStats), `getTarget()` (object), `getRawProfile()` (array)

Development
-----------

[](#development)

```
composer install
vendor/bin/phpunit
vendor/bin/pint --test
```

Support
-------

[](#support)

If you find this project useful:

⭐ [Star the repo](https://github.com/philiprehberger/php-stopwatch)

🐛 [Report issues](https://github.com/philiprehberger/php-stopwatch/issues?q=is%3Aissue+is%3Aopen+label%3Abug)

💡 [Suggest features](https://github.com/philiprehberger/php-stopwatch/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement)

❤️ [Sponsor development](https://github.com/sponsors/philiprehberger)

🌐 [All Open Source Projects](https://philiprehberger.com/open-source-packages)

💻 [GitHub Profile](https://github.com/philiprehberger)

🔗 [LinkedIn Profile](https://www.linkedin.com/in/philiprehberger)

License
-------

[](#license)

[MIT](LICENSE)

###  Health Score

42

—

FairBetter than 88% of packages

Maintenance88

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 94.1% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

10

Last Release

85d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/cfd7d24cbbf32400fa13ce0bbe7a31edd2d66a6d4488eafdb3d64c5337bf0435?d=identicon)[philiprehberger](/maintainers/philiprehberger)

---

Top Contributors

[![philiprehberger](https://avatars.githubusercontent.com/u/8218077?v=4)](https://github.com/philiprehberger "philiprehberger (16 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (1 commits)")

---

Tags

performanceprofilingtimerbenchmarkstopwatch

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/philiprehberger-php-stopwatch/health.svg)

```
[![Health](https://phpackages.com/badges/philiprehberger-php-stopwatch/health.svg)](https://phpackages.com/packages/philiprehberger-php-stopwatch)
```

###  Alternatives

[noisebynorthwest/php-spx

A simple &amp; straight-to-the-point PHP profiling extension with its built-in web UI

2.6k1.3k](/packages/noisebynorthwest-php-spx)[ayesh/php-timer

High-resolution and monotonic stop-watch for all your needs. Supports timer start, pause, resume, stop, read, and minimal conversion.

22232.8k12](/packages/ayesh-php-timer)[jsanc623/phpbenchtime

A lightweight benchmark timer and lap profiler for PHP.

2439.3k1](/packages/jsanc623-phpbenchtime)[sandstorm/plumber

Profiling Toolkit for Neos Flow and Neos

364.9k](/packages/sandstorm-plumber)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
