phphd/exception-toolkit
A PHP/Laravel exception toolkit for centralizing error handling: customize reporting, rendering, logging, and response formatting for exceptions. It helps turn thrown errors into consistent, actionable responses and cleaner application code.
The 1.2.0 release exposes two static entry points on Phd\ExceptionToolkit\PhdExceptionToolkitExtension — getContainer() and configureContainer() — which means you no longer need to wire the extension into a container by hand just to get a working error renderer.
Minimal setup:
composer require phphd/exception-toolkit
use Phd\ExceptionToolkit\PhdExceptionToolkitExtension;
$container = PhdExceptionToolkitExtension::getContainer();
PhdExceptionToolkitExtension::configureContainer($container);
Where to look first, in order:
PhdExceptionToolkitExtension — the only class you should need to touch in most apps; it is now both the accessor and the configuration seam.\Throwable where possible.First use case to try: early-stage error rendering. Laravel's handler only exists once the framework has booted, so bootstrap failures, artisan commands that die before the app container is ready, and standalone CLI/worker scripts are where this package earns its place. Point it at a container, configure it, and let it render the exception before Laravel is in the picture.
Bootstrap-time handler (the 1.2.0 sweet spot). Now that getContainer() is public, you can drop the toolkit into a script entrypoint without a DI bootstrap step:
// e.g. bin/worker, or the top of bootstrap/app.php before the app is built
$container = PhdExceptionToolkitExtension::getContainer();
PhdExceptionToolkitExtension::configureContainer($container);
set_exception_handler(fn (Throwable $e) => $container->get(Renderer::class)->render($e));
Configure once, in a service provider. For normal Laravel apps, call configureContainer() from a register() method and keep getContainer() out of request-handling code. Treat configuration as a boot-time concern, not a runtime one.
Custom renderers/handlers via the container. The intended extension path is to register your own implementations into the container returned by getContainer() before anything resolves them. This keeps your renderers testable as plain classes with constructor injection, rather than as statically-registered closures.
Narrow-scope adoption. Don't replace Laravel's Exceptions handler wholesale. The pragmatic integration is: let Laravel handle HTTP and job exceptions as normal, and use the toolkit for the slice Laravel cannot see — bootstrap, pre-container CLI, health-check scripts. Where you do bridge the two, do it from Exceptions::report()/render() as a fallback, not from a global set_exception_handler() that shadows the framework.
Testing pattern. Because configuration lives on a static extension, make the container/config setup an explicit test fixture step (a trait's setUp call) rather than relying on it being configured by a provider, so unit tests don't depend on the full app booting.
getContainer() is a static accessor; anything you configureContainer() persists for the process. In a long-running worker (Octane, queues) a per-request reconfiguration will accumulate or leak between jobs. Configure once, in a boot-time location, and assert that in review.app(). app() bindings, facades, and config() are not available inside handlers resolved from it — pass plain scalars or pre-resolved services in instead.APP_DEBUG; the toolkit has no reason to know your environment, so gate it explicitly.getContainer()/configureContainer() are new public surface in 1.2.0 — if you're writing against them, constrain to ^1.2. Semver suggests non-breaking, but a static accessor is exactly the kind of thing that gets reshaped.PhdExceptionToolkitExtension itself; you'll be fighting the static entry points.How can I help you explore Laravel packages today?