├── .gitignore ├── .travis.yml ├── composer.json ├── config └── tail.php ├── license ├── phpunit.xml ├── readme.md ├── src ├── BaseOptions.php ├── Connection.php ├── Exceptions │ ├── InvalidConnectionException.php │ └── InvalidOptionException.php ├── Facades │ └── Tail.php ├── Listener.php ├── LumenServiceProvider.php ├── Message.php ├── ServiceProvider.php └── Tail.php └── tests └── testBaseOptions.php /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /vendor 3 | composer.lock -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.4 5 | - 5.5 6 | - 5.6 7 | - hhvm 8 | 9 | before_script: 10 | - composer self-update 11 | - composer install --prefer-source --no-interaction --dev 12 | 13 | # script: build.sh 14 | script: phpunit --configuration phpunit.xml --coverage-text -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mookofe/tail", 3 | "description": "RabbitMQ and PHP client for Laravel and Lumen that allows you to add and listen queues messages just simple", 4 | "keywords": ["laravel5", "laravel", "rabbitmq", "queue", "library", "package", "client", "Victor Cruz", "mookofe" ], 5 | "license": "MIT", 6 | "version": "v1.0.5", 7 | "authors": [ 8 | { 9 | "name": "Victor Cruz", 10 | "email": "cruzrosario@gmail.com" 11 | } 12 | ], 13 | "require": { 14 | "php": ">=5.4.7", 15 | "php-amqplib/php-amqplib": "2.*", 16 | "illuminate/support": "5.*" 17 | }, 18 | "require-dev": { 19 | "phpunit/phpunit": "4.0.*", 20 | "mockery/mockery": "dev-master" 21 | }, 22 | "autoload": { 23 | "psr-4": { 24 | "Mookofe\\Tail\\": "src/" 25 | } 26 | }, 27 | "minimum-stability": "stable" 28 | } -------------------------------------------------------------------------------- /config/tail.php: -------------------------------------------------------------------------------- 1 | 'default_connection', 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Queues Connections 21 | |-------------------------------------------------------------------------- 22 | */ 23 | 24 | 'connections' => array( 25 | 26 | 'default_connection' => array( 27 | 'host' => 'localhost', 28 | 'port' => 5672, 29 | 'username' => 'guest', 30 | 'password' => 'guest', 31 | 'vhost' => '/', 32 | 'ssl_context_options' => null, 33 | 'connection_timeout' => 3.0, 34 | 'read_write_timeout' => 3.0, 35 | 'keepalive' => false, 36 | 'heartbeat' => 0, 37 | 'exchange' => 'amq.direct', 38 | 'consumer_tag' => 'consumer', 39 | 'exchange_type' => 'direct', 40 | 'content_type' => 'text/plain', 41 | ), 42 | ), 43 | ); -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Victor Cruz 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. -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | ./tests/ 15 | 16 | 17 | 18 | 19 | 20 | src/ 21 | 22 | 23 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | mookofe/tail 2 | ========= 3 | 4 | RabbitMQ and PHP client for Laravel and Lumen that allows you to add and listen queues messages just simple. 5 | 6 | [![Build Status](https://travis-ci.org/mookofe/tail.svg?branch=master)](https://travis-ci.org/mookofe/tail) 7 | [![Latest Stable Version](https://poser.pugx.org/mookofe/tail/v/stable.svg)](https://packagist.org/packages/mookofe/tail) 8 | [![License](https://poser.pugx.org/mookofe/tail/license.svg)](https://packagist.org/packages/mookofe/tail) 9 | 10 | Features 11 | ---- 12 | - Simple queue configuration 13 | - Multiple server connections 14 | - Add message to queues easily 15 | - Listen queues with useful options 16 | 17 | 18 | Requirements 19 | ---- 20 | - php-amqplib/php-amqplib: 2.* 21 | 22 | 23 | Version 24 | ---- 25 | 1.0.5 26 | 27 | 28 | Installation 29 | -------------- 30 | 31 | **Preparation** 32 | 33 | Open your composer.json file and add the following to the require array: 34 | 35 | ```js 36 | "mookofe/tail": "1.*" 37 | ``` 38 | 39 | **Install dependencies** 40 | 41 | ``` 42 | $ composer install 43 | ``` 44 | 45 | Or 46 | 47 | ```batch 48 | $ composer update 49 | ``` 50 | 51 | Integration 52 | -------------- 53 | ### Laravel 54 | After installing the package, open your Laravel config file **config/app.php** and add the following lines. 55 | 56 | In the $providers array add the following service provider for this package. 57 | 58 | ```batch 59 | Mookofe\Tail\ServiceProvider::class, 60 | ``` 61 | 62 | In the $aliases array add the following facade for this package. 63 | 64 | ```batch 65 | 'Tail' => Mookofe\Tail\Facades\Tail::class, 66 | ``` 67 | 68 | Add servers connection file running: 69 | 70 | ```batch 71 | $ php artisan vendor:publish --provider="Mookofe\Tail\ServiceProvider" --tag="config" 72 | ``` 73 | 74 | ### Lumen 75 | Register the Lumen Service Provider in **bootstrap/app.php**: 76 | 77 | ```php 78 | /* 79 | |-------------------------------------------------------------------------- 80 | | Register Service Providers 81 | |-------------------------------------------------------------------------- 82 | */ 83 | 84 | //... 85 | 86 | $app->configure('tail-settings'); 87 | $app->register(Mookofe\Tail\LumenServiceProvider::class); 88 | 89 | //... 90 | ``` 91 | 92 | Make sure sure `$app->withFacades();` is uncomment in your **bootstrap/app.php** file 93 | 94 | 95 | Create a **config** folder in the root directory of your Lumen application and copy the content 96 | from **vendor/mookofe/tail/config/tail.php** to **config/tail-settings.php**. 97 | 98 | RabbitMQ Connections 99 | -------------- 100 | By default the library will use the RabbitMQ installation credentials (on a fresh installation the user "guest" is created with password "guest"). 101 | 102 | To override the default connection or add more servers, edit the RabbitMQ connections file at: **config/tail-settings.php** 103 | 104 | ```php 105 | return array( 106 | 107 | 'default' => 'default_connection', 108 | 109 | 'connections' => array( 110 | 111 | 'default_connection' => array( 112 | 'host' => 'localhost', 113 | 'port' => 5672, 114 | 'username' => 'guest', 115 | 'password' => 'guest', 116 | 'vhost' => '/', 117 | 'ssl_context_options' => null, 118 | 'connection_timeout' => 3, 119 | 'read_write_timeout' => 50, //should be at least 2x heartbeat (if using heartbeat) 120 | 'keepalive' => true, //requires php-amqplib v2.4.1+ 121 | 'heartbeat' => 25, //requires php-amqplib v2.4.1+ 122 | 'exchange' => 'default_exchange_name', 123 | 'consumer_tag' => 'consumer', 124 | 'exchange_type' => 'direct', 125 | 'content_type' => 'text/plain' 126 | ), 127 | 'other_server' => array( 128 | 'host' => '192.168.0.10', 129 | 'port' => 5672, 130 | 'username' => 'guest', 131 | 'password' => 'guest', 132 | 'vhost' => '/', 133 | 'ssl_context_options' => array( 134 | 'capath' => '/etc/ssl/certs', 135 | 'cafile' => './startssl_ca.pem', 136 | 'verify_peer' => true, 137 | ), 138 | 'connection_timeout' => 3.0, 139 | 'read_write_timeout' => 3.0, 140 | 'keepalive' => false, 141 | 'heartbeat' => 0, 142 | 'exchange' => 'default_exchange_name', 143 | 'consumer_tag' => 'consumer', 144 | 'exchange_type' => 'fanout', 145 | 'content_type' => 'application/json' 146 | ), 147 | ), 148 | ); 149 | ``` 150 | 151 | 152 | 153 | Adding messages to queue: 154 | ---- 155 | 156 | **Adding a simple message** 157 | 158 | ```php 159 | Tail::add('queue-name', 'message'); 160 | ``` 161 | 162 | **Adding message changing RabbitMQ server** 163 | 164 | ```php 165 | Tail::add('queue-name', 'message', array('connection_name' => 'connection_name_config_file')); 166 | ``` 167 | 168 | 169 | **Adding message with different exchange** 170 | 171 | ```php 172 | Tail::add('queue-name', 'message', array('exchange' => 'exchange_name')); 173 | ``` 174 | 175 | **Adding message with different content type** 176 | 177 | ```php 178 | Tail::add('queue-name', '{ 'message' : 'message' }', array('content_type' => 'application/json')); 179 | ``` 180 | 181 | 182 | **Adding message with different options** 183 | 184 | ```php 185 | $options = array ( 186 | 'connection_name' => 'connection_name_config_file', 187 | 'exchange' => 'exchange_name', 188 | 'vhost' => 'vhost' 189 | ); 190 | 191 | Tail::add('queue-name', 'message', $options); 192 | ``` 193 | 194 | 195 | **Using Tail object** 196 | 197 | ```php 198 | $message = new Tail::createMessage; 199 | $message->queue_name = 'queue-name'; 200 | $message->message = 'message'; 201 | $message->connection_name = 'connection_name_in_config_file'; 202 | $message->exchange = 'exchange_name'; 203 | $message->vhost = 'vhost'; 204 | $message->content_type = 'content/type' 205 | 206 | $message->save(); 207 | ``` 208 | 209 | Listening queues: 210 | ---- 211 | 212 | **Closure based listener** 213 | 214 | ```php 215 | Tail::listen('queue-name', function ($message) { 216 | 217 | //Your message logic code 218 | }); 219 | ``` 220 | 221 | **Closure listener with options** 222 | 223 | ```php 224 | $options = array( 225 | 'message_limit' => 50, 226 | 'time' => 60, 227 | 'empty_queue_timeout' => 5, 228 | 'connection_name' => 'connection_name_in_config_file', 229 | 'exchange' => 'exchange_name', 230 | 'vhost' => 'vhost' 231 | ); 232 | 233 | Tail::listenWithOptions('queue-name', $options, function ($message) { 234 | 235 | //Your message logic code 236 | }); 237 | ``` 238 | 239 | **Options definitions:** 240 | 241 | | Name | Description | Default value| 242 | |---|---|---| 243 | | queue_name | Queue name on RabbitMQ | * Required | 244 | | message_limit | Number of messages to be processed | 0: Unlimited | 245 | | time | Time in seconds the process will be running | 0: Unlimited | 246 | | empty\_queue\_timeout | Time in seconds to kill listening when the queue is empty | 0: Unlimited | 247 | | connection_name | Server connection name | Defined at connections file | 248 | | exchange | Exchange name on RabbitMQ Server | Specified on connections file | 249 | | vhost | Virtual host on RabbitMQ Server | Specified on connections file | 250 | 251 | 252 | By default the listen process will be running forever unless you specify one of the running time arguments above (message\_limit, time, empty\_queue\_timeout). They can be mixed all together, so when one of the condition is met the process will be stopped. 253 | 254 | 255 | 256 | License 257 | ---- 258 | This package is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT) -------------------------------------------------------------------------------- /src/BaseOptions.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class BaseOptions { 13 | 14 | /** 15 | * Valid options array, include all valid options can be set 16 | * 17 | * @var array 18 | */ 19 | protected $allowedOptions = array('exchange', 'exchange_type', 'vhost', 'connection_name', 'queue_name', 'content_type'); 20 | 21 | /** 22 | * Config repository dependency 23 | * 24 | * @var Illuminate\Config\Repository 25 | */ 26 | protected $config; 27 | 28 | /** 29 | * Exchange name on RabbitMQ Server 30 | * 31 | * @var string 32 | */ 33 | public $exchange; 34 | 35 | /** 36 | * Virtual host name on RabbitMQ Server 37 | * 38 | * @var string 39 | */ 40 | public $vhost; 41 | 42 | /** 43 | * Connection name defined in config/tail-settings.php file 44 | * 45 | * @var string 46 | */ 47 | public $connection_name; 48 | 49 | /** 50 | * Queue name for this connection 51 | * 52 | * @var string 53 | */ 54 | public $queue_name; 55 | 56 | /** 57 | * RabbitMQ AMQP exchange type 58 | * should be one of: 59 | * direct, fanout, topic or headers 60 | * 61 | * @var string 62 | */ 63 | public $exchange_type; 64 | 65 | /** 66 | * Content-Type for the messages send over this connection 67 | * 68 | * @var string 69 | */ 70 | public $content_type; 71 | 72 | /** 73 | * Constructor 74 | * 75 | * @param Illuminate\Config\Repository $config Config dependency 76 | * 77 | * @return Mookofe\Tail\BaseOptions 78 | */ 79 | public function __construct(Repository $config) 80 | { 81 | $this->config = $config; 82 | } 83 | 84 | /** 85 | * Validate the given options with the allowed options 86 | * 87 | * @param array $options Options array to get validated 88 | * 89 | * @return Mookofe\Tail\BaseOptions 90 | */ 91 | public function validateOptions(array $options) 92 | { 93 | foreach ($options as $option => $value) 94 | { 95 | if (!in_array($option, $this->allowedOptions)) 96 | throw new InvalidOptionException("Option [$option] is not valid"); 97 | } 98 | 99 | return $this; 100 | } 101 | 102 | /** 103 | * Set the following options in the class 104 | * 105 | * @param array $options Options with values to be set 106 | * 107 | * @return Mookofe\Tail\BaseOptions 108 | */ 109 | public function setOptions(array $options) 110 | { 111 | //Validate options 112 | $this->validateOptions($options); 113 | 114 | //Set options 115 | foreach ($options as $option => $value) 116 | $this->$option = $value; 117 | 118 | return $this; 119 | } 120 | 121 | /** 122 | * Build options set to build a connection to the queue server 123 | * 124 | * @return array 125 | */ 126 | public function buildConnectionOptions() 127 | { 128 | //Default connection 129 | $connection_name = $this->config->get("tail-settings.default"); 130 | 131 | //Check if set for this connection 132 | if ($this->connection_name) 133 | $connection_name = $this->connection_name; 134 | 135 | $connectionOptions = $this->config->get("tail-settings.connections.$connection_name"); 136 | 137 | //Adding default values to exchange_type and content_type to avoid breaking change 138 | if (!isset($connectionOptions['exchange_type'])) 139 | $connectionOptions['exchange_type'] = 'direct'; 140 | if (!isset($connectionOptions['content_type'])) 141 | $connectionOptions['content_type'] = 'text/plain'; 142 | 143 | //Set current instance properties values 144 | if ($this->vhost) 145 | $connectionOptions['vhost'] = $this->vhost; 146 | if ($this->exchange) 147 | $connectionOptions['exchange'] = $this->exchange; 148 | if ($this->exchange_type) 149 | $connectionOptions['exchange_type'] = $this->exchange_type; 150 | if ($this->content_type) 151 | $connectionOptions['content_type'] = $this->content_type; 152 | 153 | //Queue specific options 154 | $connectionOptions['queue_name'] = $this->queue_name; 155 | 156 | return $connectionOptions; 157 | } 158 | } -------------------------------------------------------------------------------- /src/Connection.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Connection extends BaseOptions{ 13 | 14 | /** 15 | * RabbitMQ server name or IP 16 | * 17 | * @var string 18 | */ 19 | public $host; 20 | 21 | /** 22 | * RabbitMQ server port 23 | * 24 | * @var string 25 | */ 26 | public $port; 27 | 28 | /** 29 | * RabbitMQ server username 30 | * 31 | * @var string 32 | */ 33 | public $username; 34 | 35 | /** 36 | * RabbitMQ server password 37 | * 38 | * @var string 39 | */ 40 | public $password; 41 | 42 | /** 43 | * RabbitMQ server consumer tag 44 | * 45 | * @var string 46 | */ 47 | public $consumer_tag; 48 | 49 | /** 50 | * RabbitMQ connection SSL context options 51 | * 52 | * @var array 53 | */ 54 | public $ssl_context_options; 55 | 56 | /** 57 | * RabbitMQ connection timeout in seconds 58 | * 59 | * @var float 60 | */ 61 | public $connection_timeout; 62 | 63 | /** 64 | * RabbitMQ connection read/write timeout in seconds 65 | * 66 | * @var float 67 | */ 68 | public $read_write_timeout; 69 | 70 | /** 71 | * RabbitMQ connection keepalive flag 72 | * 73 | * @var bool 74 | */ 75 | public $keepalive; 76 | 77 | /** 78 | * RabbitMQ connection heartbeat in seconds 79 | * 80 | * @var int 81 | */ 82 | public $heartbeat; 83 | 84 | /** 85 | * RabbitMQ AMQP Connection 86 | * 87 | * @var PhpAmqpLib\Connection\AMQPConnection 88 | */ 89 | public $AMQPConnection; 90 | 91 | /** 92 | * RabbitMQ AMQP channel 93 | * 94 | * @var PhpAmqpLib\Connection\AMQPConnection 95 | */ 96 | public $channel; 97 | 98 | /** 99 | * Connection constructor 100 | * 101 | * @param array $options Options array to set connection 102 | * 103 | * @return Mookofe\Tail\Connection 104 | */ 105 | public function __construct(array $options = null) 106 | { 107 | $this->allowedOptions = array_merge($this->allowedOptions, array( 108 | 'host', 109 | 'port', 110 | 'username', 111 | 'password', 112 | 'consumer_tag', 113 | 'ssl_context_options', 114 | 'connection_timeout', 115 | 'read_write_timeout', 116 | 'keepalive', 117 | 'heartbeat' 118 | ) 119 | ); 120 | 121 | if (!$options) 122 | $options = $this->buildConnectionOptions(); 123 | 124 | $this->setOptions($options); 125 | } 126 | 127 | /** 128 | * Open a connection with the RabbitMQ Server 129 | * 130 | * @return void 131 | */ 132 | public function open() 133 | { 134 | try 135 | { 136 | $additionalConnectionOptions = array(); 137 | foreach (array('connection_timeout', 'read_write_timeout', 'keepalive', 'heartbeat') as $option) { 138 | if (isset($this->$option)) { 139 | $additionalConnectionOptions[$option] = $this->$option; 140 | } 141 | } 142 | $this->AMQPConnection = new AMQPSSLConnection( 143 | $this->host, 144 | $this->port, 145 | $this->username, 146 | $this->password, 147 | $this->vhost, 148 | $this->ssl_context_options, 149 | $additionalConnectionOptions 150 | ); 151 | $this->channel = $this->AMQPConnection->channel(); 152 | $this->channel->queue_declare($this->queue_name, false, false, false, false); 153 | $this->channel->exchange_declare($this->exchange, $this->exchange_type, false, true, false); 154 | $this->channel->queue_bind($this->queue_name, $this->exchange); 155 | } 156 | catch (Exception $e) 157 | { 158 | throw new Exception($e); 159 | } 160 | } 161 | 162 | /** 163 | * Close the connection with the RabbitMQ server 164 | * 165 | * @return void 166 | */ 167 | public function close() 168 | { 169 | if (isset($this->channel)) 170 | $this->channel->close(); 171 | if (isset($this->AMQPConnection)) 172 | $this->AMQPConnection->close(); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/Exceptions/InvalidConnectionException.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class InvalidConnectionException extends Exception { 11 | 12 | } -------------------------------------------------------------------------------- /src/Exceptions/InvalidOptionException.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class InvalidOptionException extends Exception { 11 | 12 | 13 | } -------------------------------------------------------------------------------- /src/Facades/Tail.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class Tail extends Facade { 11 | 12 | /** 13 | * Get the registered name of the component. 14 | * 15 | * @return string 16 | */ 17 | protected static function getFacadeAccessor() { return 'Tail'; } 18 | 19 | } -------------------------------------------------------------------------------- /src/Listener.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class Listener extends BaseOptions { 16 | 17 | /** 18 | * Number of messages to be proccessed 19 | * 20 | * @var int 21 | */ 22 | public $message_limit = 0; 23 | 24 | /** 25 | * Time in seconds the proccess will be running 26 | * 27 | * @var int 28 | */ 29 | public $time = 0; 30 | 31 | /** 32 | * Time in seconds to kill listening when the queue is empty 33 | * 34 | * @var int 35 | */ 36 | public $empty_queue_timeout = 0; 37 | 38 | 39 | /** 40 | * Listener constructor 41 | * 42 | * @param array $options Options array to get validated 43 | * 44 | * @return Mookofe\Tail\Listener 45 | */ 46 | public function __construct(Repository $config, array $options = NULL) 47 | { 48 | parent::__construct($config); 49 | 50 | $this->allowedOptions = array_merge($this->allowedOptions, array('message_limit', 'time', 'empty_queue_timeout')); 51 | 52 | if ($options) 53 | $this->setOptions($options); 54 | } 55 | 56 | /** 57 | * Listen queue server for given queue name 58 | * 59 | * @param string $queue_name Queue name to listen 60 | * @param array $options Options to listen 61 | * @param Closure $closure Function to run for every message 62 | * 63 | * @return void 64 | */ 65 | public function listen($queue_name, array $options = null, Closure $closure) 66 | { 67 | $this->queue_name = $queue_name; 68 | 69 | if ($options) 70 | $this->setOptions($options); 71 | 72 | $GLOBALS['messages_proccesed'] = 0; 73 | $GLOBALS['start_time'] = time(); 74 | 75 | $connection = new Connection($this->buildConnectionOptions()); 76 | $connection->open(); 77 | 78 | $listenerObject = $this; 79 | 80 | $connection->channel->basic_consume($this->queue_name, $connection->consumer_tag, false, false, false, false, function ($msg) use ($closure, $listenerObject) { 81 | 82 | try 83 | { 84 | $closure($msg->body); 85 | } 86 | catch (Exception $e) 87 | { 88 | throw $e; 89 | } 90 | 91 | $msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']); 92 | 93 | //Update counters 94 | $GLOBALS['messages_proccesed']++; 95 | 96 | //Check if necesary to close consumer 97 | if ($listenerObject->message_limit && $GLOBALS['messages_proccesed'] >= $listenerObject->message_limit) 98 | $msg->delivery_info['channel']->basic_cancel($msg->delivery_info['consumer_tag']); 99 | 100 | if ($listenerObject->time && (time()-$GLOBALS['start_time']>= $listenerObject->time)) 101 | $msg->delivery_info['channel']->basic_cancel($msg->delivery_info['consumer_tag']); 102 | }); 103 | 104 | register_shutdown_function(function ($connection) { 105 | $connection->close(); 106 | }, $connection); 107 | 108 | // Loop as long as the channel has callbacks registered 109 | try 110 | { 111 | while (count($connection->channel->callbacks)) { 112 | $connection->channel->wait(null, false, $this->empty_queue_timeout); 113 | } 114 | } 115 | catch (AMQPTimeoutException $e) 116 | { 117 | return false; 118 | } 119 | catch (Exception $e) 120 | { 121 | throw $e; 122 | } 123 | } 124 | 125 | } -------------------------------------------------------------------------------- /src/LumenServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | class LumenServiceProvider extends \Illuminate\Support\ServiceProvider { 9 | 10 | 11 | /** 12 | * Bootstrap the application events. 13 | * 14 | * @return void 15 | */ 16 | public function boot() 17 | { 18 | } 19 | 20 | /** 21 | * Register the service provider. 22 | * 23 | * @return void 24 | */ 25 | public function register() 26 | { 27 | //Bind config 28 | $this->app->bind('Mookofe\Tail\Message', function ($app) { 29 | return new Message($app->config); 30 | }); 31 | $this->app->bind('Mookofe\Tail\Listener', function ($app) { 32 | return new Listener($app->config); 33 | }); 34 | 35 | //Register Facade 36 | $this->app->bind('Tail', 'Mookofe\Tail\Tail'); 37 | 38 | if (!class_exists('Tail')) { 39 | class_alias('Mookofe\Tail\Facades\Tail', 'Tail'); 40 | } 41 | 42 | //Add App facade if is lumen >5.2 43 | if (!class_exists('App') && $this->version() >= 5.2) { 44 | class_alias('Illuminate\Support\Facades\App', 'App'); 45 | } 46 | } 47 | 48 | /** 49 | * Get Lumen version 50 | */ 51 | protected function version() 52 | { 53 | $version = explode('(', $this->app->version()); 54 | if (isset($version[1])) { 55 | return substr($version[1], 0, 3); 56 | } 57 | return null; 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /src/Message.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class Message extends BaseOptions { 16 | 17 | 18 | /** 19 | * Message to be send or received from the queue server 20 | * 21 | * @var string 22 | */ 23 | public $message; 24 | 25 | /** 26 | * Message constructor 27 | * 28 | * @param array $options Options array to get validated 29 | * 30 | * @return Mookofe\Tail\Message 31 | */ 32 | public function __construct(Repository $config, array $options = NULL) 33 | { 34 | parent::__construct($config); 35 | 36 | if ($options) 37 | $this->setOptions($options); 38 | } 39 | 40 | /** 41 | * Add a message directly to the queue server 42 | * 43 | * @param string $queue_name Queue name on RabbitMQ 44 | * @param string $message Message to be add to the queue server 45 | * @param array $options Options values for message to add 46 | * 47 | * @return void 48 | */ 49 | public function add($queue_name, $message, array $options = NULL) 50 | { 51 | $this->queue_name = $queue_name; 52 | $this->message = $message; 53 | 54 | if ($options) 55 | $this->setOptions($options); 56 | 57 | $this->save(); 58 | } 59 | 60 | /** 61 | * Save the current message instance into de queue server 62 | * 63 | * @return void 64 | */ 65 | public function save() 66 | { 67 | try 68 | { 69 | $connection = new Connection($this->buildConnectionOptions()); 70 | $connection->open(); 71 | 72 | $msg = new AMQPMessage($this->message, array('content_type' => $this->content_type, 'delivery_mode' => 2)); 73 | $connection->channel->basic_publish($msg, $this->exchange, $this->queue_name); 74 | 75 | $connection->close(); 76 | } 77 | catch (Exception $e) 78 | { 79 | $connection->close(); 80 | throw new Exception($e); 81 | } 82 | } 83 | 84 | } -------------------------------------------------------------------------------- /src/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | class ServiceProvider extends \Illuminate\Support\ServiceProvider { 9 | 10 | 11 | /** 12 | * Bootstrap the application events. 13 | * 14 | * @return void 15 | */ 16 | public function boot() 17 | { 18 | $this->publishes([ 19 | __DIR__.'/../config/tail.php' => config_path('tail-settings.php'), 20 | ], 'config'); 21 | 22 | } 23 | 24 | /** 25 | * Register the service provider. 26 | * 27 | * @return void 28 | */ 29 | public function register() 30 | { 31 | $this->mergeConfigFrom( 32 | __DIR__.'/../config/tail.php', 'tail-settings' 33 | ); 34 | 35 | //Register Facade 36 | $this->app->bind('Tail', 'Mookofe\Tail\Tail'); 37 | } 38 | 39 | /** 40 | * Get the services provided by the provider. 41 | * 42 | * @return array 43 | */ 44 | public function provides() 45 | { 46 | return array(); 47 | } 48 | } -------------------------------------------------------------------------------- /src/Tail.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class Tail { 14 | 15 | /** 16 | * Add a message directly to the queue server 17 | * 18 | * @param string $queue_name Queue name on RabbitMQ 19 | * @param string $message Message to be add to the queue server 20 | * @param array $options Options values for message to add 21 | * 22 | * @return void 23 | */ 24 | public function add($queueName, $message, array $options = null) 25 | { 26 | $msg = App::make('Mookofe\Tail\Message'); 27 | $msg->add($queueName, $message, $options); 28 | } 29 | 30 | /** 31 | * Create new blank message instance 32 | * 33 | * @return Mookofe\Tail\Message 34 | */ 35 | public function createMessage() 36 | { 37 | return App::make('Mookofe\Tail\Message'); 38 | } 39 | 40 | /** 41 | * Listen queue server for given queue name 42 | * 43 | * @param string $queue_name Queue name to listen 44 | * @param array $options Options to listen 45 | * 46 | * @return void 47 | */ 48 | public function listen($queue_name, Closure $callback) 49 | { 50 | $listener = App::make('Mookofe\Tail\Listener'); 51 | $listener->listen($queue_name, null, $callback); 52 | } 53 | 54 | /** 55 | * Listen queue server for given queue name 56 | * 57 | * @param string $queue_name Queue name to listen 58 | * @param array $options Options to listen 59 | * @param Closure $closure Function to run for every message 60 | * 61 | * @return void 62 | */ 63 | public function listenWithOptions($queue_name, array $options, Closure $callback) 64 | { 65 | $listener = App::make('Mookofe\Tail\Listener'); 66 | $listener->listen($queue_name, $options, $callback); 67 | } 68 | 69 | } -------------------------------------------------------------------------------- /tests/testBaseOptions.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class testBaseOptions extends \PHPUnit_Framework_TestCase 13 | { 14 | 15 | protected $input; 16 | 17 | public function __construct() 18 | { 19 | $this->input = Mockery::mock('Illuminate\Config\Repository'); 20 | 21 | } 22 | 23 | public function tearDown() 24 | { 25 | Mockery::close(); 26 | } 27 | 28 | public function testValidateOptions() 29 | { 30 | $options = array('queue_name' => 'this_queue'); 31 | $baseOptions = new BaseOptions($this->input); 32 | 33 | $result = $baseOptions->validateOptions($options); 34 | 35 | //Asserts 36 | $this->assertInstanceOf('Mookofe\Tail\BaseOptions', $result); 37 | } 38 | 39 | /** 40 | * @expectedException Mookofe\Tail\Exceptions\InvalidOptionException 41 | */ 42 | public function testValidateOptionsInvalid() 43 | { 44 | $options = array('invalid_field' => 'this_is_invalid_field'); 45 | $baseOptions = new BaseOptions($this->input); 46 | 47 | $result = $baseOptions->validateOptions($options); 48 | } 49 | 50 | public function testSetOptions() 51 | { 52 | $options = array('queue_name' => 'this_queue'); 53 | $baseOptions = new BaseOptions($this->input); 54 | 55 | $baseOptions->setOptions($options); 56 | 57 | //Assertss 58 | $this->assertObjectHasAttribute('queue_name', $baseOptions); 59 | $this->assertEquals($baseOptions->queue_name, $options['queue_name']); 60 | } 61 | 62 | public function testBuildConnectionOptions() 63 | { 64 | //Mock Input object 65 | $this->input->shouldReceive('get')->once()->andReturn('just_to_return'); 66 | $this->input->shouldReceive('get')->once()->andReturn(array()); 67 | 68 | //Setup enviroment 69 | $baseOptions = new BaseOptions($this->input); 70 | $options = $baseOptions->buildConnectionOptions(); 71 | 72 | //Asserts 73 | $this->assertInternalType('array', $options); 74 | $this->assertArrayHasKey('queue_name', $options); 75 | } 76 | } --------------------------------------------------------------------------------