{"id":20314956,"url":"https://github.com/phphd/exception-handler","last_synced_at":"2026-02-06T23:34:42.055Z","repository":{"id":229315772,"uuid":"760431164","full_name":"phphd/exception-handler","owner":"phphd","description":"Bundle implementing exception handler via Symfony Messenger component","archived":false,"fork":false,"pushed_at":"2025-01-11T19:37:11.000Z","size":36,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-11-18T06:24:17.418Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/phphd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-02-20T12:09:46.000Z","updated_at":"2025-02-20T09:28:52.000Z","dependencies_parsed_at":"2024-10-14T19:08:20.237Z","dependency_job_id":"75bc9fb8-9d75-404e-9304-d3536fc9d075","html_url":"https://github.com/phphd/exception-handler","commit_stats":null,"previous_names":["phphd/exception-handler-bundle","phphd/exception-handler"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/phphd/exception-handler","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phphd%2Fexception-handler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phphd%2Fexception-handler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phphd%2Fexception-handler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phphd%2Fexception-handler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/phphd","download_url":"https://codeload.github.com/phphd/exception-handler/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phphd%2Fexception-handler/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29180504,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-06T23:15:33.022Z","status":"ssl_error","status_checked_at":"2026-02-06T23:15:09.128Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-14T18:17:23.751Z","updated_at":"2026-02-06T23:34:42.039Z","avatar_url":"https://github.com/phphd.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Exception Handler\n\n🧰 PHP Exception Handler built on top of [Symfony Messenger](https://symfony.com/doc/current/messenger.html) component. \nProvides middlewares that allow you to easily re-raise exceptions, chain them, or handle with a dedicated handler.\n\n[![Build Status](https://img.shields.io/github/actions/workflow/status/phphd/exception-handler/ci.yaml?branch=main)](https://github.com/phphd/exception-handler/actions?query=branch%3Amain)\n[![Codecov](https://codecov.io/gh/phphd/exception-handler/graph/badge.svg?token=GZRXWYT55Z)](https://codecov.io/gh/phphd/exception-handler)\n[![Psalm coverage](https://shepherd.dev/github/phphd/exception-handler/coverage.svg)](https://shepherd.dev/github/phphd/exception-handler)\n[![Psalm level](https://shepherd.dev/github/phphd/exception-handler/level.svg)](https://shepherd.dev/github/phphd/exception-handler)\n[![Packagist Downloads](https://img.shields.io/packagist/dt/phphd/exception-handler.svg)](https://packagist.org/packages/phphd/exception-handler)\n[![Licence](https://img.shields.io/github/license/phphd/exception-handler.svg)](https://github.com/phphd/exception-handler/blob/main/LICENSE)\n\n## Installation 📥\n\n1. Install via composer\n\n    ```sh\n    composer require phphd/exception-handler\n    ```\n\n2. Enable the bundle in the `bundles.php`\n\n    ```php\n    PhPhD\\ExceptionHandler\\Bundle\\PhdExceptionHandlerBundle::class =\u003e ['all' =\u003e true],\n    ```\n\n## Configuration ⚒️\n\nTo leverage features of this bundle, you should add `phd_exception_handler` middleware to the list:\n\n```diff\nframework:\n    messenger:\n        buses:\n            command.bus:\n                default_middleware: false\n                middleware:\n+                   - phd_exception_handler\n                    - validation\n                    - doctrine_transaction\n```\n\nThe core principle of exception handling revolves around the idea that exceptions are dispatched to the corresponding\nbus to be handled. There must be one exception bus per one origin bus.\n\nThe exception bus name convention is straightforward: `command.bus` exceptions are forwarded\ninto `command.exception.bus`.\n\n```yaml\nframework:\n    messenger:\n        buses:\n            command.exception.bus:\n                default_middleware: false\n                middleware:\n                    - phd_exception_rethrow_unhandled\n                    - phd_exception_chaining\n                    - phd_exception_result_filter\n                    -   handle_message:\n                            - true\n```\n\nCurrently, there are few exception handling middlewares provided.\n\n### Rethrowing unhandled\n\nMiddleware: `phd_exception_rethrow_unhandled`\n\nIn case if dispatched exception had not been handled it is rethrown back. The exception is considered as handled if\nhandler returns a response, or throws another exception.\n\n### Exception chaining\n\nMiddleware: `phd_exception_chaining`\n\nImplements automatic exceptions escalation logic with `#[RaiseAs]` attribute.\n\n### Result filter\n\nMiddleware: `phd_exception_result_filter`\n\nFilters out all null results of exception handlers.\n\n## Usage 🚀\n\n### Re-Raising Exceptions\n\nThe simplest use-case is defining `#[RaiseAs]` attribute on your exception class:\n\n```php\nuse PhPhD\\ExceptionHandler\\Middleware\\Chain\\Escalator\\RaiseAs;\n\n#[RaiseAs(AccessDeniedHttpException::class, bus: 'api.exception.bus')]\nfinal class NonWhiteListedUserException extends DomainException\n{\n}\n```\n\nIn this example, any time `NonWhiteListedUserException` is thrown from an underlying handler,\nit will be raised as `AccessDeniedHttpException`.\n\nAs you can see, there's required attribute bus option. Since some exceptions could be thrown from multiple different\ncontexts (hence, different buses), it is required to explicitly specify the bus from which the particular exception\nmust be raised, so that in other scenarios another exceptions could be escalated:\n\n```php\nuse PhPhD\\ExceptionHandler\\Middleware\\Chain\\Escalator\\RaiseAs;\n\n#[RaiseAs(ImportLockedHttpException::class, bus: 'api.exception.bus')]\n#[RaiseAs(RecoverableMessageHandlingException::class, bus: 'consumer.exception.bus')]\nfinal class ImportLockedException extends RuntimeException\n{\n}\n```\n\nIn this example, `ImportLockedException` could be thrown either in http context (`api.bus`), or in the mq consumer\ncontext (`consumer.bus`). Therefore, raised exceptions are different.\n\n### Manual Handling\n\nThe exception is dispatched down to your custom handlers, where you could either return a Response, throw a new\nexception, or just log it and return `null` so that exception will be re-thrown again.\n\n```php\n#[AsMessageHandler('api.exception.bus')]\nfinal readonly class InventoryExceptionHandler\n{\n    /** @throws Throwable */\n    public function __invoke(InventoryDomainException $exception, InventoryCommand $command): ?Response\n    {\n        if ($exception instanceof JournalHasUnInventoriedItemException) {\n            $data = $this-\u003eformatJournalException($exception);\n\n            return new JsonResponse($data, Response::HTTP_BAD_REQUEST);\n        }\n\n        if ($exception instanceof StockItemNotVaildatedException) {\n            $data = $this-\u003eformatItemException($exception, $command-\u003egetJournal());\n\n            throw new StockItemNotValidatedHttpException($data, $exception);\n        }\n\n        return null;\n    }\n}\n```\n\nIf you would like to use the same exception handler for multiple exception buses, you can do so by adding multiple\n`#[AsMessageHandler]` attributes:\n\n```php\n#[AsMessageHandler(bus: 'command.exception.bus')]\n#[AsMessageHandler(bus: 'query.exception.bus')]\nfinal readonly class InventoryExceptionHandler\n{\n    // ...\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphphd%2Fexception-handler","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fphphd%2Fexception-handler","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphphd%2Fexception-handler/lists"}