├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── phpunit.xml.dist ├── src ├── Adapters │ ├── AmqpQueue.php │ ├── BeanstalkdQueue.php │ ├── FileSystemQueue.php │ ├── MongoQueue.php │ ├── PdoQueue.php │ ├── PredisQueue.php │ └── RedisQueue.php ├── Contracts │ ├── Queue.php │ └── Worker.php ├── EventBusWorker.php ├── NullEvent.php └── ProducerEventBusMiddleware.php └── tests ├── Adapters ├── AmqpQueueTest.php ├── BeanstalkdQueueTest.php ├── FileSystemQueueTest.php ├── MongoQueueTest.php ├── PdoQueueTest.php ├── PredisQueueTest.php └── RedisQueueTest.php ├── DummyEvent.php ├── DummyEventHandler.php ├── EventBusWorkerTest.php └── ThrowsExceptionEventBusMiddleware.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | /build/ 3 | composer.phar 4 | composer.lock 5 | 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | language: php 4 | 5 | php: 6 | - 7 7 | 8 | matrix: 9 | fast_finish: true 10 | 11 | services: 12 | - beanstalkd 13 | - mongodb 14 | - redis-server 15 | - rabbitmq-server 16 | 17 | addons: 18 | apt: 19 | sources: 20 | - mongodb-3.0-precise 21 | packages: 22 | - mongodb-org-server 23 | 24 | before_script: 25 | - sudo apt-get install beanstalkd 26 | - sudo chmod 777 /etc/default/beanstalkd 27 | - echo "START=yes" >> /etc/default/beanstalkd && echo "BEANSTALKD_LISTEN_ADDR=127.0.0.1" >> /etc/default/beanstalkd && echo "BEANSTALKD_LISTEN_PORT=11300" >> /etc/default/beanstalkd && sudo service beanstalkd restart 28 | - echo 'extension="mongodb.so"' >> myconfig.ini && echo 'extension="redis.so"' >> myconfig.ini && phpenv config-add myconfig.ini 29 | - wget -O gnatsd.zip https://github.com/nats-io/gnatsd/releases/download/v0.8.0/gnatsd-v0.8.0-linux-amd64.zip && unzip gnatsd.zip && mv gnatsd-*-linux-amd64 gnatsd && sudo mv gnatsd/gnatsd /usr/bin/ 30 | - curl -sS https://getcomposer.org/installer | php -- --filename=composer 31 | - chmod +x composer 32 | - composer install 33 | 34 | script: 35 | - php vendor/bin/phpunit --coverage-text 36 | 37 | branches: 38 | only: 39 | - master 40 | 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Nil Portugués Calderó 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. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EventBus Queue 2 | 3 | [![Build Status](https://travis-ci.org/PHPMessageBus/eventbus-queue.svg?branch=master)](https://travis-ci.org/PHPMessageBus/eventbus-queue) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/PHPMessageBus/eventbus-queue/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/PHPMessageBus/eventbus-queue/?branch=master) [![SensioLabsInsight](https://insight.sensiolabs.com/projects/3e4f3e13-a8c1-4f1e-a5ad-42e799915dfe/mini.png?gold)](https://insight.sensiolabs.com/projects/3e4f3e13-a8c1-4f1e-a5ad-42e799915dfe) [![Latest Stable Version](https://poser.pugx.org/nilportugues/eventbus-queue/v/stable?)](https://packagist.org/packages/nilportugues/eventbus-queue) [![Total Downloads](https://poser.pugx.org/nilportugues/eventbus-queue/downloads?)](https://packagist.org/packages/nilportugues/eventbus-queue) [![License](https://poser.pugx.org/nilportugues/eventbus-queue/license?)](https://packagist.org/packages/nilportugues/eventbus-queue) 4 | [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://paypal.me/nilportugues) 5 | 6 | - [Installation](#installation) 7 | - [Usage](#usage) 8 | - [ProducerEventBus](#producereventbus) 9 | - [Consumer for the ProducerEventBus](#consumer-for-the-producereventbus) 10 | - [EventBusWorker](#eventbusworker) 11 | - [Adapter Configurations](#adapter-configurations) 12 | - [PDOQueue](#pdoqueue) 13 | - [MongoDBQueue](#mongodbqueue) 14 | - [RedisQueue](#redisqueue) 15 | - [PredisQueue](#predisqueue) 16 | - [FileSystemQueue](#filesystemqueue) 17 | - [AmqpQueue](#amqpqueue) 18 | - [BeanstalkdQueue](#beanstalkdqueue) 19 | - [Contribute](#contribute) 20 | - [Support](#support) 21 | - [Authors](#authors) 22 | - [License](#license) 23 | 24 | This package is an extension library for **[nilportugues/messagebus](http://github.com/PHPMessageBus/messagebus)**, adding queues to the EventBus implementation. 25 | 26 | This package will provide you for the classes necessary to build: 27 | 28 | - **The Producer**: code that sends the Event to a Queue by serializing the Event. This happens synchronously. 29 | - **The Consumer**: code that reads in the background, therefore asynchronously, reads and unserializes the Event from the Queue and passes it to the EventBus to do the heavy lifting. 30 | 31 | 32 | ## Why? 33 | 34 | It's all about deciding which command logic can be delayed, or hidden away in order to make it faster. And this is what we want to do. 35 | 36 | **You never remove a bottleneck, you just move it.** The downside is that we might have to assume a possible delay. 37 | 38 | ## Installation 39 | 40 | In order to start using this project you require to install it using [Composer](https://getcomposer.org): 41 | 42 | ``` 43 | composer require nilportugues/eventbus-queue 44 | ``` 45 | 46 | ## Usage 47 | 48 | This package will provide you with a new middleware: `ProducerEventBusMiddleware`. 49 | 50 | This middleware requires a serializer and a storage that will depend on the Queue Adapter used. Supported adapters are: 51 | 52 | - **PDOQueue**: queue built with a SQL database using Doctrine's DBAL. 53 | - **MongoDBQueue**: queue built with MongoDB library. 54 | - **RedisQueue**: queue using the Redis PHP extension. 55 | - **PredisQueue**: queue using the Predis library. 56 | - **FileSystemQueue**: queue built with using the local file system. 57 | - **AmqpQueue**: use RabbitMQ or any queue implementing the Amqp protocol. 58 | - **BeanstalkdQueue**: use Beanstalk as queue. 59 | 60 | To set it up, register the `ProducerEventBusMiddleware` to the Event Bus. Because we'll need to define a second EventBus (consumer), we'll call this the `ProducerEventBus`. 61 | 62 | #### ProducerEventBus 63 | 64 | ```php 65 | consume( 153 | $container->get('EventBusQueueAdapter'), 154 | $container->get('ErrorQueue'), 155 | $container->get('ConsumerEventBus') 156 | ); 157 | ``` 158 | 159 | Consumer class will run the `consume` method until all events are consumed. Then it will exit. This is optimal to make sure it will not leak memory. 160 | 161 | If you need to keep the consumer running forever use server scripts like [Supervisor](http://supervisord.org/). 162 | 163 | #### Supervisor Configuration 164 | 165 | Supervisor is a process monitor for the Linux operating system, and will automatically restart your workers if they fail. To install Supervisor on Ubuntu, you may use the following command: 166 | 167 | ```sh 168 | sudo apt-get install supervisor 169 | ``` 170 | 171 | Supervisor configuration files are typically stored in the `/etc/supervisor/conf.d` directory. Within this directory, you may create any number of configuration files that instruct how your processes should be monitored. 172 | 173 | For instance, let's create `/etc/supervisor/conf.d/my_worker.conf` so that it starts and monitors a worker script named `my_worker.php`: 174 | 175 | ```ini 176 | [program:my_worker] 177 | process_name=%(program_name)s_%(process_num)02d 178 | command=php my_worker.php 179 | autostart=true 180 | autorestart=true 181 | user=www-data 182 | numprocs=20 183 | redirect_stderr=true 184 | stdout_logfile=/var/log/my_worker.log 185 | ``` 186 | 187 | In this file, we tell Supervisor that we want 20 instances always running. If the `my_worker.php` ends or fails it will spin up a new one. 188 | 189 | In order to make this task run forever, you'll have to type in the following commands: 190 | 191 | ```sh 192 | sudo supervisorctl reread 193 | sudo supervisorctl update 194 | sudo supervisorctl start my_worker 195 | ``` 196 | 197 | ## Adapter Configurations 198 | 199 | #### PDOQueue 200 | 201 | For this to work, you'll be required to create a table in your database. 202 | 203 | For instance, sqlite dialect table creation would be: 204 | 205 | ```sql 206 | CREATE TABLE testAdapterQueue ( 207 | id INTEGER PRIMARY KEY AUTOINCREMENT, 208 | event_data TEXT NOT NULL, 209 | event_status CHAR(50), 210 | created_at INTEGER NOT NULL 211 | ); 212 | ``` 213 | 214 | #### MongoDBQueue 215 | 216 | In order to use it, you require to install PHP 7's mongodb extension. 217 | 218 | ``` 219 | sudo pecl install mongodb 220 | ``` 221 | 222 | #### RedisQueue 223 | 224 | In order to use it, you require to install PHP 7's phpredis extension. 225 | 226 | ``` 227 | # Build Redis PHP module 228 | git clone -b php7 https://github.com/phpredis/phpredis.git 229 | sudo mv phpredis/ /etc/ && \ 230 | cd /etc/phpredis \ 231 | phpize \ 232 | ./configure \ 233 | make && make install \ 234 | touch /etc/php/7.0/mods-available/redis.ini \ 235 | echo 'extension=redis.so' > /etc/php/7.0/mods-available/redis.ini 236 | ``` 237 | 238 | #### PredisQueue 239 | 240 | Nothing, but performs better if phpredis extension is found. 241 | 242 | #### FileSystemQueue 243 | 244 | Nothing to do. 245 | 246 | #### AmqpQueue 247 | 248 | Nothing to do other than having access to a amqp server. 249 | 250 | #### Beanstalkd 251 | 252 | Nothing to do other than having access to a beanstalkd server. 253 | 254 | ## Contribute 255 | 256 | Contributions to the package are always welcome! 257 | 258 | * Report any bugs or issues you find on the [issue tracker](https://github.com/PHPMessageBus/event-bus-queue/issues/new). 259 | * You can grab the source code at the package's [Git repository](https://github.com/PHPMessageBus/event-bus-queue). 260 | 261 | 262 | ## Support 263 | 264 | Get in touch with me using one of the following means: 265 | 266 | - Emailing me at 267 | - Opening an [Issue](https://github.com/PHPMessageBus/event-bus-queue/issues/new) 268 | 269 | 270 | ## Authors 271 | 272 | * [Nil Portugués Calderó](http://nilportugues.com) 273 | * [The Community Contributors](https://github.com/PHPMessageBus/event-bus-queue/graphs/contributors) 274 | 275 | 276 | ## License 277 | The code base is licensed under the [MIT license](LICENSE). 278 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nilportugues/eventbus-queue", 3 | "description": "Event Bus Queue library. Allows implementing asynchronous Event Bus.", 4 | "type": "library", 5 | "homepage": "http://nilportugues.com", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Nil Portugués Calderó", 10 | "email": "contact@nilportugues.com" 11 | } 12 | ], 13 | "require": { 14 | "php": ">=7", 15 | "nilportugues/uuid": "^1.0", 16 | "nilportugues/messagebus" : "^1.1" 17 | }, 18 | "require-dev": { 19 | "ext-redis" : "*", 20 | "phpunit/phpunit": "5.*", 21 | "fabpot/php-cs-fixer": "~1.9", 22 | "predis/predis": "1.0.*", 23 | "php-amqplib/php-amqplib": "2.6.*", 24 | "pda/pheanstalk": "3.1.*", 25 | "mongodb/mongodb": "1.0.*", 26 | "doctrine/dbal": "2.5.*" 27 | }, 28 | "minimum-stability": "dev", 29 | "prefer-stable": true, 30 | "autoload": { 31 | "psr-4": { 32 | "NilPortugues\\MessageBus\\EventBusQueue\\": "src" 33 | } 34 | }, 35 | "autoload-dev": { 36 | "psr-4": { 37 | "NilPortugues\\Tests\\MessageBus\\EventBusQueue\\": "tests/" 38 | } 39 | }, 40 | "suggest": { 41 | "ext-redis" : "To use the RedisQueue Adapter in the ProducerEventBus & EventBusWorker", 42 | "predis/predis": "To use the Predis Adapter in the ProducerEventBus & EventBusWorker", 43 | "php-amqplib/php-amqplib": "To use the AmqpQueue Adapter in the ProducerEventBus & EventBusWorker", 44 | "pda/pheanstalk": "To use the BeanstalkdQueue Adapter in the ProducerEventBus & EventBusWorker", 45 | "mongodb/mongodb": "to use the MongoQueue Adapter in the ProducerEventBus & EventBusWorker", 46 | "doctrine/dbal": "To use the PdoQueue Adapter in the ProducerEventBus & EventBusWorker" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | ./tests 33 | 34 | 35 | 36 | 37 | 38 | ./ 39 | 40 | ./tests 41 | ./vendor/ 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/Adapters/AmqpQueue.php: -------------------------------------------------------------------------------- 1 | serializer = $serializer; 36 | $this->amqpChannel = $streamConnection->channel(); 37 | $this->queueName = $queueName; 38 | } 39 | 40 | /** 41 | * Returns the name of the Queue. 42 | * 43 | * @return string 44 | */ 45 | public function name() : string 46 | { 47 | return $this->queueName; 48 | } 49 | 50 | /** 51 | * Adds an event to the Queue. 52 | * 53 | * @param Event $event 54 | */ 55 | public function push(Event $event) 56 | { 57 | $this->declareQueue(); 58 | $this->amqpChannel->basic_publish( 59 | new AMQPMessage($this->serializer->serialize($event), ['delivery_mode' => 2]), 60 | '', 61 | $this->queueName, 62 | true 63 | ); 64 | } 65 | 66 | /** 67 | * Returns an event from the Queue. 68 | * 69 | * @return Event 70 | */ 71 | public function pop() : Event 72 | { 73 | $this->declareQueue(); 74 | $message = $this->amqpChannel->basic_get($this->queueName); 75 | 76 | if (!empty($message)) { 77 | $this->amqpChannel->basic_ack($message->delivery_info['delivery_tag']); 78 | } 79 | 80 | return ($message) ? $this->serializer->unserialize($message->body) : NullEvent::create(); 81 | } 82 | 83 | /** 84 | * Returns true if queue has been fully processed or not, false otherwise. 85 | * 86 | * @return bool 87 | */ 88 | public function hasElements(): bool 89 | { 90 | $hasElements = false; 91 | 92 | $message = $this->amqpChannel->basic_get($this->queueName); 93 | if ($message) { 94 | $hasElements = true; 95 | } 96 | 97 | return $hasElements; 98 | } 99 | 100 | /** 101 | * 102 | */ 103 | protected function declareQueue() 104 | { 105 | if (false === $this->isDeclared) { 106 | $this->amqpChannel->queue_declare($this->queueName, false, false, false, false); 107 | $this->isDeclared = true; 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/Adapters/BeanstalkdQueue.php: -------------------------------------------------------------------------------- 1 | serializer = $serializer; 32 | $this->pheanstalk = $pheanstalk; 33 | $this->queueName = $queueName; 34 | } 35 | 36 | /** 37 | * Returns the name of the Queue. 38 | * 39 | * @return string 40 | */ 41 | public function name() : string 42 | { 43 | return $this->queueName; 44 | } 45 | 46 | /** 47 | * Adds an event to the Queue. 48 | * 49 | * @param Event $event 50 | */ 51 | public function push(Event $event) 52 | { 53 | $this->pheanstalk->putInTube($this->queueName, $this->serializer->serialize($event)); 54 | } 55 | 56 | /** 57 | * Returns an event from the Queue. 58 | * 59 | * @return Event 60 | */ 61 | public function pop() : Event 62 | { 63 | /** @var \Pheanstalk\Job $event */ 64 | $event = $this->pheanstalk->reserveFromTube($this->queueName); 65 | 66 | return ($event) ? $this->serializer->unserialize($event->getData()) : NullEvent::create(); 67 | } 68 | 69 | /** 70 | * Returns true if queue has been fully processed or not, false otherwise. 71 | * 72 | * @return bool 73 | */ 74 | public function hasElements(): bool 75 | { 76 | $stats = $this->pheanstalk->statsTube($this->queueName); 77 | 78 | return 0 !== $stats['current-jobs-ready']; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Adapters/FileSystemQueue.php: -------------------------------------------------------------------------------- 1 | guard($baseDirectory); 36 | 37 | $this->baseDirectory = $baseDirectory; 38 | $this->serializer = $serializer; 39 | $this->queueName = $queueName; 40 | } 41 | 42 | /** 43 | * @param string $directory 44 | * 45 | * @throws InvalidArgumentException 46 | */ 47 | protected function guard(string $directory) 48 | { 49 | if (false === \is_dir($directory)) { 50 | throw new InvalidArgumentException(\sprintf('The provided path %s is not a valid directory', $directory)); 51 | } 52 | 53 | if (false === \is_writable($directory)) { 54 | throw new InvalidArgumentException(\sprintf('The provided directory %s is not writable', $directory)); 55 | } 56 | } 57 | 58 | /** 59 | * Returns the name of the Queue. 60 | * 61 | * @return string 62 | */ 63 | public function name() : string 64 | { 65 | return $this->queueName; 66 | } 67 | 68 | /** 69 | * Adds an event to the Queue. 70 | * 71 | * @param Event $event 72 | */ 73 | public function push(Event $event) 74 | { 75 | file_put_contents($this->filePath(), $this->serializer->serialize($event).PHP_EOL); 76 | } 77 | 78 | /** 79 | * Returns an event from the Queue. 80 | * 81 | * @return Event 82 | */ 83 | public function pop() : Event 84 | { 85 | $iterator = $this->directoryIterator(); 86 | 87 | if ($this->directoryIterator()->count()) { 88 | $iteratorArray = iterator_to_array($iterator, true); 89 | $fileName = key($iteratorArray); 90 | 91 | $event = file_get_contents($this->baseDirectory.DIRECTORY_SEPARATOR.$fileName); 92 | $event = $this->serializer->unserialize($event); 93 | unlink($this->baseDirectory.DIRECTORY_SEPARATOR.$fileName); 94 | 95 | return $event; 96 | } 97 | 98 | return NullEvent::create(); 99 | } 100 | 101 | /** 102 | * Returns true if queue has been fully processed or not, false otherwise. 103 | * 104 | * @return bool 105 | */ 106 | public function hasElements(): bool 107 | { 108 | $iterator = $this->directoryIterator(); 109 | 110 | return false === empty($iterator->count()); 111 | } 112 | 113 | /** 114 | * @return string 115 | */ 116 | protected function filePath() : string 117 | { 118 | return sprintf('%s/%s.%s.job.php', $this->baseDirectory, $this->queueName, Uuid::create()); 119 | } 120 | 121 | /** 122 | * @return \GlobIterator 123 | */ 124 | protected function directoryIterator() 125 | { 126 | $iterator = new \GlobIterator( 127 | $this->baseDirectory.DIRECTORY_SEPARATOR.'*.job.php', 128 | \FilesystemIterator::KEY_AS_FILENAME 129 | ); 130 | 131 | return $iterator; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/Adapters/MongoQueue.php: -------------------------------------------------------------------------------- 1 | serializer = $serializer; 34 | $this->queueName = $queueName; 35 | $this->mongo = $client->selectCollection($databaseName, $queueName); 36 | } 37 | 38 | /** 39 | * Returns the name of the Queue. 40 | * 41 | * @return string 42 | */ 43 | public function name() : string 44 | { 45 | return $this->queueName; 46 | } 47 | 48 | /** 49 | * Adds an event to the Queue. 50 | * 51 | * @param Event $event 52 | */ 53 | public function push(Event $event) 54 | { 55 | $this->mongo->insertOne([ 56 | 'data' => $this->serializer->serialize($event), 57 | 'status' => 'pending', 58 | 'created_at' => time(), 59 | ]); 60 | } 61 | 62 | /** 63 | * Returns an event from the Queue. 64 | * 65 | * @return Event 66 | */ 67 | public function pop() : Event 68 | { 69 | $event = $this->mongo->findOneAndDelete(['status' => 'pending']); 70 | 71 | return ($event) ? $this->serializer->unserialize($event->data) : NullEvent::create(); 72 | } 73 | 74 | /** 75 | * Returns true if queue has been fully processed or not, false otherwise. 76 | * 77 | * @return bool 78 | */ 79 | public function hasElements(): bool 80 | { 81 | return 0 !== $this->mongo->count(['status' => 'pending']); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Adapters/PdoQueue.php: -------------------------------------------------------------------------------- 1 | serializer = $serializer; 33 | $this->connection = DriverManager::getConnection(['pdo' => $pdo]); 34 | $this->queueName = $queueName; 35 | } 36 | 37 | /** 38 | * Returns the name of the Queue. 39 | * 40 | * @return string 41 | */ 42 | public function name() : string 43 | { 44 | return $this->queueName; 45 | } 46 | 47 | /** 48 | * Adds an event to the Queue. 49 | * 50 | * @param Event $event 51 | */ 52 | public function push(Event $event) 53 | { 54 | $this->connection 55 | ->createQueryBuilder() 56 | ->insert($this->queueName) 57 | ->setValue('event_data', '?') 58 | ->setValue('event_status', '?') 59 | ->setValue('created_at', '?') 60 | ->setParameter(0, $this->serializer->serialize($event)) 61 | ->setParameter(1, 'pending') 62 | ->setParameter(2, time()) 63 | ->execute(); 64 | } 65 | 66 | /** 67 | * Returns an event from the Queue. 68 | * 69 | * @return Event 70 | */ 71 | public function pop() : Event 72 | { 73 | $pop = $this->connection 74 | ->createQueryBuilder() 75 | ->from($this->queueName) 76 | ->select('*') 77 | ->execute() 78 | ->fetch(); 79 | 80 | if ($pop) { 81 | $this->connection 82 | ->createQueryBuilder() 83 | ->update($this->queueName) 84 | ->set('event_status', '?') 85 | ->where('id = ?') 86 | ->setParameter(0, 'done') 87 | ->setParameter(1, $pop['id']) 88 | ->execute(); 89 | 90 | return $this->serializer->unserialize($pop['event_data']); 91 | } 92 | 93 | return NullEvent::create(); 94 | } 95 | 96 | /** 97 | * Returns true if queue has been fully processed or not, false otherwise. 98 | * 99 | * @return bool 100 | */ 101 | public function hasElements(): bool 102 | { 103 | $total = $this->connection 104 | ->executeQuery(sprintf('SELECT COUNT(*) AS totalCount FROM %s WHERE event_status = \'pending\';', $this->queueName)) 105 | ->fetch(); 106 | 107 | return 0 !== (int) $total['totalCount']; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/Adapters/PredisQueue.php: -------------------------------------------------------------------------------- 1 | predis = $predisDriver; 32 | $this->queueName = $queueName; 33 | $this->serializer = $serializer; 34 | } 35 | 36 | /** 37 | * Returns the name of the Queue. 38 | * 39 | * @return string 40 | */ 41 | public function name() : string 42 | { 43 | return $this->queueName; 44 | } 45 | 46 | /** 47 | * Adds an event to the Queue. 48 | * 49 | * @param Event $event 50 | */ 51 | public function push(Event $event) 52 | { 53 | $this->predis->rpush( 54 | $this->queueName, 55 | $this->serializer->serialize($event) 56 | ); 57 | } 58 | 59 | /** 60 | * Returns an event from the Queue. 61 | * 62 | * @return Event 63 | */ 64 | public function pop() : Event 65 | { 66 | $last = $this->predis->lPop($this->queueName); 67 | 68 | return ($last) ? $this->serializer->unserialize($last) : NullEvent::create(); 69 | } 70 | 71 | /** 72 | * Returns true if queue has been fully processed or not, false otherwise. 73 | * 74 | * @return bool 75 | */ 76 | public function hasElements(): bool 77 | { 78 | return 0 !== $this->predis->lLen($this->queueName); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Adapters/RedisQueue.php: -------------------------------------------------------------------------------- 1 | redis = $redis; 32 | 33 | $this->queueName = $queueName; 34 | $this->serializer = $serializer; 35 | } 36 | 37 | /** 38 | * Returns the name of the Queue. 39 | * 40 | * @return string 41 | */ 42 | public function name() : string 43 | { 44 | return $this->queueName; 45 | } 46 | 47 | /** 48 | * Adds an event to the Queue. 49 | * 50 | * @param Event $event 51 | */ 52 | public function push(Event $event) 53 | { 54 | $this->redis->rPush($this->queueName, $this->serializer->serialize($event)); 55 | } 56 | 57 | /** 58 | * Returns an event from the Queue. 59 | * 60 | * @return Event 61 | */ 62 | public function pop() : Event 63 | { 64 | $last = $this->redis->lPop($this->queueName); 65 | 66 | return ($last) ? $this->serializer->unserialize($last) : NullEvent::create(); 67 | } 68 | 69 | /** 70 | * Returns true if queue has been fully processed or not, false otherwise. 71 | * 72 | * @return bool 73 | */ 74 | public function hasElements(): bool 75 | { 76 | return 0 !== $this->redis->lLen($this->queueName); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Contracts/Queue.php: -------------------------------------------------------------------------------- 1 | pop()) { 20 | try { 21 | if (false === $event instanceof NullEvent) { 22 | $worker($event); 23 | } else { 24 | break; 25 | } 26 | } catch (Exception $e) { 27 | if (!empty($event) && (false === $event instanceof NullEvent)) { 28 | $errorQueue->push($event); 29 | } 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/NullEvent.php: -------------------------------------------------------------------------------- 1 | 4 | * Date: 29/03/16 5 | * Time: 22:40. 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | namespace NilPortugues\MessageBus\EventBusQueue; 12 | 13 | use NilPortugues\MessageBus\EventBus\Contracts\Event; 14 | 15 | /** 16 | * Class NullEvent. 17 | */ 18 | class NullEvent implements Event 19 | { 20 | /** 21 | * @var NullEvent The reference to *Singleton* instance of this class 22 | */ 23 | private static $instance; 24 | 25 | /** 26 | * Protected constructor to prevent creating a new instance of the 27 | * *Singleton* via the `new` operator from outside of this class. 28 | */ 29 | protected function __construct() 30 | { 31 | } 32 | 33 | /** 34 | * Returns the *Singleton* instance of this class. 35 | * 36 | * @return NullEvent The *Singleton* instance. 37 | */ 38 | public static function create() : NullEvent 39 | { 40 | if (null === static::$instance) { 41 | static::$instance = new static(); 42 | } 43 | 44 | return static::$instance; 45 | } 46 | 47 | /** 48 | * Private clone method to prevent cloning of the instance of the 49 | * *Singleton* instance. 50 | * 51 | * @codeCoverageIgnore 52 | */ 53 | private function __clone() 54 | { 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/ProducerEventBusMiddleware.php: -------------------------------------------------------------------------------- 1 | queue = $queue; 22 | } 23 | 24 | /** 25 | * @param Event $event 26 | * @param callable|null $next 27 | */ 28 | public function __invoke(Event $event, callable $next = null) 29 | { 30 | $this->queue->push($event); 31 | 32 | if ($next) { 33 | $next($event); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/Adapters/AmqpQueueTest.php: -------------------------------------------------------------------------------- 1 | 4 | * Date: 7/04/16 5 | * Time: 22:33. 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | namespace NilPortugues\Tests\MessageBus\EventBusQueue\Adapters; 12 | 13 | use NilPortugues\MessageBus\EventBusQueue\Adapters\AmqpQueue; 14 | use NilPortugues\MessageBus\Serializer\NativeSerializer; 15 | use NilPortugues\Tests\MessageBus\EventBusQueue\DummyEvent; 16 | use PhpAmqpLib\Connection\AMQPStreamConnection; 17 | 18 | class AmqpQueueTest extends \PHPUnit_Framework_TestCase 19 | { 20 | /** @var AMQPStreamConnection */ 21 | protected $producerConnection; 22 | 23 | /** @var AMQPStreamConnection */ 24 | protected $consumerConnection; 25 | 26 | /** @var NativeSerializer */ 27 | protected $serializer; 28 | 29 | /** @var AmqpQueue */ 30 | protected $consumer; 31 | 32 | /** @var AmqpQueue */ 33 | protected $producer; 34 | 35 | public function setUp() 36 | { 37 | $this->consumerConnection = new AMQPStreamConnection('127.0.0.1', 5672, 'guest', 'guest'); 38 | $this->producerConnection = new AMQPStreamConnection('127.0.0.1', 5672, 'guest', 'guest'); 39 | $this->serializer = new NativeSerializer(); 40 | 41 | $this->producer = new AmqpQueue($this->serializer, $this->producerConnection, 'testAdapterQueue'); 42 | $this->consumer = new AmqpQueue($this->serializer, $this->consumerConnection, 'testAdapterQueue'); 43 | } 44 | 45 | public function testAdapterQueue() 46 | { 47 | $event = new DummyEvent(); 48 | $this->producer->push($event); 49 | 50 | $this->assertEquals($event, $this->consumer->pop()); 51 | } 52 | 53 | public function testName() 54 | { 55 | $this->assertEquals('testAdapterQueue', $this->consumer->name()); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/Adapters/BeanstalkdQueueTest.php: -------------------------------------------------------------------------------- 1 | 4 | * Date: 7/04/16 5 | * Time: 22:57. 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | namespace NilPortugues\Tests\MessageBus\EventBusQueue\Adapters; 12 | 13 | use NilPortugues\MessageBus\EventBusQueue\Adapters\BeanstalkdQueue; 14 | use NilPortugues\MessageBus\Serializer\NativeSerializer; 15 | use NilPortugues\Tests\MessageBus\EventBusQueue\DummyEvent; 16 | use Pheanstalk\Pheanstalk; 17 | 18 | class BeanstalkdQueueTest extends \PHPUnit_Framework_TestCase 19 | { 20 | /** @var Pheanstalk */ 21 | protected $producerConnection; 22 | 23 | /** @var Pheanstalk */ 24 | protected $consumerConnection; 25 | 26 | /** @var NativeSerializer */ 27 | protected $serializer; 28 | 29 | /** @var BeanstalkdQueue */ 30 | protected $consumer; 31 | 32 | /** @var BeanstalkdQueue */ 33 | protected $producer; 34 | 35 | public function setUp() 36 | { 37 | $this->consumerConnection = new Pheanstalk('127.0.0.1'); 38 | $this->producerConnection = new Pheanstalk('127.0.0.1'); 39 | $this->serializer = new NativeSerializer(); 40 | 41 | $this->producer = new BeanstalkdQueue($this->serializer, $this->producerConnection, 'testAdapterQueue'); 42 | $this->consumer = new BeanstalkdQueue($this->serializer, $this->consumerConnection, 'testAdapterQueue'); 43 | } 44 | 45 | public function testAdapterQueue() 46 | { 47 | $event = new DummyEvent(); 48 | $this->producer->push($event); 49 | 50 | $this->assertTrue($this->producer->hasElements()); 51 | $this->assertEquals($event, $this->consumer->pop()); 52 | } 53 | 54 | public function testName() 55 | { 56 | $this->assertEquals('testAdapterQueue', $this->consumer->name()); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/Adapters/FileSystemQueueTest.php: -------------------------------------------------------------------------------- 1 | 4 | * Date: 7/04/16 5 | * Time: 23:03. 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | namespace NilPortugues\Tests\MessageBus\EventBusQueue\Adapters; 12 | 13 | use InvalidArgumentException; 14 | use NilPortugues\MessageBus\EventBusQueue\Adapters\FileSystemQueue; 15 | use NilPortugues\MessageBus\EventBusQueue\NullEvent; 16 | use NilPortugues\MessageBus\Serializer\NativeSerializer; 17 | use NilPortugues\Tests\MessageBus\EventBusQueue\DummyEvent; 18 | 19 | class FileSystemQueueTest extends \PHPUnit_Framework_TestCase 20 | { 21 | /** @var NativeSerializer */ 22 | protected $serializer; 23 | 24 | /** @var FileSystemQueue */ 25 | protected $consumer; 26 | 27 | /** @var FileSystemQueue */ 28 | protected $producer; 29 | 30 | /** @var string */ 31 | protected $dirPath; 32 | 33 | public function setUp() 34 | { 35 | $this->serializer = new NativeSerializer(); 36 | 37 | $path = __DIR__.'/../jobs/'; 38 | if (false === file_exists($path)) { 39 | mkdir($path, 0777, true); 40 | } 41 | 42 | $this->dirPath = realpath($path); 43 | 44 | $this->producer = new FileSystemQueue($this->serializer, $this->dirPath, 'testAdapterQueue'); 45 | $this->consumer = new FileSystemQueue($this->serializer, $this->dirPath, 'testAdapterQueue'); 46 | } 47 | 48 | public function testAdapterQueue() 49 | { 50 | $event = new DummyEvent(); 51 | $this->producer->push($event); 52 | 53 | $this->assertTrue($this->producer->hasElements()); 54 | $this->assertEquals($event, $this->consumer->pop()); 55 | } 56 | 57 | public function testAdapterQueueReturnNullEvent() 58 | { 59 | $queue = new FileSystemQueue($this->serializer, '.', 'testAdapterQueue'); 60 | $this->assertInstanceOf(NullEvent::class, $queue->pop()); 61 | } 62 | 63 | public function testName() 64 | { 65 | $this->assertEquals('testAdapterQueue', $this->consumer->name()); 66 | } 67 | 68 | public function testItWillThrowExceptionIfDirectoryDoesNotExist() 69 | { 70 | $this->expectException(InvalidArgumentException::class); 71 | 72 | new FileSystemQueue($this->serializer, '/nope', 'testAdapterQueue'); 73 | } 74 | 75 | public function testItThrowsExceptionWhenDirDoesNotExist() 76 | { 77 | $this->expectException(InvalidArgumentException::class); 78 | 79 | new FileSystemQueue($this->serializer, '/nope', 'testAdapterQueue'); 80 | } 81 | 82 | public function testItThrowsExceptionWhenDirIsNotWritable() 83 | { 84 | $this->expectException(InvalidArgumentException::class); 85 | 86 | new FileSystemQueue($this->serializer, '/', 'testAdapterQueue'); 87 | } 88 | 89 | public function tearDown() 90 | { 91 | if (file_exists($this->dirPath)) { 92 | foreach (\glob("{$this->dirPath}/*") as $file) { 93 | unlink($file); 94 | } 95 | } 96 | rmdir($this->dirPath); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /tests/Adapters/MongoQueueTest.php: -------------------------------------------------------------------------------- 1 | 4 | * Date: 8/04/16 5 | * Time: 0:34. 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | namespace NilPortugues\Tests\MessageBus\EventBusQueue\Adapters; 12 | 13 | use MongoDB\Client; 14 | use NilPortugues\MessageBus\EventBusQueue\Adapters\MongoQueue; 15 | use NilPortugues\MessageBus\Serializer\NativeSerializer; 16 | use NilPortugues\Tests\MessageBus\EventBusQueue\DummyEvent; 17 | 18 | class MongoQueueTest extends \PHPUnit_Framework_TestCase 19 | { 20 | /** @var Client */ 21 | protected $producerConnection; 22 | 23 | /** @var Client */ 24 | protected $consumerConnection; 25 | 26 | /** @var NativeSerializer */ 27 | protected $serializer; 28 | 29 | /** @var MongoQueue */ 30 | protected $consumer; 31 | 32 | /** @var MongoQueue */ 33 | protected $producer; 34 | 35 | public function setUp() 36 | { 37 | $this->consumerConnection = new Client(); 38 | $this->producerConnection = new Client(); 39 | $this->serializer = new NativeSerializer(); 40 | 41 | $this->producer = new MongoQueue($this->serializer, $this->producerConnection, 'test', 'testAdapterQueue'); 42 | $this->consumer = new MongoQueue($this->serializer, $this->consumerConnection, 'test', 'testAdapterQueue'); 43 | } 44 | 45 | public function testAdapterQueue() 46 | { 47 | $event = new DummyEvent(); 48 | $this->producer->push($event); 49 | 50 | $this->assertTrue($this->producer->hasElements()); 51 | $this->assertEquals($event, $this->consumer->pop()); 52 | } 53 | 54 | public function testName() 55 | { 56 | $this->assertEquals('testAdapterQueue', $this->consumer->name()); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/Adapters/PdoQueueTest.php: -------------------------------------------------------------------------------- 1 | 4 | * Date: 8/04/16 5 | * Time: 0:36. 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | namespace NilPortugues\Tests\MessageBus\EventBusQueue\Adapters; 12 | 13 | use NilPortugues\MessageBus\EventBusQueue\Adapters\PdoQueue; 14 | use NilPortugues\MessageBus\EventBusQueue\NullEvent; 15 | use NilPortugues\MessageBus\Serializer\NativeSerializer; 16 | use NilPortugues\Tests\MessageBus\EventBusQueue\DummyEvent; 17 | use PDO; 18 | 19 | class PdoQueueTest extends \PHPUnit_Framework_TestCase 20 | { 21 | /** @var PDO */ 22 | protected $producerConnection; 23 | 24 | /** @var PDO */ 25 | protected $consumerConnection; 26 | 27 | /** @var NativeSerializer */ 28 | protected $serializer; 29 | 30 | /** @var PdoQueue */ 31 | protected $consumer; 32 | 33 | /** @var PdoQueue */ 34 | protected $producer; 35 | 36 | public function setUp() 37 | { 38 | $this->producerConnection = $this->consumerConnection = new PDO('sqlite::memory:'); 39 | $this->producerConnection->exec('CREATE TABLE testAdapterQueue ( 40 | id INTEGER PRIMARY KEY AUTOINCREMENT, 41 | event_data TEXT NOT NULL, 42 | event_status CHAR(50), 43 | created_at INTEGER NOT NULL 44 | );' 45 | ); 46 | 47 | $this->serializer = new NativeSerializer(); 48 | 49 | $this->producer = new PdoQueue($this->serializer, $this->producerConnection, 'testAdapterQueue'); 50 | $this->consumer = new PdoQueue($this->serializer, $this->consumerConnection, 'testAdapterQueue'); 51 | } 52 | 53 | public function testAdapterQueue() 54 | { 55 | $event = new DummyEvent(); 56 | $this->producer->push($event); 57 | 58 | $this->assertTrue($this->producer->hasElements()); 59 | $this->assertEquals($event, $this->consumer->pop()); 60 | } 61 | 62 | public function testAdapterQueueReturnNullEvent() 63 | { 64 | $queue = new PdoQueue($this->serializer, $this->producerConnection, 'testAdapterQueue'); 65 | $this->assertInstanceOf(NullEvent::class, $queue->pop()); 66 | } 67 | 68 | public function testName() 69 | { 70 | $this->assertEquals('testAdapterQueue', $this->consumer->name()); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tests/Adapters/PredisQueueTest.php: -------------------------------------------------------------------------------- 1 | 4 | * Date: 8/04/16 5 | * Time: 1:00. 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | namespace NilPortugues\Tests\MessageBus\EventBusQueue\Adapters; 12 | 13 | use NilPortugues\MessageBus\EventBusQueue\Adapters\PredisQueue; 14 | use NilPortugues\MessageBus\Serializer\NativeSerializer; 15 | use NilPortugues\Tests\MessageBus\EventBusQueue\DummyEvent; 16 | use Predis\Client; 17 | 18 | class PredisQueueTest extends \PHPUnit_Framework_TestCase 19 | { 20 | /** @var \Predis\Client */ 21 | protected $producerConnection; 22 | 23 | /** @var \Predis\Client */ 24 | protected $consumerConnection; 25 | 26 | /** @var NativeSerializer */ 27 | protected $serializer; 28 | 29 | /** @var PredisQueue */ 30 | protected $consumer; 31 | 32 | /** @var PredisQueue */ 33 | protected $producer; 34 | 35 | /** @var Client */ 36 | protected $predis; 37 | 38 | public function setUp() 39 | { 40 | $this->predis = new Client(); 41 | 42 | $this->consumerConnection = $this->predis; 43 | $this->producerConnection = $this->predis; 44 | $this->serializer = new NativeSerializer(); 45 | 46 | $this->producer = new PredisQueue($this->serializer, $this->producerConnection, 'testAdapterQueue'); 47 | $this->consumer = new PredisQueue($this->serializer, $this->consumerConnection, 'testAdapterQueue'); 48 | } 49 | 50 | public function testAdapterQueue() 51 | { 52 | $event = new DummyEvent(); 53 | $this->producer->push($event); 54 | 55 | $this->assertTrue($this->producer->hasElements()); 56 | $this->assertEquals($event, $this->consumer->pop()); 57 | } 58 | 59 | public function testName() 60 | { 61 | $this->assertEquals('testAdapterQueue', $this->consumer->name()); 62 | } 63 | 64 | public function tearDown() 65 | { 66 | $this->predis->del($this->consumer->name()); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /tests/Adapters/RedisQueueTest.php: -------------------------------------------------------------------------------- 1 | 4 | * Date: 8/04/16 5 | * Time: 1:00. 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | namespace NilPortugues\Tests\MessageBus\EventBusQueue\Adapters; 12 | 13 | use NilPortugues\MessageBus\EventBusQueue\Adapters\RedisQueue; 14 | use NilPortugues\MessageBus\Serializer\NativeSerializer; 15 | use NilPortugues\Tests\MessageBus\EventBusQueue\DummyEvent; 16 | use Redis; 17 | 18 | class RedisQueueTest extends \PHPUnit_Framework_TestCase 19 | { 20 | /** @var Redis */ 21 | protected $producerConnection; 22 | 23 | /** @var Redis */ 24 | protected $consumerConnection; 25 | 26 | /** @var NativeSerializer */ 27 | protected $serializer; 28 | 29 | /** @var RedisQueue */ 30 | protected $consumer; 31 | 32 | /** @var RedisQueue */ 33 | protected $producer; 34 | 35 | /** @var Redis */ 36 | protected $redis; 37 | 38 | public function setUp() 39 | { 40 | $this->redis = new Redis(); 41 | $this->redis->connect('127.0.0.1'); 42 | 43 | $this->consumerConnection = $this->redis; 44 | $this->producerConnection = $this->redis; 45 | $this->serializer = new NativeSerializer(); 46 | 47 | $this->producer = new RedisQueue($this->serializer, $this->producerConnection, 'testAdapterQueue'); 48 | $this->consumer = new RedisQueue($this->serializer, $this->consumerConnection, 'testAdapterQueue'); 49 | } 50 | 51 | public function testAdapterQueue() 52 | { 53 | $event = new DummyEvent(); 54 | $this->producer->push($event); 55 | 56 | $this->assertTrue($this->producer->hasElements()); 57 | $this->assertEquals($event, $this->consumer->pop()); 58 | } 59 | 60 | public function testName() 61 | { 62 | $this->assertEquals('testAdapterQueue', $this->consumer->name()); 63 | } 64 | 65 | public function tearDown() 66 | { 67 | $this->redis->del($this->consumer->name()); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tests/DummyEvent.php: -------------------------------------------------------------------------------- 1 | handlers = [ 48 | DummyEventHandler::class => function () { 49 | return new DummyEventHandler(); 50 | }, 51 | ]; 52 | 53 | $this->translator = new EventFullyQualifiedClassNameStrategy([ 54 | DummyEventHandler::class, 55 | ]); 56 | 57 | $this->resolver = new SimpleArrayResolver($this->handlers); 58 | $this->serializer = new NativeSerializer(); 59 | 60 | $this->setUpQueue(); 61 | 62 | $this->producerEventBus = new EventBus([ 63 | new ProducerEventBusMiddleware($this->consumerQueue), 64 | new EventBusMiddleware($this->translator, $this->resolver), 65 | ]); 66 | 67 | $this->consumerEventBus = new EventBus([ 68 | new EventBusMiddleware($this->translator, $this->resolver), 69 | ]); 70 | } 71 | 72 | protected function setUpQueue() 73 | { 74 | mkdir(__DIR__.'/jobs', 0777, true); 75 | mkdir(__DIR__.'/errored-jobs', 0777, true); 76 | $this->consumerQueue = new FileSystemQueue($this->serializer, __DIR__.'/jobs', $this->queueName); 77 | $this->errorQueue = new FileSystemQueue($this->serializer, __DIR__.'/errored-jobs', $this->queueName); 78 | } 79 | 80 | public function testItCanConsume() 81 | { 82 | for ($i = 1; $i <= 10; ++$i) { 83 | $this->producerEventBus->__invoke(new DummyEvent()); 84 | } 85 | $consumer = new EventBusWorker(); 86 | $consumer->consume($this->consumerQueue, $this->errorQueue, $this->consumerEventBus); 87 | } 88 | 89 | public function testItCanCatchExceptionWhileConsume() 90 | { 91 | for ($i = 1; $i <= 10; ++$i) { 92 | $this->producerEventBus->__invoke(new DummyEvent()); 93 | } 94 | 95 | $this->consumerEventBus = new EventBus([ 96 | new ThrowsExceptionEventBusMiddleware(), 97 | new EventBusMiddleware($this->translator, $this->resolver), 98 | ]); 99 | 100 | $consumer = new EventBusWorker(); 101 | $consumer->consume($this->consumerQueue, $this->errorQueue, $this->consumerEventBus); 102 | } 103 | 104 | public function tearDown() 105 | { 106 | $this->emptyDir(__DIR__.'/errored-jobs'); 107 | $this->emptyDir(__DIR__.'/jobs'); 108 | } 109 | 110 | protected function emptyDir($path) 111 | { 112 | if (file_exists($path)) { 113 | foreach (\glob($path.'/*') as $file) { 114 | unlink($file); 115 | } 116 | } 117 | rmdir($path); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /tests/ThrowsExceptionEventBusMiddleware.php: -------------------------------------------------------------------------------- 1 |