bhittani / dispenser
Dispense entities under a disciplined regime
Requires
- php: >= 5.6
Requires (Dev)
- friendsofphp/php-cs-fixer: ^2.15
- phpunit/phpunit: <8
This package is auto-updated.
Last update: 2024-10-24 14:05:55 UTC
README
Dispense entities under a disciplined regime.
Install
You may install this package using composer.
$ composer require bhittani/dispenser --prefer-dist
Usage
Dispenser
At its core, the dispenser handles a function call.
<?php require_once __DIR__ . '/vendor/autoload.php'; use Bhittani\Dispenser\Dispenser; $dispenser = new Dispenser(function ($a, $b) { return $a . '.' . $b; }); $dispenser->dispense('foo', 'bar'); // 'foo.bar'
Queue Dispenser
A queue dispenser maintains a collection of dispensers in a queue.
<?php require_once __DIR__ . '/vendor/autoload.php'; use Bhittani\Dispenser\Queue; use Bhittani\Dispenser\Dispenser; $queue = new Queue; $queue->push(new Dispenser(function ($a, $b) { return $a . $b . 1; })); $queue->push(new Dispenser(function ($a, $b) { return $a . $b . 2; })); // Doesn't have to be a dispenser, but recommended. $queue->push(function ($a, $b) { return $a . $b . 3; }); $queue->dispense('a', 'b'); // ['ab1', 'ab2', 'ab3']
Stack Dispenser
A stack dispenser maintains a collection of dispensers in a stack.
<?php require_once __DIR__ . '/vendor/autoload.php'; use Bhittani\Dispenser\Stack; use Bhittani\Dispenser\Dispenser; $stack = new Stack; $stack->push(new Dispenser(function ($a, $b) { return $a . $b . 1; })); $stack->push(new Dispenser(function ($a, $b) { return $a . $b . 2; })); // Doesn't have to be a dispenser, but recommended. $stack->push(function ($a, $b) { return $a . $b . 3; }); $stack->dispense('a', 'b'); // ['ab3', 'ab2', 'ab1']
Priority Dispenser
A priority dispenser maintains a collection of dispensers in a priority heap.
<?php require_once __DIR__ . '/vendor/autoload.php'; use Bhittani\Dispenser\Priority; use Bhittani\Dispenser\Dispenser; $priority = new Priority; $priority->insert(new Dispenser(function ($a, $b) { return $a . $b . 3; }), 1); $priority->insert(new Dispenser(function ($a, $b) { return $a . $b . 1; }), 3); // Doesn't have to be a dispenser, but recommended. $priority->insert(function ($a, $b) { return $a . $b . 2; }, 2); $priority->dispense('a', 'b'); // ['ab1', 'ab2', 'ab3']
The rank is directly proportional to the priority value. In the example above, priority value 3 dispenses first, followed by 2, and finally by 1.
Pipeline Dispenser
A pipeline allows piping through dispensers by passing on the result of the previous piped dispenser onto the next piped dispenser.
<?php require_once __DIR__ . '/vendor/autoload.php'; use Bhittani\Dispenser\Stack; use Bhittani\Dispenser\Pipeline; use Bhittani\Dispenser\Dispenser; $pipeline = new Pipeline; $pipeline->push($stack = new Stack); $stack->push(new Dispenser(function ($n) { return $n / 5; })); // 100/5=20 $stack->push(new Dispenser(function ($n) { return $n + 60; })); // 40+60=100 $stack->push(function ($n) { return $n * 4; }); // 10*4=40 $pipeline->push(function ($n) { return $n / 2; }); // 20/2=10 $pipeline->dispense(10); // 10
The iterator dispenser's discipline will be respected. i.e. if we were to use a priority dispenser, the results would vary due to the potential difference in the order of dispensation.
Chain Dispenser
A chain accepts dispensers and work on it as a chain. As opposed to a pipeline, a dispenser within a chain receives a handler to the next dispenser in the chain and can be completely ignored in order to stop further processing of the chain.
<?php require_once __DIR__ . '/vendor/autoload.php'; use Bhittani\Dispenser\Chain; use Bhittani\Dispenser\Dispenser; // Accepts an optional fallback dispenser. $chain = new Chain(function ($foo, $bar) { return '!'; }); $chain->push(new Dispenser(function ($foo, $bar, $next) { return '(' . $next($foo, $bar) . ')'; })); // Doesn't have to be a dispenser, but recommended. $chain->push(function ($foo, $bar, $next) { return $foo.$next($foo, $bar); }); $chain->push(function ($foo, $bar, $next) { return $next($foo, $bar).$bar; }); $chain->dispense('middle', 'ware'); // (middle!ware)
Every dispenser's discipline will be respected. i.e. if we were to use a stack dispenser, the results would vary due to the potential difference in the order of dispensation.
Different types of dispensers can be pushed to the same chain.
The fallback dispenser is optional and does not have a final $next parameter because it will be the last dispenser in the chain.
Using the chain dispenser as an http middleware
Due to the powerful discipline of the chain dispenser, we can use it as an http middleware chain.
<?php require_once __DIR__ . '/vendor/autoload.php'; use Bhittani\Dispenser\Chain; use Bhittani\Dispenser\Queue; use Bhittani\Dispenser\Dispenser; $chain = new Chain(function ($request) { return make_a_response_however_you_want_to($request); }); $chain->push($middlewares = new Queue); // With a fictional request & response objects, // lets record the time spent on the request. $middlewares->push(new Dispenser(function ($request, $next) { $request = $request->startTime(); $response = $next($request); $request->stopTime(); return $response->withTime($request->getElapsedTime()); })); $middlewares->push(new Dispenser(function ($request, $next) { // Do something... return $next($request); })); // Handle the $request... $chain->dispense($request);
Creating custom/extended dispensers
Extended dispensers can be created by implementing the Bhittani\Dispenser\DispenserInterface
interface. It has only one required method dispense
which accepts variadic arguments. This makes it very simple to mix and utilize other dispensers. The power is in your hands.
Example dispenser implementation
<?php require_once __DIR__ . '/vendor/autoload.php'; use Bhittani\Dispenser\DispenserInterface; class Dispatcher implements DispenserInterface { protected $subscribers = []; public function subscribe($key, DispenserInterface $subscriber) { if (! isset($this->subscribers[$key])) { $this->subscribers[$key] = []; } $this->subscribers[$key][] = $subscriber; return $this; } public function dispense(...$parameters) { $key = array_shift($parameters); if (! isset($this->subscribers[$key])) { return []; } return array_map(function ($subscriber) use ($parameters) { return $subscriber->dispense($parameters); }, $this->subscribers[$key]); } } $dispatcher = new Dispatcher; $dispatcher->subscribe('foo', new Dispenser(function ($a, $b) { return $a.'1foo1'.$b; })); $dispatcher->subscribe('foo', new Dispenser(function ($a, $b) { return $a.'2foo2'.$b; })); $dispatcher->subscribe('bar', new Dispenser(function ($a, $b) { return $a.'bar'.$b; })); $dispatcher->dispense('bar', 'a', 'b'); // ['abarb'] $dispatcher->dispense('foo', 'a', 'b'); // ['a1foo1b', 'a2foo2b']
The above implementation works as an event subscriber and publisher.
For availability, the
Dispatcher
is also available under theBhittani\Dispenser
namespace.
Changelog
Please see CHANGELOG for more information on what has changed.
Testing
$ git clone https://github.com/kamalkhan/dispenser $ cd dispenser $ composer install $ composer test
Contributing
Please see CONTRIBUTING and CONDUCT for details.
Security
If you discover any security related issues, please email shout@bhittani.com
instead of using the issue tracker.
Credits
License
The MIT License (MIT). Please see the License File for more information.