wernerdweight/enhanced-exception
Enhanced Exception is a Laravel/PHP package that makes exceptions more informative, adding extra context and cleaner output to simplify debugging and error reporting in your applications.
wernerdweight/enhanced-exception is not a framework extension — it ships a single base exception class you extend in your own code. There is no service provider, no config file, and no facade. Treat it as a tiny piece of scaffolding you copy into a project, not a feature you wire up.
composer require wernerdweight/enhanced-exception
First place to look: vendor/wernerdweight/enhanced-exception/src/. It is one file. Read it end to end (a two-minute job) before using it — the entire value of the package is whatever that class adds on top of \Exception, most likely a context/data payload plus convenience constructors. Confirm the exact method names and their signatures against the version you install; the package is at 1 star with 0 dependents and a single 2023 release, so the README is not going to save you.
First use case: replace a raw throw new \RuntimeException('...') in a domain service with a named exception that carries a context array, so your Laravel log entries and report() calls have structured, searchable data attached instead of a concatenated string message:
use Wernerdweight\EnhancedException\EnhancedException;
final class PaymentDeclinedException extends EnhancedException
{
}
Then throw PaymentDeclinedException::withContext('Card declined', ['gateway' => 'stripe', 'code' => $code]); (substitute the actual constructor/factory the class exposes) and let your existing Handler log it.
Build a thin domain hierarchy. The only pattern that makes sense for a base-exception package is one layer of abstraction. Define per-bounded-context base classes (DomainException, InfrastructureException) extending EnhancedException, then concrete leaves below those. This lets Laravel's handler dispatch on the middle layer while concrete classes stay small.
Attach context at the throw site, not at the catch site. The whole point of an enhanced exception is that the frame that knows the values is the frame that throws. Push IDs, codes, and correlation tokens into the exception when it is constructed; catching code should not be reaching back into request state to reconstruct context.
Wrap third-party exceptions with previous. When translating a GuzzleHttp\Exception\ConnectException into your domain exception, pass the original as the previous exception so stack traces survive. Combine with context so the log line has both the structured payload and the original trace.
Name exceptions after failure states, not after operations. InvoiceAlreadyPaidException reads better in logs and is easier to match with instanceof than InvoiceServiceException, and it makes ->render() overrides trivially readable.
Use it as the boundary for user-facing rendering. In App\Exceptions\Handler, match on your base type and map to an HTTP status and JSON payload. Because your leaves share one parent, you can add a single clause covering all "expected" failures and reserve 500s for everything else.
Keep the class out of your public API. If you author packages too, don't leak EnhancedException in your own signatures — catch and rethrow as your own type, or you will drag a 1-star dependency into every downstream composer.json.
declare(strict_types=1), PHP version constraints in composer.json, and whether it supports the PHP version you deploy. If it is only a handful of lines, vendor the class into your own namespace and drop the dependency; you lose nothing and gain control over serialization and typing.storage/logs/laravel.log verbatim. Strip tokens, passwords, and full request bodies before throwing.instanceof ordering matters in the handler. Match your most specific exceptions first; PHP evaluates your match/if chain top-down and a broad DomainException clause will swallow narrower ones.failed() paths), re-check behaviour after PHP upgrades — base \Exception serialization changed subtly in 8.1, and a package last touched in early 2023 may not have been tested against newer runtimes.Handler — not in the package.How can I help you explore Laravel packages today?