A zero-dependency, single-class PHP profiler for measuring elapsed time and memory usage between named checkpoints.
PerformanceMeter is intentionally minimal — a single final class with a handful of static methods that works without any other dependency. It exists to fill a specific niche:
Quick, single-file timing checks where pulling in a full profiling library would be overkill.
It is not a replacement for full-featured profilers; it is the cheapest possible thing that lets you answer "how long did this block take and how much memory did it use?".
- One-off benchmarking scripts and microbenchmarks
- CLI tools and cron jobs where you want a quick elapsed-time print at the end
- Tutorial / educational code where introducing a heavier dependency would obscure the lesson
- Library examples and reproduction scripts in bug reports
- Hot-path probes during local development, when adding a
composer requireround-trip is friction
| Need | Use instead |
|---|---|
| Application-level profiling with nested sections, periods, categories | symfony/stopwatch |
| Production profiling, flame graphs, call tree analysis | Xdebug profiler, Blackfire, Tideways, SPX |
| Web request profiling with timeline UI | Symfony's WebProfilerBundle, Laravel Telescope, Clockwork |
| Memory leak hunting with object retention graphs | Xdebug + xdebug_debug_zval, or memprof |
| OpenTelemetry / APM integration | open-telemetry/sdk and an APM vendor SDK |
If you are reaching for any of the above, this package is not the right tool — and that is by design.
- PHP 8.1 or higher
- No runtime dependencies
composer require initphp/performance-meterYou can also include src/PerformanceMeter.php (and src/Exception/PointerNotFoundException.php) manually if you cannot use Composer — the package has no transitive dependencies.
require_once'vendor/autoload.php';
useInitPHP\PerformanceMeter\PerformanceMeter;
PerformanceMeter::setPointer('main');
for ($i = 0; $i <= 1000; $i++) {
usleep(10);
}
PerformanceMeter::setPointer('mainEnd');
echo PerformanceMeter::elapsedTime('main', 'mainEnd', 3) . ' seconds elapsed' . PHP_EOL;
echo PerformanceMeter::memoryUsage('main', 'mainEnd', 2) . ' memory used' . PHP_EOL;
// Example output:// 0.015 seconds elapsed// 0.77KB memory usedWhen you only pass a starting checkpoint, the second argument defaults to "now":
PerformanceMeter::setPointer('boot');
// ... do work ...echo PerformanceMeter::elapsedTime('boot') . ' seconds since boot' . PHP_EOL;mark($name) is a one-to-one alias of setPointer($name) for readers who prefer stopwatch-style vocabulary:
PerformanceMeter::mark('before');
heavy_work();
PerformanceMeter::mark('after');
echo PerformanceMeter::elapsedTime('before', 'after');More usage patterns — peak memory, comparing two implementations, resetting between runs — live in docs/cookbook.md.
| Method | Purpose |
|---|---|
setPointer(string $name): void | Record a checkpoint with the current time + memory. Case-insensitive. |
mark(string $name): void | Alias of setPointer(). |
elapsedTime(string $start, ?string $end = null, int $decimal = 4): float | Seconds between two checkpoints. $end = null ⇒ "now". |
memoryUsage(string $start, ?string $end = null, int $decimal = 2, bool $realUsage = false): string | Memory delta, formatted as "x.xxKB" or "x.xxMB". |
peakMemoryUsage(int $decimal = 2, bool $realUsage = false): string | Peak memory used so far by the process. |
has(string $name): bool | Whether a checkpoint with that name has been recorded. |
getPointers(): array | Snapshot copy of every recorded checkpoint. |
reset(): void | Clear all checkpoints. |
elapsedTime() and memoryUsage()throwInitPHP\PerformanceMeter\Exception\PointerNotFoundException when $start (or a non-null $end) does not match a recorded checkpoint.
Full reference with parameter notes, error conditions and runnable examples: docs/api-reference.md.
docs/getting-started.md— install, first measurement, conceptual modeldocs/api-reference.md— every public method, parameter by parameterdocs/cookbook.md— real-world recipes (CLI benchmarks, cron timing, A/B comparisons, peak memory tracking, v1 → v2 migration)
v2.0 is a clean break that fixes real bugs and tightens the API. Most callers only need to upgrade PHP.
| Area | v1 behaviour | v2 behaviour | Action |
|---|---|---|---|
| PHP requirement | >=7.4 | ^8.1 | Upgrade your runtime. |
Missing $startPoint | Silently returned ~0 ("now" – "now") | Throws PointerNotFoundException | Wrap in try/catch or call PerformanceMeter::has() first. |
Missing non-null $endPoint | Silently fell back to "now" | Throws PointerNotFoundException | Same — fix the typo or check with has(). |
memoryUsage() with a freed-memory delta ≥ 1 MB | Reported in KB (broken) | Reports correctly in MB with sign | No code change; output now matches expectations. |
decimal < 0 | Accepted, produced odd output | Throws InvalidArgumentException | Pass decimal >= 0. |
Subclassing PerformanceMeter | Allowed (pointless — all-static) | Blocked (final) | Compose, do not inherit. |
protected static $pointers | Visible to subclasses | private | Use getPointers() / has() / reset(). |
New: reset(), has(), peakMemoryUsage(), getPointers() | — | Added | Opt-in. |
A migration cookbook entry with side-by-side diffs lives in docs/cookbook.md.
This package follows the org-wide InitPHP contribution guide — PSR-12, declare(strict_types=1);, PHPStan at the configured level, PHPUnit-tested behaviour changes, Conventional Commits.
Locally:
composer install
composer test# PHPUnit
composer phpstan # static analysis
composer cs-check # coding standards (use cs-fix to apply)
composer qa # all of the aboveCI runs on PHP 8.1 → 8.4 against both highest and lowest installable dependencies.
Please report security issues privately — see the org-wide SECURITY.md. Do not open public issues for vulnerabilities.
Released under the MIT License. Copyright © 2022-2026 InitPHP.