Library / SDK
filp/whoops avatar
filp/whoops

filp/whoops: a stacked error handler for PHP that renders the exception page

PHP errors for cool kids

13,227 stars600 forksPHPMIT

At a glance

What is it?
Whoops is a stand-alone PHP error handler framework whose PrettyPageHandler turns an uncaught exception into a readable page with the trace, frame source and editor links. It suits web developers who want better error output without adopting a full framework.
Who is it for?
Adopt filp/whoops if you run PHP outside a framework that already bundles it, or if you want the same error page inside Laravel, Mezzio, ZubZet or a PSR-7 middleware stack. Do not adopt it as a production error page for end users: PrettyPageHandler exposes trace frames and source code, so you need a separate handler or environment gate for live traffic.
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 last received commits 4 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

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

From daily snapshots

Star history

  • filp/whoops13,227 ★ · -4
Star history
Repository2026-09-222026-11-14
filp/whoops13.2k13.2k

-4 Stars in the last 53 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

What filp/whoops solves, and who it is written for

PHP's default output for an uncaught exception is a short fatal error line with a file and a line number. That is enough to know something broke, and not enough to know why. filp/whoops is an error handler framework that replaces that output with a page listing the exception class, message, stack frames, and the surrounding source of the frame where the failure happened. The README describes it as a "pretty error interface that helps you debug your web projects", built on a "stack-based error handling" system.

The audience is PHP developers working outside a framework that already bundles it. The README states that Laravel 4, Laravel 5.5+, Mezzio and ZubZet 1.2+ ship Whoops already, so those users get the page without installing anything. Everyone else, including people on Silex, Phalcon, CakePHP, Zend, Yii, Slim or Pimple, either follows a community integration or registers the handler directly. The library is stand-alone with, as the README puts it, "currently" no required dependencies, which matters if you are adding it to a legacy application where a new dependency tree is a negotiation.

It is a development aid first. The HTML it produces is designed to be read by the person who wrote the failing code, not by the person using the site.

The stacked handler model inside Whoops\Run

Whoops is not a single error page. It is a dispatcher. You create a Whoops\Run instance, push one or more handlers onto it, and call register(). From that point the Run object takes over PHP's error and exception handling, walks the handler stack in order, and lets each handler decide whether it can deal with the throwable.

The README lists the built-in handlers in the Whoops\Handler namespace: PrettyPageHandler for the HTML page, PlainTextHandler for CLI output, JsonResponseHandler and XmlResponseHandler for machine-readable responses, and CallbackHandler, which wraps any closure or callable. The README notes you do not push CallbackHandler explicitly, because Whoops wraps any closure or callable passed to Whoops\Run::pushHandler automatically. A SOAP handler exists as a separate pluggable package.

That ordering is the design decision worth understanding. Because handlers are consulted in sequence, you can put a JSON handler first and a pretty handler second, and an AJAX request gets JSON while a normal page load falls through to HTML. The same mechanism lets you keep the pretty page for local development and swap in something quieter for production, without changing how the exception is captured.

Installing filp/whoops and registering your first handler

If you are not on one of the frameworks that already includes Whoops, the README gives a two-step install. First pull the package with Composer:

bash
composer require filp/whoops

Then register the pretty handler. The README's example is three statements: construct Whoops\Run, push a PrettyPageHandler, and call register().

php
$whoops = new \Whoops\Run;
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
$whoops->register();

After register() runs, the next uncaught exception in that request should render the Whoops page instead of PHP's fatal error line. The README points to the files under examples/ for more options, and to docs/API Documentation.md for the full API.

If you want the HTML without letting Whoops write it or terminate the process, the README shows how to capture it. Disable quitting and output, then call handleException and take the return value:

php
$whoops = new \Whoops\Run;
$whoops->allowQuit(false);
$whoops->writeToOutput(false);
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
$html = $whoops->handleException($e);

That path is what you want when another layer, a template engine or a test harness, needs to own the response. The README also mentions extending Whoops\Util\SystemFacade to override system calls and passing that instance to the Run constructor.

The editor links, and what the pretty page actually reveals

One documented feature is opening referenced files directly in your editor or IDE, covered in docs/Open Files In An Editor.md. In practice this means a trace frame can be a link that jumps to the exact line in your local checkout rather than a filename you copy by hand. For a developer with a large stack trace this changes the loop from read, search, open, scroll to click.

It is worth being blunt about the other side of that. The page is built from the exception's trace frames and their source. That is the point during development, and it is a disclosure problem in production. The README does not describe a built-in environment check that suppresses the detailed page for live traffic, and it does not document a redaction mechanism for frame data. If you register PrettyPageHandler unconditionally, you have decided that whoever triggers an error sees the internals of the failing code path. The safe pattern is to gate registration on an environment flag or push a different handler first in production, and the README's handler list is what makes that possible.

Handlers for AJAX and CLI, and where Whoops is the wrong tool

The JSON and XML handlers exist because a stack trace rendered as an HTML page is useless to a script. JsonResponseHandler captures exceptions and returns information as a JSON string, and the README names AJAX requests as the intended case. PlainTextHandler does the equivalent for CLI applications, where HTML is noise. If your application has both browser and API surfaces, the handler stack is the mechanism that keeps both readable.

Where Whoops is the wrong choice: it is a display layer for errors, not an error reporting or alerting system. Nothing in the README describes shipping exceptions to an external service, grouping them, or alerting on rates. If your requirement is knowing that production is failing at 3am, Whoops does not do that job, and pairing it with a reporting service is a separate decision.

A second boundary is the framework case. The README says Laravel 4, Laravel 5.5+, Mezzio and ZubZet 1.2+ already have Whoops. Registering your own Whoops\Run in one of those applications means two error handlers competing for the same throwable, and the README does not document how that conflict resolves. Check what the framework already registered before adding your own.

How Whoops differs from a framework's own exception page

The obvious alternative is the exception renderer that ships with your framework, or Symfony's ErrorHandler, which also converts PHP errors and exceptions into a debug page. The difference is scope and coupling. A framework renderer is wired into that framework's container, configuration and request lifecycle, and it assumes you accepted the rest of the framework to get it. Whoops is a stand-alone library with no required dependencies, so it can be dropped into a legacy script, a micro-application or a StackPHP/PSR-7 middleware chain where no framework renderer exists.

The second difference is composability. Whoops exposes the handler stack as a public API: push a JSON handler, push a pretty handler, push a closure. The README's example of capturing HTML with allowQuit(false) and writeToOutput(false) is a good illustration of that posture, since it lets an outer layer own the response. Framework renderers tend to be configured rather than composed.

The trade-off runs the other way too. A framework renderer knows about your container and can show bound services, routes and configuration values. Whoops shows the exception, the trace and the frame source, and leaves the framework-specific context to whoever integrates it. If that context is what you rely on to debug, the framework page is the better tool.

Maintenance, licence and the cost of upgrading

The repository is not archived, and the last push was on 2026-11-02. That is a recent commit, so the project is not dormant. The README names Filipe Dobreira as the primary author and Denis Sokolov as the current maintainer, with Graham Campbell and Markus Staab credited for continuous participation, which tells you the maintenance is a small, named group rather than a large organisation.

On cost: the repository has no published GitHub releases, so there is no release history to judge how disruptive upgrades have been historically. The repository does contain a CHANGELOG.md, which is where you would look before bumping the constraint. Because Whoops is stand-alone with no required dependencies, a version bump does not drag a dependency tree with it, which keeps the upgrade surface small. The integration points that can break are the ones you wrote: custom handlers, a subclassed Whoops\Util\SystemFacade, or a framework bridge package maintained by someone else.

The licence is MIT. That permits commercial and closed-source use, and it requires preserving the copyright notice and licence text; the repository carries LICENSE.md. This is a description of the licence terms, not legal advice, and if your organisation has a policy on third-party licences, run it through that.

Editorial conclusion

Adopt filp/whoops if you run PHP outside a framework that already bundles it, or if you want the same error page inside Laravel, Mezzio, ZubZet or a PSR-7 middleware stack. Do not adopt it as a production error page for end users: PrettyPageHandler exposes trace frames and source code, so you need a separate handler or environment gate for live traffic. Before wiring it in, check the handler list in the README against your response format (HTML, JSON, XML) and confirm whether your framework already ships Whoops, because registering a second Run instance will fight the first one.

Frequently asked questions

How do I set up filp/whoops in a plain PHP project?

Install it with composer require filp/whoops, then create a Whoops\Run instance, push a Whoops\Handler\PrettyPageHandler onto it, and call register(). After that, uncaught exceptions render as the Whoops error page instead of PHP's fatal error output.

How do I use filp/whoops without letting it terminate the request?

The README shows calling allowQuit(false) and writeToOutput(false) on the Run instance, pushing the PrettyPageHandler, and then calling handleException, which returns the generated HTML for you to process yourself.

What is filp/whoops?

It is an error handler framework for PHP. Out of the box it provides a pretty error interface for debugging web projects, and underneath it is a stack-based error handling system with handlers for HTML, plain text, JSON and XML output.

Official sources

  1. filp/whoops on GitHub
  2. Issues
  3. License: MIT
  4. Project website
  5. README
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/filp-whoops.svg)](https://hysenlabs.com/projects/filp-whoops)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/filp-whoops.svg)](https://hysenlabs.com/projects/filp-whoops)

Was this useful?