phisys/exceptions-bundle
PhiSys Exceptions Bundle is a Symfony package for centralized exception handling and consistent error responses. It provides configurable handlers for web and API applications, helping teams standardize error reporting and simplify debugging.
Validate the package identity before you install anything. The repository is phisys/exceptions-bundle — a Symfony bundle for dosfarma/exceptions integration. The description.md provided here describes a "Laravel-native" package with publishable config and migrations; that text does not match the repository name, the dependency (dosfarma/exceptions), or an exceptions-bundle naming convention. Treat the Laravel framing as inaccurate until you have read the actual composer.json and src/. If you are on Laravel, this is probably not the package you were told it is.
Assuming you are on Symfony:
composer require phisys/exceptions-bundle (it will pull dosfarma/exceptions as a transitive dependency — check the constraint it resolves to, since both are old).config/bundles.php automatically; otherwise add Phisys\ExceptionsBundle\PhisysExceptionsBundle::class => ['all' => true].src/PhisysExceptionsBundle.php (what it actually registers), src/DependencyInjection/*Extension.php + Configuration.php (what is configurable — assume the surface is small), and the dosfarma/exceptions base classes in vendor/dosfarma/exceptions/src. There is no meaningful README, so the source is the documentation.config/packages/ if the extension exposes a tree. If Configuration.php has no nodes, there is nothing to configure — the bundle is wiring only, and your behavior lives in the dosfarma classes.Wrap vendor exceptions behind your own base class. Define App\Exception\AppException extends Dosfarma\...\BaseException (or whatever the vendor root is) and throw only your classes from application code. This is the single cheapest exit strategy: when the bundle goes stale — and at 0 stars, one release in 2021, it will — you swap the parent class in one file instead of grepping hundreds of throw statements.
Drive rendering through a kernel.exception listener, not scattered try/catch. The idiomatic Symfony shape is one listener that inspects the thrown exception, maps it to a status code and a response body, and delegates to content negotiation. If the bundle already registers such a listener, decorate or extend it; if it does not, write your own and treat the bundle purely as a source of exception types.
Keep the mapping table in config, not in conditionals. A flat array of ExceptionClass => ['status' => 422, 'code' => 'VALIDATION_FAILED'] in config/packages/ is testable, greppable, and reviewable in a diff. Hardcoded instanceof chains in a listener rot fast.
Use the framework-agnostic base classes for CLI and queue consumers. One of the few genuinely useful properties of dosfarma/exceptions being a separate package is that typed exceptions carry meaning outside the HTTP kernel — a console command or Messenger handler can throw the same NotFound and let its own boundary decide how to surface it, rather than importing Symfony HTTP concerns into domain code.
Attach context at throw time, not at catch time. If the base classes support a context payload, populate it with correlation IDs, resource IDs, and the operation name where the error originates. By the time the listener sees it, the relevant locals are gone and you are reconstructing the story from stack traces.
Separate API and HTML rendering at the listener. For JSON endpoints return a stable envelope (error.code, error.message, error.context); for HTML let Symfony's error controller take over unless you have a real reason to override. Mixing the two in one code path produces JSON leaking into browser tabs.
Write one integration test that exercises the full path — throw each mapped exception through a test client (static::createClient() + a route that throws) and assert status + body shape. This is the upgrade canary: when a Symfony major forces a fork or replacement, this test tells you exactly what broke.
composer.json before you write a line of integration code or a proposal.composer.json constraints to block Symfony 6/7 and PHP 8.2+ resolution. You can often loosen a constraint in your root composer.json and let it resolve, but only after reading the source to confirm it does not use APIs removed since — and only if you are prepared to own it.prod, bin/console cache:clear (or a deploy that warms cache) is required before mapping changes take effect. Debugging "my new status code isn't applied" in a container that skipped cache warmup is a classic wasted hour.ErrorListener and the exception event have ordering; if your listener does not fire, check getSubscribedEvents priority and whether a kernel.exception listener registered earlier called setResponse() and stopped propagation. bin/console debug:event-dispatcher kernel.exception shows the actual ordering.services.yaml service decoration on the bundle's handler rather than editing vendor/. Forking is the signal you have outgrown the package, not a first-line customization technique — at that point bring a build-vs-buy recommendation rather than quietly absorbing maintenance.createMock signatures, or @expectedException patterns that break under PHPUnit 10+/PHP 8.3. If the test suite in vendor/ is dead, your integration test becomes the only safety net.Handler + a small base class) instead.How can I help you explore Laravel packages today?