Framework
phpbench/phpbench avatar
phpbench/phpbench

PHPBench turns PHP snippets into repeatable timing and memory numbers

PHP Benchmarking framework

2,028 stars133 forksPHPMIT

At a glance

What is it?
PHPBench is a Composer-installed benchmark runner for PHP that repeats code in revolutions, samples those revolutions in iterations, and reports time, memory and assertions. Its documented process isolation covers the iteration, and the README says nothing about isolating individual revolutions.
Who is it for?
PHPBench earns its place when a PHP change needs a timing or memory figure you can defend in review, and it is the wrong instrument when you want to know why code is slow. Its statistical model is honest about the cost: revolutions are cheap to repeat, iterations pay for a separate process each time.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What execution time and memory numbers this tool is built to produce

PHPBench answers the question a correctness test cannot: is this PHP code slower than it was last month. Its own comparison fixes the audience. The README calls it a benchmark runner for PHP analogous to PHPUnit but for performance rather than correctness, which puts it in the hands of library maintainers and framework contributors who need a defensible number attached to a pull request. You write classes that name the code under test, state how many times to run it, and get back execution time, memory figures for the benchmarking subject, and a verdict from an assertion. The feature list runs to 7 items: revolutions, iterations, process isolation, reporting, report storage and comparison, memory usage, and assertions. None of them profiles a running application. The unit of work is code you name, not a request you send.

Revolutions sampled by iterations, with process isolation at one level only

Two nested counters do all the work, and the naming is where newcomers get lost. Revolutions repeat your code many times to determine average execution time. Iterations sample your revolutions many times and review aggregated statistical data. An iteration is therefore a batch, and the statistics describe that batch rather than any single execution. The process isolation feature says each iteration is executed in a separate process, and that one line is the whole difference from a hand written timing loop: static state, a warmed opcache and a loaded autoloader are paid for once per iteration instead of leaking into the next sample. It also sets a cost floor. Every extra iteration buys a cleaner sample at the price of another process launch, and the documentation gives no guidance on choosing the balance between the two. The README shows no command line flags for either counter.

Installing the development dependency and finding the entry point afterwards

The README gives one install line and points at hosted documentation for everything else. As a development dependency of an existing project, that line is the whole install:

bash
composer require phpbench/phpbench --dev

The `--dev` flag is part of the command rather than advice around it, so the runner lands in the require-dev set and not in what a production deploy installs. Two things follow from that one line. The installation page at `http://phpbench.readthedocs.io/en/latest/installing.html` is where the other options live, and the README names no benchmark run command, no example invocation and no sample output. The repository fills that gap with 8 directories under `examples/`, including `examples/Benchmark/` and `examples/Assertion/`, plus a shared `examples/bootstrap.php`. Read those files alongside the installation page before writing your first benchmark class, because the invocation has to come from one of them.

Assertions that turn a slower build into a failed run

Assertions are the part of PHPBench that leaves a developer's terminal and lands in continuous integration. The feature line says you can assert that code is performing within acceptable limits, or that it has not regressed from a previously recorded baseline. Those are 2 different questions: an absolute ceiling you write yourself, and a comparison against numbers stored on an earlier run. Report storage and comparison is what makes the second one possible, because you can keep benchmarks locally and use them as a baseline reference later. The catch is that a baseline is only worth what the machine that produced it was worth. The README does not document any metadata attached to a stored report, so the PHP version, the extension set and the host are yours to record by hand. Compare a slow run against a fast baseline and you learn which box was faster, not which change caused it.

Console, CSV, Markdown and HTML reports you can keep locally

Reporting is customizable and 4 output formats are named: console, CSV, Markdown and HTML. The screenshots show 5 shapes of the same data, including a run compared against a baseline, an aggregated report, an HTML bar chart, its console counterpart, and a fifth one labelled the Blinken logger. The choice between them matters more than it looks. A console bar chart is for one developer reading a change on their own machine, CSV and Markdown exist to be committed or pasted into a pull request, and HTML is the artifact that survives a build server. One run can feed several of these at once, so the decision belongs in project configuration rather than in shell history. Memory usage is measured on benchmarking subjects alongside time, which means the aggregated report carries both without a second harness.

The shipped config, a JSON schema, and an Alpine image pinned to 8.4.0RC2

Two files sitting at the root of the repository describe what PHPBench considers a valid configuration for a project: `phpbench.json` for the project and `phpbench.schema.json` for its shape. Shipping a schema means a mistyped key can be caught by an editor before a benchmark runs, and the tree also holds `phpstan.neon.dist` beside `phpstan-baseline.neon`, so the project's own static analysis carries a recorded set of accepted findings. The container that runs it is a single file:

yaml
services:
  php:
    image: php:8.4.0RC2-zts-alpine3.20
    volumes:
      - .:/app

That image is a candidate build of PHP 8.4 rather than a stable one, the thread safe variant, on Alpine 3.20, with the working tree mounted at `/app`. A candidate tag is the detail worth pausing on: the development container is not pinned to a stable PHP build, so what you reproduce inside it can differ from what a production host runs. The tree also carries `flake.lock` and `flake.nix` as a Nix flake pair, plus `box.json.dist` next to a second box configuration used for builds made on GitHub. The repository is MIT licensed, which takes the licence question off the table. It is not archived, and the last push to master landed on 2026-06-14.

PHPUnit and PHPBench sitting in the same tree

PHPUnit is the comparison the README itself makes, and the difference in approach is the point of the comparison. PHPUnit executes code once per test case and asserts on values, so a failure names a wrong answer. PHPBench executes code many times per case, keeps every repetition, and asserts on elapsed time, memory and change against a baseline, so a failure names a slow answer. Neither does the other's job. A correctness suite cannot report that a serializer got slower, and no amount of timing will report that a function returns the wrong array. The repository runs both against itself: `phpunit.xml.dist` configures PHPUnit over `tests/`, `lib/` holds the runner, and `.php-cs-fixer.dist.php` and `rector.php` cover style and automated refactoring. Two separate CI workflows, `ci.yaml` and `documentation.yaml`, keep the test run and the docs build apart.

What the documentation never settles before you trust a number

Three gaps are worth knowing before a result reaches a pull request. The README names no default iteration count and no default revolution count, so a benchmark class copied from the examples carries whatever numbers its author typed into it, and identical code measured on a slow laptop and a fast desktop produces different figures for reasons that have nothing to do with the change. Nothing is documented about what a harness should do when an assertion fails in continuous integration, and there is no documented retry, quarantine or override path, so an unlucky baseline becomes a problem to solve by hand. And the process isolation guarantee is stated for iterations only, which leaves benchmark code that writes files or opens sockets outside the boundary the runner controls.

Editorial conclusion

PHPBench earns its place when a PHP change needs a timing or memory figure you can defend in review, and it is the wrong instrument when you want to know why code is slow. Its statistical model is honest about the cost: revolutions are cheap to repeat, iterations pay for a separate process each time. Before trusting any figure, read the iteration and revolution counts in your own benchmark classes, because the README sets no defaults and documents no rollback path for an assertion that fails in continuous integration.

Frequently asked questions

What is PHPBench and who is it for?

PHPBench is a benchmark runner for PHP, described by its own README as analogous to PHPUnit but for performance rather than correctness. It suits developers who need execution time, memory usage and regression assertions for code they control.

How do I install PHPBench?

The README gives one command, `composer require phpbench/phpbench --dev`, which installs it as a development dependency of an existing project. It refers readers to the installation page on readthedocs for other options.

Does PHPBench run every measurement in a separate process?

Process isolation is documented per iteration, and each iteration is executed in a separate process. Revolutions are the repeated executions sampled inside an iteration, and the README does not state that they are isolated from each other.

Which report formats does PHPBench support?

The documented output formats are console, CSV, Markdown and HTML. Reports can be stored locally and reused later as a baseline reference.

Can PHPBench fail a build when code gets slower?

Assertions check that code stays within acceptable limits and that it has not regressed from a previously recorded baseline. How the harness behaves when such an assertion fails in continuous integration is not documented.

Where can I find example PHPBench benchmarks?

The repository ships 8 directories under `examples/`, among them `examples/Benchmark/`, `examples/Assertion/`, `examples/Annotations/` and `examples/Attributes/`, plus a shared `examples/bootstrap.php`. The README links to readthedocs for anything beyond those files.

Official sources

  1. Issues
  2. License: MIT
  3. phpbench/phpbench on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/phpbench-phpbench.svg)](https://hysenlabs.com/projects/phpbench-phpbench)

Was this useful?