├── .gitignore
├── LICENSE.md
├── README.RU.md
├── README.UZ.md
├── README.md
├── composer.json
├── config
└── simple-mq.php
├── phpunit.xml
├── routes
└── amqp-handlers.php
├── src
├── ActionMQ.php
├── Console
│ └── Commands
│ │ ├── ConsumeCommand.php
│ │ ├── DefineQueueCommand.php
│ │ ├── MakeHandlerCommand.php
│ │ └── stubs
│ │ └── handler.stub
├── ConsumeMQ.php
├── Exceptions
│ ├── ConnectionNotFoundException.php
│ └── DefaultConnectionNotFoundException.php
├── Facades
│ ├── ActionMQ.php
│ ├── ConsumeMQ.php
│ └── SimpleMQ.php
├── MQ
│ ├── Connection.php
│ ├── ConnectionManager.php
│ ├── Definition.php
│ ├── Message.php
│ ├── MessageBuilder.php
│ └── Publisher.php
├── SimpleMQ.php
└── SimpleRabbitMQServiceProvider.php
└── tests
├── ExampleTest.php
└── Pest.php
/.gitignore:
--------------------------------------------------------------------------------
1 | /.phpunit.cache
2 | /node_modules
3 | /public/build
4 | /public/hot
5 | /public/storage
6 | /storage/*.key
7 | /vendor
8 |
9 | .env
10 | .env.backup
11 | .env.production
12 | .phpunit.result.cache
13 | Homestead.json
14 | Homestead.yaml
15 | auth.json
16 | npm-debug.log
17 | yarn-error.log
18 | composer.lock
19 |
20 | /.fleet
21 | /.idea
22 | /.vscode
23 |
24 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Temur Usmonaliyev
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.RU.md:
--------------------------------------------------------------------------------
1 |
2 |
laravel-simple-rabbitmq
3 |
4 |
5 | > _Documentation generated with the help of ChatGPT_
6 |
7 | Пакет для упрощенного использования RabbitMQ с поддержкой нескольких подключений, легкой публикацией сообщений и режимом
8 | потребителя.
9 |
10 | ### Основные функции
11 |
12 | - **Несколько подключений**: Легкое управление несколькими подключениями RabbitMQ в одном приложении.
13 |
14 | - **Поддержка Exchange**: Вы можете отправлять сообщения в exchange.
15 |
16 | - **Публикация сообщений**: Легко публикуйте сообщения в очереди и exchange с помощью удобного, цепочечного синтаксиса.
17 |
18 | - **Режим потребителя**: Позволяет потребителям получать и обрабатывать сообщения из очередей в режиме реального
19 | времени.
20 |
21 | - **Управление очередями и exchange в конфигурационном файле**: Вы можете зарегистрировать очереди и exchange в
22 | `config/simple-mq.php` и
23 | легко определить их с помощью команды `amqp:define-queues`.
24 |
25 |
26 |
Установка
27 |
28 |
29 | Вы можете установить пакет через Composer:
30 |
31 | ```bash
32 | composer require usmonaliyev/laravel-simple-rabbitmq
33 | ```
34 |
35 | На следующем этапе необходимо опубликовать конфигурационные файлы и файлы регистрации действий:
36 |
37 | ```bash
38 | php artisan vendor:publish --provider="Usmonaliyev\SimpleRabbit\SimpleRabbitMQServiceProvider"
39 | ```
40 |
41 | В результате выполнения этой команды у вас появится файл конфигурации `config/simple-mq.php` и файл регистрации
42 | `routes/amqp-handlers.php`.
43 |
44 | Файл конфигурации `config/simple-mq.php` содержит подключения к RabbitMQ с учетными данными, очередями, стандартным
45 | подключением и
46 | стандартной очередью.
47 |
48 | Следующий этап — настройка файла `.env`.
49 |
50 | ```.dotenv
51 | SIMPLE_MQ_CONNECTION=
52 | SIMPLE_MQ_QUEUE=
53 |
54 | SIMPLE_MQ_HOST=
55 | SIMPLE_MQ_PORT=
56 | SIMPLE_MQ_USERNAME=
57 | SIMPLE_MQ_PASSWORD=
58 | ```
59 |
60 |
61 |
Использование
62 |
63 |
64 | Пакет может публиковать и потреблять сообщения.
65 |
66 | ### Публикация
67 |
68 | Вы можете опубликовать сообщение с использованием стандартного подключения и очереди:
69 |
70 | ```php
71 | setBody(['name' => 'Первый Foo'])
84 | ->handler('create-foo')
85 | ->publish();
86 |
87 | return response()->json(['message' => 'OK']);
88 | }
89 | }
90 | ```
91 |
92 | Также функция `exchange` публикует сообщение в exchange RabbitMq:
93 |
94 | ```php
95 | setBody(['name' => 'Первый Foo'])
110 | ->setRoutingKey('foo.bar')
111 | ->handler('create-foo')
112 | ->publish();
113 |
114 | return response()->json(['message' => 'OK']);
115 | }
116 | }
117 | ```
118 |
119 | Если у вас несколько подключений к RabbitMq, вы можете опубликовать сообщение с использованием метода `connection`.
120 |
121 | ```php
122 | queue('foo-queue')
137 | ->setBody(['name' => 'Первый Foo'])
138 | ->handler('create-foo')
139 | ->publish();
140 |
141 | return response()->json(['message' => 'OK']);
142 | }
143 | }
144 | ```
145 |
146 | ### Потребление
147 |
148 | Создайте папку `app/AMQP/Handlers` и создайте свои классы обработчиков.
149 |
150 | Например:
151 |
152 | ```php
153 | ack();
166 |
167 | return ['ok' => true];
168 | }
169 | }
170 | ```
171 |
172 | Не забудьте подтвердить сообщение в конце процесса, иначе потребитель не примет следующее сообщение.
173 |
174 | Затем зарегистрируйте свой обработчик в файле `routes/amqp-handlers.php`.
175 |
176 | ```php
177 |
195 | Контракты
196 |
201 |
202 |
203 |
204 |
205 |
206 |
Планы
207 |
208 |
209 | - [ ] Настройка exchange в `config/simple-mq.php`
210 | - [ ] Написание файлов `README.UZ.md` и `README.RU.md`.
211 | - [ ] Настройка тестирования.
212 |
213 |
214 |
Тестирование
215 |
216 |
217 | ```bash
218 | composer test
219 | ```
220 |
221 |
222 |
Лицензия
223 |
224 |
225 | Лицензия [MIT](LICENSE.md).
226 |
--------------------------------------------------------------------------------
/README.UZ.md:
--------------------------------------------------------------------------------
1 |
2 |
laravel-simple-rabbitmq
3 |
4 |
5 | > _Documentation generated with the help of ChatGPT_
6 |
7 | RabbitMQ dan foydalanishni soddalashtiruvchi paket, ko‘p connectionlarni qo‘llab-quvvatlash, oson xabar yuborish va
8 | consumer rejimiga ega.
9 |
10 | ### Asosiy xususiyatlar
11 |
12 | - **Ko'p ulanishlar**: Bitta dastur ichida bir nechta RabbitMQ connectionlarni oson boshqarish imkoniyati.
13 |
14 | - **Exchange qo‘llab-quvvatlashi**: Xabarlarni exchange ga yuborishingiz mumkin.
15 |
16 | - **Xabar yuborish**: queue va exchange ga xabarlarni zanjirli sintaksis yordamida oson yuborish.
17 |
18 | - **Consumer mode**: Consumer navbatlardan real vaqtda xabarlarni olish va qayta ishlash imkoniyatini beradi.
19 |
20 | - **Queue va exchangelarni konfiguratsiya faylida boshqarish**: `config/simple-mq.php` faylida queue va exchange ni
21 | ro‘yxatdan o‘tkazib, `amqp:define-queues` buyrug‘i yordamida oson e'lon qilish.
22 |
23 |
24 |
O'rnatish
25 |
26 |
27 | Paketni Composer orqali o'rnatishingiz mumkin:
28 |
29 | ```bash
30 | composer require usmonaliyev/laravel-simple-rabbitmq
31 | ```
32 |
33 | Keyingi qadamda konfiguratsiya va kerakli fayllarini nashr qilish kerak:
34 |
35 | ```bash
36 | php artisan vendor:publish --provider="Usmonaliyev\SimpleRabbit\SimpleRabbitMQServiceProvider"
37 | ```
38 |
39 | Ushbu buyruq natijasida sizda `config/simple-mq.php` konfiguratsiya fayli va `routes/amqp-handlers.php` registratsiya
40 | fayli paydo bo'ladi.
41 |
42 | `config/simple-mq.php` konfiguratsiya faylida RabbitMQ ulanishlari, ularnish login va parollari, queue, default
43 | connection va default queue belgilash mumkin.
44 |
45 | Keyingi bosqich `.env` faylini sozlash.
46 |
47 | ```.dotenv
48 | SIMPLE_MQ_CONNECTION=
49 | SIMPLE_MQ_QUEUE=
50 |
51 | SIMPLE_MQ_HOST=
52 | SIMPLE_MQ_PORT=
53 | SIMPLE_MQ_USERNAME=
54 | SIMPLE_MQ_PASSWORD=
55 | ```
56 |
57 |
58 |
Foydalanish
59 |
60 |
61 | Paket xabarlarni yuborish va consume qilish imkoniyatiga ega.
62 |
63 | ### Xabar yuborish
64 |
65 | Default connection va queue dan foydalanib xabar yuborishingiz uchun:
66 |
67 | ```php
68 | setBody(['name' => 'Birinchi Foo'])
81 | ->handler('create-foo')
82 | ->publish();
83 |
84 | return response()->json(['message' => 'OK']);
85 | }
86 | }
87 | ```
88 |
89 | Bundan tashqari, `exchange` funksiyasi xabarni RabbitMq exchange ga yuboradi:
90 |
91 | ```php
92 | setBody(['name' => 'Birinchi Foo'])
107 | ->setRoutingKey('foo.bar')
108 | ->handler('create-foo')
109 | ->publish();
110 |
111 | return response()->json(['message' => 'OK']);
112 | }
113 | }
114 | ```
115 |
116 | Agar sizda RabbitMq bilan ko‘p ulanish mavjud bo‘lsa, `connection` funksiyasidan foydalanishingiz mumkin.
117 |
118 | ```php
119 | queue('foo-queue')
134 | ->setBody(['name' => 'Birinchi Foo'])
135 | ->handler('create-foo')
136 | ->publish();
137 |
138 | return response()->json(['message' => 'OK']);
139 | }
140 | }
141 | ```
142 |
143 | ### Consuming
144 |
145 | `app/AMQP/Handlers` papkasini yarating va o‘z handler classingizni yarating.
146 |
147 | Masalan:
148 |
149 | ```php
150 | ack();
163 |
164 | return ['ok' => true];
165 | }
166 | }
167 | ```
168 |
169 | Jarayon oxirida xabarni tasdiqlashni unutmang (Acknowledge => $message->ack()), aks holda consumer keyingi xabarni qabul
170 | qilmaydi.
171 |
172 | Keyin o'z handleringizni `routes/amqp-handlers.php` fayliga ro'yxatdan o'tkazing.
173 |
174 | ```php
175 |
193 | Shartnomalar
194 |
199 |
200 |
201 |
202 |
203 |
204 |
Rejalar
205 |
206 |
207 | - [ ] `config/simple-mq.php` faylida exchange sozlash
208 | - [ ] `README.UZ.md` va `README.RU.md` fayllarini yozish.
209 | - [ ] Testlarni sozlash.
210 |
211 |
212 |
Test qilish
213 |
214 |
215 | ```bash
216 | composer test
217 | ```
218 |
219 |
220 |
Litsenziya
221 |
222 |
223 | [MIT](LICENSE.md) Litsenziya.
224 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
laravel-simple-rabbitmq
3 |
4 |
5 |
11 |
12 | The package for simplified RabbitMQ usage, supporting multiple connections, easy publishing, and consumer mode.
13 |
14 | ## Documentation
15 |
16 | - [Русский (RU)](README.RU.md)
17 | - [O'zbekcha (UZ)](README.UZ.md)
18 |
19 | ### Key Features
20 |
21 | - **Multiple Connections**: Effortlessly manage multiple RabbitMQ connections within the same application.
22 |
23 | - **Exchange supporting**: You can push messages to exchanges
24 |
25 | - **Message Publishing**: Easily publish messages to queues and exchange with a fluent, chainable syntax.
26 |
27 | - **Consumer Mode**: Enable consumers to receive and process messages from queues in real time.
28 |
29 | - **Manage queues and exchanges in config file**: You can register queues and exchanges in `config/simple-mq.php` and
30 | define them in easy way which is `amqp:define-queues` command.
31 |
32 |
33 |
Installation
34 |
35 |
36 | You can install the package via composer:
37 |
38 | ```bash
39 | composer require usmonaliyev/laravel-simple-rabbitmq
40 | ```
41 |
42 | Next step you must publish config and action register files:
43 |
44 | ```bash
45 | php artisan vendor:publish --provider="Usmonaliyev\SimpleRabbit\SimpleRabbitMQServiceProvider"
46 | ```
47 |
48 | As a result of this command, you will have a configuration file `config/simple-mq.php` and a registry file
49 | `routes/amqp-handlers.php`.
50 |
51 | The `config/simple-mq.php` config file contains RabbitMQ connections with credentials, queues, default connection and
52 | default queue.
53 |
54 | The next stage is configure `.env` file.
55 |
56 | ```.dotenv
57 | SIMPLE_MQ_CONNECTION=
58 | SIMPLE_MQ_QUEUE=
59 |
60 | SIMPLE_MQ_HOST=
61 | SIMPLE_MQ_PORT=
62 | SIMPLE_MQ_USERNAME=
63 | SIMPLE_MQ_PASSWORD=
64 | ```
65 |
66 |
67 |
Usage
68 |
69 |
70 | The package can publish and consume messages
71 |
72 | ### Publishing
73 |
74 | You can publish a message with default connection and default queue:
75 |
76 | ```php
77 | setBody(['name' => 'First Foo'])
90 | ->handler('create-foo')
91 | ->publish();
92 |
93 | return response()->json(['message' => 'OK']);
94 | }
95 | }
96 | ```
97 |
98 | Also, `exchange` function publish message to RabbitMq exchange:
99 |
100 | ```php
101 | setBody(['name' => 'First Foo'])
116 | ->setRoutingKey('foo.bar')
117 | ->handler('create-foo')
118 | ->publish();
119 |
120 | return response()->json(['message' => 'OK']);
121 | }
122 | }
123 | ```
124 |
125 | If you have multiply connection to RabbitMq, you can publish a message with `connection` method.
126 |
127 | ```php
128 | queue('foo-queue')
143 | ->setBody(['name' => 'First Foo'])
144 | ->handler('create-foo')
145 | ->publish();
146 |
147 | return response()->json(['message' => 'OK']);
148 | }
149 | }
150 | ```
151 |
152 | ### Consuming
153 |
154 | Create `app/AMQP/Handlers` folder and create your handler classes.
155 |
156 | For example:
157 |
158 | ```php
159 | ack();
172 |
173 | return ['ok' => true];
174 | }
175 | }
176 | ```
177 |
178 | Don't forget acknowledge message end of process, else consumer does not accept next message.
179 |
180 | Then register your handler in `routes/amqp-handlers.php` file.
181 |
182 | ```php
183 |
201 | Contracts
202 |
207 |
208 |
209 |
210 |
211 |
212 |
Plans
213 |
214 |
215 | - [ ] Exchange configuration in `config/simple-mq.php`
216 | - [ ] Setup testing.
217 |
218 |
219 |
Testing
220 |
221 |
222 | ```bash
223 | composer test
224 | ```
225 |
226 |
227 |
License
228 |
229 |
230 | The [MIT](LICENSE.md) License.
231 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "usmonaliyev/laravel-simple-rabbitmq",
3 | "description": "This package provides simple usage of rabbitmq.",
4 | "keywords": [
5 | "usmonaliyev",
6 | "simple",
7 | "rabbit",
8 | "mq"
9 | ],
10 | "homepage": "https://github.com/usmonaliyev/laravel-simple-rabbitmq",
11 | "license": "MIT",
12 | "type": "library",
13 | "authors": [
14 | {
15 | "name": "Temur Usmonaliyev",
16 | "email": "t.usmonaliyev99@gmail.com",
17 | "role": "Developer"
18 | }
19 | ],
20 | "require": {
21 | "php": "^8.1",
22 | "illuminate/console": "*",
23 | "illuminate/support": "*",
24 | "php-amqplib/php-amqplib": "^3.7"
25 | },
26 | "require-dev": {
27 | "laravel/pint": "^1.18",
28 | "pestphp/pest": "^1.23",
29 | "phpunit/phpunit": "^9.0",
30 | "symfony/var-dumper": "^6.4"
31 | },
32 | "autoload": {
33 | "psr-4": {
34 | "Usmonaliyev\\SimpleRabbit\\": "src"
35 | }
36 | },
37 | "autoload-dev": {
38 | "psr-4": {
39 | "Usmonaliyev\\SimpleRabbit\\Tests\\": "tests"
40 | }
41 | },
42 | "minimum-stability": "stable",
43 | "scripts": {
44 | "test": "vendor/bin/pest",
45 | "test-coverage": "vendor/bin/pest --coverage-html coverage"
46 | },
47 | "config": {
48 | "sort-packages": true,
49 | "allow-plugins": {
50 | "pestphp/pest-plugin": true
51 | }
52 | },
53 | "extra": {
54 | "laravel": {
55 | "providers": [
56 | "Usmonaliyev\\SimpleRabbit\\SimpleRabbitMQServiceProvider"
57 | ]
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/config/simple-mq.php:
--------------------------------------------------------------------------------
1 | env('SIMPLE_MQ_QUEUE', 'simple'),
16 |
17 | 'connection' => env('SIMPLE_MQ_CONNECTION', 'default'),
18 |
19 | /*
20 | |--------------------------------------------------------------------------
21 | | Connections
22 | |--------------------------------------------------------------------------
23 | |
24 | | Here you may define all of the message broker connections for your
25 | | application. These connections can be reused across multiple queues.
26 | |
27 | */
28 | 'connections' => [
29 |
30 | 'default' => [
31 | 'host' => env('SIMPLE_MQ_HOST', 'localhost'),
32 | 'port' => env('SIMPLE_MQ_PORT', 5672),
33 | 'username' => env('SIMPLE_MQ_USERNAME', 'guest'),
34 | 'password' => env('SIMPLE_MQ_PASSWORD', 'guest'),
35 | 'vhost' => env('SIMPLE_MQ_VHOST', '/'),
36 | 'insist' => false,
37 | 'login_method' => 'AMQPLAIN',
38 | 'login_response' => null,
39 | 'locale' => 'en_US',
40 | 'connection_timeout' => 30,
41 | 'read_write_timeout' => 30,
42 | 'context' => null,
43 | 'keepalive' => false,
44 | 'heartbeat' => false,
45 | 'channel_rpc_timeout' => 0,
46 | 'ssl_protocol' => null,
47 | ],
48 | ],
49 |
50 | /*
51 | |--------------------------------------------------------------------------
52 | | Queues
53 | |--------------------------------------------------------------------------
54 | |
55 | | Each queue can be linked to a specific connection and configured with
56 | | additional queue-specific settings, such as queue name or routing keys.
57 | |
58 | */
59 | 'queues' => [
60 |
61 | 'simple' => [
62 | // Specifies the name of the connection to be used for this queue.
63 | 'connection' => 'default',
64 |
65 | /**
66 | * Be carefully these arguments because you can define queue one time with these arguments.
67 | * After queue is created, you don't change these arguments
68 | *
69 | * If you must change arguments, delete queue and define them one more time.
70 | */
71 | 'arguments' => [
72 | /**
73 | * Sets the time-to-live for messages in this queue to 60000 milliseconds (60 seconds).
74 | *
75 | * 1 min = 60000
76 | * 1 hour = 3600000
77 | * 1 day = 3600000 * 24
78 | */
79 | 'x-message-ttl' => 3600000,
80 | // Limits the maximum number of messages in the queue to 1000.
81 | 'x-max-length' => 1000,
82 | // Specifies the dead-letter exchange to which messages will be sent if they expire.
83 | 'x-dead-letter-exchange' => null,
84 | // Routing key for messages that are routed to the dead-letter exchange.
85 | 'x-dead-letter-routing-key' => null,
86 | ],
87 | ],
88 | ],
89 |
90 | ];
91 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 | ./tests
11 |
12 |
13 |
14 |
15 | ./app
16 | ./src
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/routes/amqp-handlers.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | private array $handlers = [];
18 |
19 | /**
20 | * Register a new action
21 | */
22 | public function register(string $handler, array|callable $callback): void
23 | {
24 | $this->handlers[$handler] = $callback;
25 | }
26 |
27 | /**
28 | * Getter of actions property
29 | *
30 | * @return array[]|callable[]
31 | */
32 | public function getHandlers(): array
33 | {
34 | return $this->handlers;
35 | }
36 |
37 | /**
38 | * Loading actions from route/amqp-handlers.php
39 | */
40 | public function load(): void
41 | {
42 | $callback = fn () => include_once base_path('routes/amqp-handlers.php');
43 |
44 | $callback();
45 | }
46 |
47 | /**
48 | * Main consumer
49 | */
50 | public function consume(AMQPMessage $amqpMessage): mixed
51 | {
52 | $message = new Message($amqpMessage);
53 |
54 | // If there is no handler which match to message, message is deleted
55 | if (! isset($this->handlers[$message->getHandler()])) {
56 | $message->ack();
57 |
58 | return null;
59 | }
60 |
61 | return $this->dispatch($message);
62 | }
63 |
64 | /**
65 | * Dispatcher to execute handler
66 | */
67 | protected function dispatch($message): mixed
68 | {
69 | $handler = $this->handlers[$message->getHandler()];
70 |
71 | try {
72 | if (is_callable($handler)) {
73 | return call_user_func_array($handler, [$message]);
74 | }
75 |
76 | [$class, $method] = $handler;
77 | $instance = App::make($class);
78 |
79 | return $instance->{$method}($message);
80 |
81 | } catch (Exception $e) {
82 | $error = sprintf('ERROR [%s] %s: %s'.PHP_EOL, gmdate('Y-m-d H:i:s'), get_class($e), $e->getMessage());
83 | echo $error;
84 |
85 | return null;
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/Console/Commands/ConsumeCommand.php:
--------------------------------------------------------------------------------
1 | argument('queue');
35 | $connection = $this->argument('connection');
36 |
37 | $action = App::make(ActionMQ::class);
38 | $action->load();
39 |
40 | ConsumeMQ::connection($connection)
41 | ->queue($queue)
42 | ->listen($action->consume(...));
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Console/Commands/DefineQueueCommand.php:
--------------------------------------------------------------------------------
1 | $queue) {
33 | $arguments = array_filter($queue['arguments'], fn ($argument) => ! is_null($argument));
34 |
35 | SimpleMQ::connection($queue['connection'])
36 | ->getDefinition()
37 | ->setArguments($arguments)
38 | ->defineQueue($name);
39 | }
40 |
41 | $this->info('Queues are defined.');
42 |
43 | $exchange = $this->option('exchange');
44 | if ($exchange) {
45 | return;
46 | }
47 |
48 | // TODO Define exchange
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Console/Commands/MakeHandlerCommand.php:
--------------------------------------------------------------------------------
1 | ack();
14 | }
15 | }
--------------------------------------------------------------------------------
/src/ConsumeMQ.php:
--------------------------------------------------------------------------------
1 | connection = $name;
31 |
32 | return $this;
33 | }
34 |
35 | public function queue(?string $name = null): self
36 | {
37 | $this->queue = $name;
38 |
39 | return $this;
40 | }
41 |
42 | /**
43 | * Start listening RabbitMq server
44 | *
45 | * @throws Exception
46 | */
47 | public function listen(callable $callback): void
48 | {
49 | $this->buildConnection();
50 | $this->setupQos();
51 |
52 | $queue = $this->getQueue();
53 |
54 | $this->channel->basic_consume($queue, '', false, false, false, false, $callback);
55 |
56 | echo "Consumer is listening to \"$queue\" queue...".PHP_EOL;
57 |
58 | while ($this->channel->is_consuming()) {
59 | $this->channel->wait();
60 | }
61 |
62 | $this->channel->close();
63 | $this->amqpConnection->close();
64 | }
65 |
66 | /**
67 | * Setup QOS for this channel to read message one by one
68 | */
69 | protected function setupQos($qos = [null, 1, null]): void
70 | {
71 | $this->channel->basic_qos(...$qos);
72 | }
73 |
74 | /**
75 | * Building AMQPStreamConnection to rabbit mq server
76 | *
77 | * @throws Exception
78 | */
79 | protected function buildConnection(): void
80 | {
81 | $manager = App::make(ConnectionManager::class);
82 |
83 | $this->amqpConnection = $manager
84 | ->connection($this->getConnectionName())
85 | ->getAMQPConnection();
86 |
87 | $this->channel = $this->amqpConnection->channel();
88 | }
89 |
90 | /**
91 | * Getting queue name if it is null, default queue name
92 | */
93 | public function getQueue(): string
94 | {
95 | return $this->queue ??= Config::get('simple-mq.queue');
96 | }
97 |
98 | /**
99 | * Getting connection name if it is null, default connection name
100 | */
101 | public function getConnectionName(): string
102 | {
103 | return $this->connection ??= Config::get('simple-mq.connection');
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/Exceptions/ConnectionNotFoundException.php:
--------------------------------------------------------------------------------
1 | name = $name;
28 |
29 | $config = Config::get("simple-mq.connections.$name");
30 |
31 | if (! isset($config)) {
32 | throw new ConnectionNotFoundException($name);
33 | }
34 |
35 | $this->connection = new AMQPStreamConnection(
36 | $config['host'],
37 | $config['port'],
38 | $config['username'],
39 | $config['password'],
40 | $config['vhost'],
41 | $config['insist'],
42 | $config['login_method'],
43 | $config['login_response'],
44 | $config['locale'],
45 | $config['connection_timeout'],
46 | $config['read_write_timeout'],
47 | $config['context'],
48 | $config['keepalive'],
49 | $config['heartbeat'],
50 | $config['channel_rpc_timeout'],
51 | $config['ssl_protocol']
52 | );
53 | }
54 |
55 | /**
56 | * Get a publisher for the specified queue.
57 | */
58 | public function queue(?string $queueName = null): MessageBuilder
59 | {
60 | $queueName = $queueName ?? Config::get('simple-mq.queue');
61 |
62 | return new MessageBuilder($this->name, $queueName, 'QUEUE');
63 | }
64 |
65 | /**
66 | * Get a publisher for the specified exchange.
67 | */
68 | public function exchange($exchangeName): MessageBuilder
69 | {
70 | return new MessageBuilder($this->name, $exchangeName, 'EXCHANGE');
71 | }
72 |
73 | /**
74 | * Returns the active AMQPStreamConnection connection.
75 | *
76 | * @throws Exception
77 | */
78 | public function getAMQPConnection(): AMQPStreamConnection
79 | {
80 | return $this->connection;
81 | }
82 |
83 | /**
84 | * Retrieves an AMQP channel for the current connection.
85 | * If no channel exists, a new channel is created and returned.
86 | * Channels are used to interact with RabbitMQ, allowing you to
87 | * publish messages, consume messages, and perform other operations.
88 | *
89 | * @return AMQPChannel The channel associated with the current connection.
90 | *
91 | * @throws Exception If the channel cannot be created or the connection is invalid.
92 | */
93 | public function getChannel(): AMQPChannel
94 | {
95 | return $this->connection->channel();
96 | }
97 |
98 | /**
99 | * Definition section manager queues and exchange
100 | */
101 | public function getDefinition(): Definition
102 | {
103 | return new Definition($this->connection);
104 | }
105 |
106 | /**
107 | * Close channel and connection before destruction
108 | *
109 | * @throws Exception
110 | */
111 | public function __destruct()
112 | {
113 | $this->connection->close();
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/MQ/ConnectionManager.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | private array $connections = [];
15 |
16 | /**
17 | * Get a connection instance.
18 | *
19 | * @throws Exception Default queue is not configured
20 | */
21 | public function connection(?string $name = null): Connection
22 | {
23 | $name = $name ?? Config::get('simple-mq.connection');
24 |
25 | if (! $name) {
26 | throw new DefaultConnectionNotFoundException;
27 | }
28 |
29 | return $this->connections[$name] ??= $this->make($name);
30 | }
31 |
32 | /**
33 | * Create a new AMQPStreamConnection instance.
34 | *
35 | * @throws Exception
36 | */
37 | protected function make(string $name): Connection
38 | {
39 | return new Connection($name);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/MQ/Definition.php:
--------------------------------------------------------------------------------
1 | connection = $connection;
49 |
50 | $this->channel = $connection->channel();
51 | }
52 |
53 | /**
54 | * Define a new queue
55 | */
56 | public function defineQueue(string $name): ?array
57 | {
58 | $argTable = $this->getArguments();
59 |
60 | return $this->channel->queue_declare(
61 | $name,
62 | $this->passive,
63 | $this->durable,
64 | $this->exclusive,
65 | $this->auto_delete,
66 | $this->nowait,
67 | $argTable,
68 | );
69 | }
70 |
71 | /**
72 | * To define new exchange
73 | *
74 | * @throws Exception Now there is no implementation
75 | */
76 | public function defineExchange(string $name, $type = 'direct'): ?array
77 | {
78 | throw new Exception('The function does not have implementation.');
79 | }
80 |
81 | /**
82 | * Setup arguments to create new queue
83 | *
84 | * @return $this
85 | */
86 | public function setArguments(array $arguments): self
87 | {
88 | $this->arguments = $arguments;
89 |
90 | return $this;
91 | }
92 |
93 | /**
94 | * Creates and returns AMQPTable to define new queue
95 | */
96 | protected function getArguments(): AMQPTable
97 | {
98 | return new AMQPTable($this->arguments);
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/MQ/Message.php:
--------------------------------------------------------------------------------
1 | message = $message;
19 |
20 | $this->separator();
21 | }
22 |
23 | /**
24 | * Content-Type of AMQPMessage
25 | */
26 | protected string $contentType = 'application/json';
27 |
28 | /**
29 | * Content-Encoding of AMQPMessage
30 | */
31 | protected string $contentEncoding = 'utf-8';
32 |
33 | /**
34 | * Application headers of AMQPMessage
35 | */
36 | protected array $applicationHeaders;
37 |
38 | /**
39 | * Message priority default is 0
40 | */
41 | protected int $priority;
42 |
43 | /**
44 | * Timestamp of AMQPMessage
45 | *
46 | * @var int
47 | */
48 | protected mixed $timestamp;
49 |
50 | /**
51 | * Handler name which accepted from AMQPMessage
52 | */
53 | protected string $handler;
54 |
55 | /**
56 | * Separator message details from AMQPMessage
57 | */
58 | protected function separator(): void
59 | {
60 | $this->body = json_decode($this->message->getBody(), true);
61 |
62 | /**
63 | * @var array $properties
64 | */
65 | $properties = $this->message->get_properties();
66 |
67 | $headers = $properties['application_headers']->getNativeData();
68 |
69 | $this->handler = $headers['handler'] ?? '';
70 | $this->applicationHeaders = $headers;
71 |
72 | $this->contentType = $properties['content_type'] ?? 'application/json';
73 | $this->contentEncoding = $properties['content_encoding'] ?? 'utf-8';
74 | $this->priority = $properties['priority'] ?? 0;
75 | $this->timestamp = $properties['timestamp'] ?? 0;
76 | }
77 |
78 | /**
79 | * Body of AMQPMessage as array
80 | */
81 | protected ?array $body;
82 |
83 | /**
84 | * Acknowledge one or more messages.
85 | */
86 | public function ack(): void
87 | {
88 | $this->message->ack();
89 | }
90 |
91 | /**
92 | * Get the instance as an array.
93 | */
94 | public function toArray(): array
95 | {
96 | return [
97 | 'headers' => $this->applicationHeaders,
98 | 'body' => $this->body,
99 | 'timestamp' => $this->timestamp,
100 | ];
101 | }
102 |
103 | /**
104 | * Returns value of application_headers in AMQPMessage
105 | */
106 | public function getApplicationHeaders(): array
107 | {
108 | return $this->applicationHeaders;
109 | }
110 |
111 | /**
112 | * Returns handler name of AMQPMessage
113 | */
114 | public function getHandler(): string
115 | {
116 | return $this->handler;
117 | }
118 |
119 | /**
120 | * Returns body of AMQPMessage as array
121 | */
122 | public function getBody(): ?array
123 | {
124 | return $this->body;
125 | }
126 |
127 | /**
128 | * Dump this class
129 | */
130 | public function dd(): void
131 | {
132 | dump($this);
133 | }
134 |
135 | /**
136 | * Get body as json
137 | */
138 | public function bodyAsJson(): bool|string
139 | {
140 | return json_encode($this->body);
141 | }
142 |
143 | /**
144 | * Print message to log
145 | */
146 | public function log($ack = true): void
147 | {
148 | $body = $this->bodyAsJson();
149 |
150 | $ack && $this->ack();
151 |
152 | echo sprintf('%s | %s', date('Y-m-d H:i:s'), $body).PHP_EOL;
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/src/MQ/MessageBuilder.php:
--------------------------------------------------------------------------------
1 | 'application/json',
35 | 'content_encoding' => 'utf-8',
36 | 'application_headers' => [],
37 | 'priority' => 0,
38 | 'correlation_id' => null,
39 | 'reply_to' => null,
40 | 'timestamp' => null,
41 | ];
42 |
43 | public function __construct(string $connection, string $to, string $type)
44 | {
45 | $this->connection = $connection;
46 | $this->to = $to;
47 | $this->type = $type;
48 |
49 | $this->properties['timestamp'] = time();
50 | }
51 |
52 | /**
53 | * Creates a Publisher instance to handle specific actions.
54 | */
55 | public function handler(string $name): Publisher
56 | {
57 | $this->addHeader('handler', $name);
58 |
59 | $message = $this->getMessage();
60 |
61 | return new Publisher($message, $this->connection, $this->type, $this->to, $this->routingKey);
62 | }
63 |
64 | /**
65 | * Get the AMQPMessage instance
66 | */
67 | private function getMessage(): AMQPMessage
68 | {
69 | $body = $this->getBodyAsJson();
70 | $properties = $this->toProperties();
71 |
72 | return new AMQPMessage($body, $properties);
73 | }
74 |
75 | /**
76 | * Set headers for the message
77 | */
78 | public function setHeaders(array $headers): self
79 | {
80 | $this->properties['application_headers'] = $headers;
81 |
82 | return $this;
83 | }
84 |
85 | /**
86 | * Add key value pair to existing headers
87 | */
88 | public function addHeader($key, $value): self
89 | {
90 | $this->properties['application_headers'][$key] = $value;
91 |
92 | return $this;
93 | }
94 |
95 | /**
96 | * Set the message body
97 | *
98 | * Stores the body data and invalidates the cached AMQPMessage.
99 | */
100 | public function setBody(array $body): self
101 | {
102 | $this->body = $body;
103 |
104 | return $this;
105 | }
106 |
107 | /**
108 | * Get the body as a JSON string
109 | */
110 | protected function getBodyAsJson(): string
111 | {
112 | return json_encode($this->body);
113 | }
114 |
115 | protected function toProperties(): array
116 | {
117 | $headers = $this->buildHeaders();
118 | $this->properties['application_headers'] = $headers;
119 |
120 | return $this->properties;
121 | }
122 |
123 | /**
124 | * Builds and returns message header
125 | */
126 | private function buildHeaders(): AMQPTable
127 | {
128 | return new AMQPTable($this->properties['application_headers']);
129 | }
130 |
131 | public function setRoutingKey(string $routingKey): self
132 | {
133 | $this->routingKey = $routingKey;
134 |
135 | return $this;
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/src/MQ/Publisher.php:
--------------------------------------------------------------------------------
1 | message = $message;
36 | $this->connection = $connection;
37 | $this->type = $type;
38 | $this->to = $to;
39 | $this->routingKey = $routingKey;
40 | }
41 |
42 | /**
43 | * Publish the message
44 | */
45 | public function publish(): void
46 | {
47 | $this->{$this->type}();
48 | }
49 |
50 | /**
51 | * Handle publishing to a Queue.
52 | *
53 | * @throws Exception
54 | */
55 | private function QUEUE(): void
56 | {
57 | $connection = $this->getConnection();
58 | $channel = $connection->getChannel();
59 |
60 | $channel->basic_publish($this->message, '', $this->to);
61 | }
62 |
63 | /**
64 | * Handle publishing to an Exchange.
65 | *
66 | * @throws Exception
67 | */
68 | private function EXCHANGE(): void
69 | {
70 | $connection = $this->getConnection();
71 | $channel = $connection->getChannel();
72 |
73 | $channel->basic_publish($this->message, $this->to, $this->routingKey);
74 | }
75 |
76 | /**
77 | * Retrieve the active RabbitMQ connection.
78 | *
79 | * @throws Exception if connection cannot be established.
80 | */
81 | private function getConnection(): Connection
82 | {
83 | $manager = App::make(ConnectionManager::class);
84 |
85 | return $manager->connection($this->connection);
86 |
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/SimpleMQ.php:
--------------------------------------------------------------------------------
1 | getManager()->connection($name);
20 | }
21 |
22 | /**
23 | * Retrieve a queue publisher for the specified queue.
24 | *
25 | * @throws Exception
26 | */
27 | public function queue(?string $queueName = null): MQ\MessageBuilder
28 | {
29 | return $this->connection()->queue($queueName);
30 | }
31 |
32 | /**
33 | * Retrieve an exchange publisher for the specified exchange.
34 | *
35 | * @throws Exception
36 | */
37 | public function exchange(string $exchangeName): MQ\MessageBuilder
38 | {
39 | return $this->connection()->exchange($exchangeName);
40 | }
41 |
42 | /**
43 | * Retrieve the ConnectionManager instance.
44 | *
45 | * @return ConnectionManager The connection manager.
46 | */
47 | protected function getManager(): ConnectionManager
48 | {
49 | // Resolve ConnectionManager only once and reuse it for consistency.
50 | return App::make(ConnectionManager::class);
51 | }
52 |
53 | /**
54 | * Set a message body directly with default connection and default queue
55 | *
56 | * @throws Exception
57 | */
58 | public function setBody(array $body): MQ\MessageBuilder
59 | {
60 | return $this->connection()->queue()->setBody($body);
61 | }
62 |
63 | /**
64 | * Set a message headers with default connection and default queue
65 | *
66 | * @throws Exception
67 | */
68 | public function setHeaders(array $headers): MQ\MessageBuilder
69 | {
70 | return $this->connection()->queue()->setHeaders($headers);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/SimpleRabbitMQServiceProvider.php:
--------------------------------------------------------------------------------
1 | app->runningInConsole()) {
19 | $this->publishes([__DIR__.'/../config/simple-mq.php' => config_path('simple-mq.php')], 'simple-mq');
20 |
21 | $this->publishes([__DIR__.'/../routes/amqp-handlers.php' => base_path('routes/amqp-handlers.php')]);
22 |
23 | $this->commands([
24 | DefineQueueCommand::class,
25 | ConsumeCommand::class,
26 | MakeHandlerCommand::class,
27 | ]);
28 | }
29 | }
30 |
31 | /**
32 | * Register the application services.
33 | */
34 | public function register(): void
35 | {
36 | // Automatically apply the package configuration
37 | $this->mergeConfigFrom(__DIR__.'/../config/simple-mq.php', 'simple-mq');
38 |
39 | $this->app->singleton('simple-rabbitmq', fn () => new SimpleMQ);
40 |
41 | $this->app->singleton(ActionMQ::class, fn () => new ActionMQ);
42 | $this->app->singleton(ConnectionManager::class, fn () => new ConnectionManager);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/tests/ExampleTest.php:
--------------------------------------------------------------------------------
1 | toBeTrue();
5 | });
6 |
--------------------------------------------------------------------------------
/tests/Pest.php:
--------------------------------------------------------------------------------
1 | in('Feature');
15 | uses(\PHPUnit\Framework\TestCase::class)->in('Unit');
16 |
17 | /*
18 | |--------------------------------------------------------------------------
19 | | Expectations
20 | |--------------------------------------------------------------------------
21 | |
22 | | When you're writing tests, you often need to check that values meet certain conditions. The
23 | | "expect()" function gives you access to a set of "expectations" methods that you can use
24 | | to assert different things. Of course, you may extend the Expectation API at any time.
25 | |
26 | */
27 |
28 | expect()->extend('toBeOne', function () {
29 | return $this->toBe(1);
30 | });
31 |
32 | /*
33 | |--------------------------------------------------------------------------
34 | | Functions
35 | |--------------------------------------------------------------------------
36 | |
37 | | While Pest is very powerful out-of-the-box, you may have some testing code specific to your
38 | | project that you don't want to repeat in every file. Here you can also expose helpers as
39 | | global functions to help you to reduce the number of lines of code in your test files.
40 | |
41 | */
42 |
43 | function something()
44 | {
45 | // ..
46 | }
47 |
--------------------------------------------------------------------------------