{"id":36362652,"url":"https://github.com/nuwber/rabbitevents","last_synced_at":"2026-04-08T14:00:48.943Z","repository":{"id":26347372,"uuid":"108104505","full_name":"nuwber/rabbitevents","owner":"nuwber","description":"Nuwber's RabbitEvents provides a simple observer implementation, allowing you to publishing and handling for various events that occur in your applications. For example, if you need to react to some event occurred in another API.","archived":false,"fork":false,"pushed_at":"2026-01-30T18:38:48.000Z","size":19092,"stargazers_count":121,"open_issues_count":1,"forks_count":34,"subscribers_count":9,"default_branch":"9.x","last_synced_at":"2026-01-31T11:23:43.939Z","etag":null,"topics":["event-driven","laravel","microservices","php","publish-subscribe","rabbitmq"],"latest_commit_sha":null,"homepage":"https://nuwber.github.io/rabbitevents/","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/nuwber.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"masterjus","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2017-10-24T09:21:11.000Z","updated_at":"2025-09-29T16:43:00.000Z","dependencies_parsed_at":"2023-12-29T11:27:05.861Z","dependency_job_id":"904715bd-4b40-4e4c-b8d7-33c7536f1dd5","html_url":"https://github.com/nuwber/rabbitevents","commit_stats":{"total_commits":95,"total_committers":17,"mean_commits":5.588235294117647,"dds":0.5473684210526315,"last_synced_commit":"b98420da82c76194ea69f83c33f8bd9f4cb18e22"},"previous_names":[],"tags_count":70,"template":false,"template_full_name":null,"purl":"pkg:github/nuwber/rabbitevents","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nuwber%2Frabbitevents","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nuwber%2Frabbitevents/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nuwber%2Frabbitevents/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nuwber%2Frabbitevents/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nuwber","download_url":"https://codeload.github.com/nuwber/rabbitevents/tar.gz/refs/heads/9.x","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nuwber%2Frabbitevents/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31558386,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T10:21:54.569Z","status":"ssl_error","status_checked_at":"2026-04-08T10:21:38.171Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: 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":["event-driven","laravel","microservices","php","publish-subscribe","rabbitmq"],"created_at":"2026-01-11T13:50:16.171Z","updated_at":"2026-04-08T14:00:48.921Z","avatar_url":"https://github.com/nuwber.png","language":"PHP","funding_links":["https://github.com/sponsors/masterjus"],"categories":[],"sub_categories":[],"readme":"# RabbitEvents\n\n[![Tests Status](https://github.com/nuwber/rabbitevents/workflows/Unit%20tests/badge.svg)](https://github.com/nuwber/rabbitevents/actions?query=branch%3Amaster+workflow%3A%22Unit+tests%22)\n[![codecov](https://codecov.io/gh/nuwber/rabbitevents/branch/master/graph/badge.svg?token=8E9CY6866R)](https://codecov.io/gh/nuwber/rabbitevents)\n[![Total Downloads](https://img.shields.io/packagist/dt/nuwber/rabbitevents)](https://packagist.org/packages/nuwber/rabbitevents)\n[![Latest Version](https://img.shields.io/packagist/v/nuwber/rabbitevents)](https://packagist.org/packages/nuwber/rabbitevents)\n[![License](https://img.shields.io/packagist/l/nuwber/rabbitevents)](https://packagist.org/packages/nuwber/rabbitevents)\n\nLet's imagine a use case: a User made a payment. You need to handle this payment, register the user, send him emails, send analytics data to your analysis system, and so on. The modern infrastructure requires you to create microservices that do their specific job and only it: one handles payments, one is for user management, one is the mailing system, one is for analysis. How to let all of them know that a payment succeeded and handle this message? The answer is \"To use RabbitEvents\".\n\nOnce again, the RabbitEvents library helps you publish an event and handle it in another app. It doesn't make sense to use it in the same app because Laravel's Events work better for that.\n\n## Demo\n![rabbit-events-demo](https://github.com/nuwber/rabbitevents/assets/142213350/89df6194-e49d-4d58-8286-8932f182da4b)\n\n## Table of Contents\n1. [Installation via Composer](#installation)\n   * [Configuration](#configuration)\n1. [Upgrade from 8.x to 9.x](#upgrade_8.x-9.x)\n1. [Publisher component](#publisher)\n1. [Listener component](#listener)\n1. [Listeners \u0026 Payloads](#listeners-payloads)\n1. [Custom Serializers](#custom-serializers)\n1. [Extensions](#extensions)\n1. [Examples](./examples)\n1. [Speeding up RabbitEvents](#speeding-up-rabbitevents)\n1. [Testing](#testing)\n1. [Non-standard use](#non-standard-use)\n1. [License](#license)\n\n## Installation via Composer\u003ca name=\"installation\"\u003e\u003c/a\u003e\nYou can use Composer to install RabbitEvents into your Laravel project:\n\n```bash\ncomposer require nuwber/rabbitevents\n```\n\n### Configuration\u003ca name=\"configuration\"\u003e\u003c/a\u003e\nAfter installing RabbitEvents, publish its config and a service provider using the `rabbitevents:install` Artisan command:\n\n```bash\nphp artisan rabbitevents:install\n```\n\nThis command installs the config file at `config/rabbitevents.php` and the Service Provider file at `app/providers/RabbitEventsServiceProvider.php`.\n\nThe config file is very similar to the queue connection, but with the separate config, you'll never be confused if you have another connection to RabbitMQ.\n\n```php\n\u003c?php\nuse Enqueue\\AmqpTools\\RabbitMqDlxDelayStrategy;\n\nreturn [\n    'default' =\u003e env('RABBITEVENTS_CONNECTION', 'rabbitmq'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Default Serializer\n    |--------------------------------------------------------------------------\n    |\n    | The default serializer to use when publishing messages.\n    | Supported: \"json\", \"protobuf\" or any class implementing Serializer interface.\n    |\n    */\n    'default_serializer' =\u003e \\RabbitEvents\\Foundation\\Serialization\\JsonSerializer::class,\n\n    'connections' =\u003e [\n        'rabbitmq' =\u003e [\n            'driver' =\u003e 'rabbitmq',\n            'exchange' =\u003e env('RABBITEVENTS_EXCHANGE', 'events'),\n            'durable' =\u003e env('RABBITEVENTS_QUEUE_DURABLE', true),\n            'host' =\u003e env('RABBITEVENTS_HOST', 'localhost'),\n            'port' =\u003e env('RABBITEVENTS_PORT', 5672),\n            'user' =\u003e env('RABBITEVENTS_USER', 'guest'),\n            'pass' =\u003e env('RABBITEVENTS_PASSWORD', 'guest'),\n            'vhost' =\u003e env('RABBITEVENTS_VHOST', 'events'),\n            'delay_strategy' =\u003e env('RABBITEVENTS_DELAY_STRATEGY', RabbitMqDlxDelayStrategy::class),\n            'ssl' =\u003e [\n                'is_enabled' =\u003e env('RABBITEVENTS_SSL_ENABLED', false),\n                'verify_peer' =\u003e env('RABBITEVENTS_SSL_VERIFY_PEER', true),\n                'cafile' =\u003e env('RABBITEVENTS_SSL_CAFILE'),\n                'local_cert' =\u003e env('RABBITEVENTS_SSL_LOCAL_CERT'),\n                'local_key' =\u003e env('RABBITEVENTS_SSL_LOCAL_KEY'),\n                'passphrase' =\u003e env('RABBITEVENTS_SSL_PASSPHRASE', ''),\n            ],\n            'read_timeout' =\u003e env('RABBITEVENTS_READ_TIMEOUT', 3.),\n            'write_timeout' =\u003e env('RABBITEVENTS_WRITE_TIMEOUT', 3.),\n            'connection_timeout' =\u003e env('RABBITEVENTS_CONNECTION_TIMEOUT', 3.),\n            'heartbeat' =\u003e env('RABBITEVENTS_HEARTBEAT', 0),\n            'persisted' =\u003e env('RABBITEVENTS_PERSISTED', false),\n            'lazy' =\u003e env('RABBITEVENTS_LAZY', true),\n            'qos' =\u003e [\n                'global' =\u003e env('RABBITEVENTS_QOS_GLOBAL', false),\n                'prefetch_size' =\u003e env('RABBITEVENTS_QOS_PREFETCH_SIZE', 0),\n                'prefetch_count' =\u003e env('RABBITEVENTS_QOS_PREFETCH_COUNT', 1),\n            ]\n        ],\n    ],\n    'logging' =\u003e [\n        'enabled' =\u003e env('RABBITEVENTS_LOG_ENABLED', false),\n        'level' =\u003e env('RABBITEVENTS_LOG_LEVEL', 'info'),\n        'channel' =\u003e env('RABBITEVENTS_LOG_CHANNEL'),\n    ],\n];\n```\n## Upgrade from 8.x to 9.x\u003ca name=\"upgrade_8.x-9.x\"\u003e\u003c/a\u003e\n\n### PHP 8.2 required\nRabbitEvents now requires PHP 8.2 or greater. It uses `readonly` classes and Enums.\n\n### Payload System Refactor\nThe internal payload handling has been refactored. `Message::payload()` now returns a `RabbitEvents\\Foundation\\Contracts\\Payload` object.\nTo get the raw value, use `$message-\u003epayload-\u003evalue()`.\n\n### Protobuf Support\nRabbitEvents supports [Google Protobuf](https://github.com/protocolbuffers/protobuf) messages via the `rabbitevents/protobuf` extension package.\nOnce installed, simply publish a Protobuf Message object, and it will be automatically serialized and hydrated on the listener side.\nThe system uses the `type` AMQP header to resolve the correct class.\n\n**Example:**\n```php\nuse Google\\Protobuf\\StringValue;\n\n+$message = new StringValue();\n+$message-\u003esetValue('Hello World');\n+\n+publish('my.event', $message);\n```\n\n### Dynamic Serializers\nThe correct serializer is now automatically selected based on the `content_type` header of the message.\n- `application/json` -\u003e JSON Serializer\n- `application/x-protobuf` -\u003e Protobuf Serializer\n\n### Supported Laravel versions\nRabbitEvents now supports Laravel 10.0 or greater.\n\n### Architecture Decoupling\nVersion 9.0 introduces a more abstract and extensible architecture.\n- **Decoupled Serialization**: Serializers now use an abstract `TransportMessage` contract instead of `Interop\\Amqp\\AmqpMessage`. This creates a cleaner separation between domain logic and the transport layer.\n- **Abstract Message Factory**: The `MessageFactory` has been moved to the Foundation layer and is now pluggable. You can implement your own `TransportMessageFactory` to create messages from different sources (e.g., tailored for testing or other transports).\n- **Transport Agnostic**: The core `Message` and `Sender` classes rely on internal contracts (`RabbitEvents\\Foundation\\Contracts\\*`), avoiding strict dependencies on `queue-interop`. Adapters are provided for AMQP.\n- **Connection Class Moved**: `RabbitEvents\\Foundation\\Connection` has been moved to `RabbitEvents\\Foundation\\Amqp\\Connection`. Update your type hints if you were using it directly.\n\n### Namespace Changes\nWe have reorganized the namespace structure for better clarity and standard compliance:\n- **Commands**: All console commands have been moved to `Console` namespaces.\n  - `RabbitEvents\\Foundation\\Commands` -\u003e `RabbitEvents\\Foundation\\Console`\n  - `RabbitEvents\\Listener\\Commands` -\u003e `RabbitEvents\\Listener\\Console`\n  - `RabbitEvents\\Publisher\\Commands` -\u003e `RabbitEvents\\Publisher\\Console`\n- **Releaser**: `RabbitEvents\\Foundation\\Support\\Releaser` has been moved to `RabbitEvents\\Listener\\Support\\Releaser` as it is a listener-specific component.\n\n### Removed `--connection` option from the `rabbitevents:listen` command\nThe default connection is always used instead.\n\n## RabbitEvents Publisher\u003ca name=\"publisher\"\u003e\u003c/a\u003e\n\nThe RabbitEvents Publisher component provides an API to publish events across the application structure. More information about how it works can be found on the RabbitEvents [Publisher page](https://github.com/rabbitevents/publisher).\n\n## RabbitEvents Listener\u003ca name=\"listener\"\u003e\u003c/a\u003e\n\nThe RabbitEvents Listener component provides an API to handle events that were published across the application structure. More information about how it works can be found on the RabbitEvents [Listener page](https://github.com/rabbitevents/listener).\n\nIt supports:\n- **Manual Registration:** using the `$listen` array.\n- **Attributes:** using `#[Listener]` on classes or methods.\n- **Auto-Discovery:** automatically finding `#[Listener]` attributes in your listeners directory.\n\n## Listeners \u0026 Payloads\u003ca name=\"listeners-payloads\"\u003e\u003c/a\u003e\n\nHow the payload is passed to your listener depends on the serialization format:\n\n**JSON (Default):**\nThe payload is decoded as an **associative array**.\n```php\npublic function handle(array $payload) {\n    // $payload['key']\n}\n```\n\n**Protobuf:**\nThe payload is passed as the **Message Object** itself.\n```php\npublic function handle(\\Google\\Protobuf\\Internal\\Message $message) {\n    // $message-\u003egetSomething()\n}\n```\n\n## Custom Serializers\u003ca name=\"custom-serializers\"\u003e\u003c/a\u003e\n\nYou can implement your own serializer by implementing `RabbitEvents\\Foundation\\Contracts\\Serializer` and `RabbitEvents\\Foundation\\Contracts\\ContentType`.\n\n```php\nuse RabbitEvents\\Foundation\\Contracts\\Serializer;\nuse RabbitEvents\\Foundation\\Contracts\\ContentType;\nuse RabbitEvents\\Foundation\\Contracts\\Payload;\n\nclass MySerializer implements Serializer\n{\n    public function serialize($data): Payload { ... }\n    public function deserialize($message): Payload { ... }\n    public function contentType(): ContentType { return new MyContentType(); }\n    public function canSerialize($payload): bool { ... }\n}\n```\n\nRegister your serializer in a ServiceProvider:\n\n```php\n$registry = $this-\u003eapp-\u003emake(\\RabbitEvents\\Foundation\\Serialization\\SerializerRegistry::class);\n$registry-\u003eregister(new MySerializer());\n```\n\nNew serializers are prepended to the registry, so they can override default behavior if they claim the payload.\n\n## Extensions\u003ca name=\"extensions\"\u003e\u003c/a\u003e\n\n- [Protobuf Support](https://github.com/rabbitevents/protobuf) - Provides official Google Protobuf serialization support. Required for using Protobuf messages.\n\n## Speeding up RabbitEvents\u003ca name=\"speeding-up-rabbitevents\"\u003e\u003c/a\u003e\nTo enhance the performance of RabbitEvents, consider installing the `php-amqp` extension along with the `enqueue/amqp-ext` package. \nBy doing so, RabbitEvents will utilize the `enqueue/amqp-ext` package instead of the default `enqueue/amqp-lib` package. \nThis substitution is advantageous because the C-written `php-amqp` package significantly outperforms the PHP-written `enqueue/amqp-lib` package.\n\nYou can install the `php-amqp` extension using the following command:\n```bash\npecl install amqp\n```\nor use the way you prefer. More about it can be found [here](https://pecl.php.net/package/amqp).\n\nNext, install the `enqueue/amqp-ext` package with the following command:\n```bash\ncomposer require enqueue/amqp-ext\n```\nNo additional configuration is required.\n\n## Testing \u003ca name=\"testing\"\u003e\u003c/a\u003e\n\nWe always write tests. Tests in our applications contain many mocks and fakes to test how events are published.\n\nThere is the `PublishableEventTesting` trait that provides assertion methods in an Event class that you want to test.\n\n**Event.php**\n\n```php\n\u003c?php\n\nnamespace App\\BroadcastEvents;\n\nuse RabbitEvents\\Publisher\\ShouldPublish;\nuse RabbitEvents\\Publisher\\Support\\Publishable;\nuse RabbitEvents\\Publisher\\Support\\PublishableEventTesting;\n\nclass Event implements ShouldPublish\n{\n    use Publishable;\n    use PublishableEventTesting;\n\n    public function __construct(private array $payload) \n    {\n    }\n\n    public function publishEventKey(): string\n    {\n        return 'something.happened';\n    }\n\n    public function toPublish(): array\n    {\n        return $this-\u003epayload;\n    }\n}\n```\n\n**Test.php**\n\n```php\n\u003c?php\n\nuse \\App\\RabbitEvents\\Event;\nuse \\App\\RabbitEvents\\AnotherEvent;\n\nEvent::fake();\n\n$payload = [\n    'key1' =\u003e 'value1',\n    'key2' =\u003e 'value2',\n];\n\nEvent::publish($payload);\n\nEvent::assertPublished('something.happened', $payload);\n\nAnotherEvent::assertNotPublished();\n```\n\nIf the assertion does not pass, `Mockery\\Exception\\InvalidCountException` will be thrown.\\\nDon't forget to call `\\Mockery::close()` in `tearDown` or similar methods of your tests.\n\n## Non-standard use \u003ca name=\"non-standard-use\"\u003e\u003c/a\u003e\n\nIf you're using only one part of RabbitEvents, you should know a few things:\n\n1. You remember, we're using RabbitMQ as the transport layer. In the [RabbitMQ Documentation](https://www.rabbitmq.com/tutorials/tutorial-five-python.html), you can find examples of how to publish your messages using a routing key. This routing key is the event name, like `something.happened` from the examples above.\n\n1. RabbitEvents expects that a message body is a JSON-encoded array. Every element of an array will be passed to a Listener as a separate variable. For example:\n```json\n[\n  {\n    \"key\": \"value\"  \n  },\n  \"string\",\n  123 \n]\n```\n\nThere are 3 elements in this array, so 3 variables will be passed to a Listener (an array, a string, and an integer).\nIf an associative array is being passed, the Dispatcher wraps this array by itself.\n\n# License \u003ca name=\"license\"\u003e\u003c/a\u003e\n\nRabbitEvents is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnuwber%2Frabbitevents","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnuwber%2Frabbitevents","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnuwber%2Frabbitevents/lists"}