-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
872 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
.idea | ||
*.iml | ||
out | ||
gen | ||
dev |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,243 @@ | ||
# pipechain | ||
Pipeline Pattern Implementation | ||
# PipeChain | ||
A linear chainable pipeline pattern implementation with fallbacks. | ||
|
||
## What is a linear pipeline | ||
|
||
A linear pipeline is a object that stacks callbacks and provides a | ||
processing logic to process a payload thru the callback stack, | ||
form the first to the last. | ||
|
||
## Processors | ||
|
||
PipeChain comes out of the box with 4 different processors: | ||
|
||
### The common invoker processor | ||
|
||
The common invoker processor implements a processing of the | ||
queue that considers the provided fallback callback as the | ||
successor of a failed stage callback. | ||
|
||
The common invoker processor is the default processor. | ||
|
||
Example: | ||
|
||
```php | ||
<?php declare(strict_types=1); | ||
|
||
use PipeChain\{ | ||
PipeChain | ||
}; | ||
|
||
$pipeline = new PipeChain(); | ||
|
||
$pipeline->pipe( | ||
# the stage callback | ||
function(string $inbound) { | ||
return 'something'; | ||
}, | ||
# the fallback callback | ||
function(int $inbound) { | ||
return ++$inbound; | ||
} | ||
); | ||
|
||
var_dump( | ||
$pipeline->process(100) // int(3) => 101 | ||
); | ||
|
||
``` | ||
|
||
### The naive invoker processor | ||
|
||
The naive invoker processor implements a processing of the | ||
queue that ignores the provided fallback callback. | ||
|
||
Example: | ||
|
||
```php | ||
<?php declare(strict_types=1); | ||
|
||
use PipeChain\{ | ||
PipeChain, | ||
InvokerProcessors\NaiveInvokerProcessor | ||
}; | ||
|
||
$pipeline = new PipeChain(new NaiveInvokerProcessor()); | ||
|
||
$pipeline->pipe(function(int $inbound) { | ||
return ++$inbound; | ||
}); | ||
|
||
var_dump( | ||
$pipeline->process(100) // int(3) => 101 | ||
); | ||
|
||
``` | ||
|
||
### The loyal interface invoker processor | ||
|
||
The loyal interface invoker processor implements a processing | ||
of the queue that acts like a common invoker processor with | ||
an additional interface check of each payload change. | ||
|
||
The interface check has 2 modes: | ||
- `LoyalInterfaceInvokerProcessor::INTERRUPT_ON_MISMATCH` forces | ||
the processor to pass the throwables to the next scope. | ||
- `LoyalInterfaceInvokerProcessor::RESET_ON_MISMATCH` forces | ||
the processor to use the previous payload instead. | ||
|
||
Example: | ||
|
||
```php | ||
<?php declare(strict_types=1); | ||
|
||
use PipeChain\{ | ||
PipeChain, | ||
InvokerProcessors\LoyalInterfaceInvokerProcessor | ||
}; | ||
|
||
$pipeline = new PipeChain( | ||
new LoyalInterfaceInvokerProcessor( | ||
DateTimeInterface::class, | ||
LoyalInterfaceInvokerProcessor::RESET_ON_MISMATCH | ||
) | ||
); | ||
|
||
$prior = function($inbound) { | ||
if ( ! $inbound instanceof DateTime ) { | ||
throw new Exception('Whats this?'); | ||
} | ||
|
||
return $inbound->modify('+10 hours'); | ||
}; | ||
|
||
$failure = function (string $inbound) use ($prior) { | ||
return $prior(date_create($inbound)); | ||
}; | ||
|
||
$pipeline->pipe($prior, $failure); | ||
|
||
var_dump( | ||
$pipeline | ||
->process('2017-10-12 18:00:00') | ||
->format('Y-m-d') // string(10) "2017-10-13" | ||
); | ||
|
||
``` | ||
|
||
### The loyal type invoker processor | ||
|
||
The loyal type invoker processor implements a processing | ||
of the queue that acts like the loyal interface invoker | ||
processor but with type checking of each payload change. | ||
|
||
The type check has 2 modes: | ||
- `LoyalInterfaceInvokerProcessor::INTERRUPT_ON_MISMATCH` forces | ||
the processor to pass the throwables to the next scope. | ||
- `LoyalInterfaceInvokerProcessor::RESET_ON_MISMATCH` forces | ||
the processor to use the previous payload instead. | ||
|
||
Example: | ||
|
||
```php | ||
<?php declare(strict_types=1); | ||
|
||
use PipeChain\{ | ||
PipeChain, | ||
InvokerProcessors\LoyalTypeInvokerProcessor | ||
}; | ||
|
||
$pipeline = new PipeChain( | ||
new LoyalTypeInvokerProcessor( | ||
'string', | ||
LoyalTypeInvokerProcessor::RESET_ON_MISMATCH | ||
) | ||
); | ||
|
||
$pipeline->pipe(function(string $name) { | ||
return strtolower($name); | ||
}); | ||
$pipeline->pipe(function(string $name) { | ||
return ucfirst($name); | ||
}); | ||
|
||
var_dump( | ||
$pipeline->process('jOhN') // string(4) "John" | ||
); | ||
|
||
``` | ||
|
||
## Booting own implementations | ||
|
||
```php | ||
<?php declare(strict_types=1); | ||
|
||
namespace Somewhat; | ||
|
||
use PipeChain\{ | ||
PipeChain, | ||
InvokerProcessorInterface as Processor, | ||
PipeChainCollectionInterface as Container | ||
}; | ||
|
||
class MyFirstPipeline extends PipeChain { | ||
|
||
protected function boot(Container $container, Processor $processor = null): Processor | ||
{ | ||
# pipe something. The signature of PipeChainCollectionInterface::attach() is equal | ||
# to PipeChain::pipe(..). | ||
|
||
$container->attach( | ||
# stage callable | ||
function() { | ||
|
||
}, | ||
# fallback callable (optional) | ||
function() { | ||
|
||
} | ||
); | ||
|
||
# ... | ||
|
||
# Don't forget to call the parent, the parent method ensures | ||
# that a processor will be created when $processor is null. | ||
|
||
return parent::boot($container, $processor); | ||
} | ||
} | ||
|
||
# later ... | ||
|
||
echo MyFirstPipeline::create()->process('I can get no satisfaction!'); | ||
|
||
``` | ||
|
||
## Chaining Pipelines | ||
|
||
```php | ||
<?php declare(strict_types=1); | ||
|
||
$pipeline = MyFirstPipeline::create()->chain( | ||
MySecondPipeline::create() | ||
)->chain( | ||
MyThirdPipeline::create() | ||
); | ||
|
||
$pipeline->process('This is awesome.'); | ||
``` | ||
|
||
## Maintainer, State of this Package and License | ||
|
||
Those are the Maintainer of this Package: | ||
|
||
- [Matthias Kaschubowski](https://github.com/nhlm) | ||
|
||
This package is released under the MIT license. A copy of the license | ||
is placed at the root of this repository. | ||
|
||
The State of this Package is unstable unless unit tests are added. | ||
|
||
## Todo | ||
|
||
- Adding Unit Tests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
<?php declare(strict_types=1); | ||
/** | ||
* This file is part of the PIPECHAIN Project. | ||
* | ||
* (c)2017 Matthias Kaschubowski | ||
* | ||
* This code is licensed under the MIT license, | ||
* a copy of the license is stored at the project root. | ||
*/ | ||
|
||
namespace PipeChain\Collections; | ||
|
||
|
||
use PipeChain\PipeChainCollectionInterface; | ||
use Traversable; | ||
|
||
/** | ||
* Class PipeChainCollection | ||
* @package PipeChain\Collections | ||
*/ | ||
class PipeChainCollection implements PipeChainCollectionInterface | ||
{ | ||
/** | ||
* @var \SplObjectStorage | ||
*/ | ||
protected $storage; | ||
|
||
/** | ||
* PipeChainCollection constructor. | ||
*/ | ||
public function __construct() | ||
{ | ||
$this->storage = new \SplObjectStorage(); | ||
} | ||
|
||
/** | ||
* Retrieve an external iterator | ||
* @link http://php.net/manual/en/iteratoraggregate.getiterator.php | ||
* @return Traversable|\Generator An instance of an object implementing <b>Iterator</b> or | ||
* <b>Traversable</b> | ||
* @since 5.0.0 | ||
*/ | ||
public function getIterator(): \Generator | ||
{ | ||
foreach ( $this->storage as $object ) { | ||
yield $object => $this->storage[$object]['fallback']; | ||
} | ||
} | ||
|
||
/** | ||
* Attaches a stage and assigns a fallback to the attached stage. | ||
* | ||
* @param callable $stage | ||
* @param callable|null $fallback | ||
* @return void | ||
*/ | ||
public function attach(callable $stage, callable $fallback = null) | ||
{ | ||
$this->storage->attach( | ||
$this->marshalClosure($stage), | ||
[ | ||
'fallback' => is_callable($fallback) ? $this->marshalClosure($fallback) : null | ||
] | ||
); | ||
} | ||
|
||
/** | ||
* Count elements of an object | ||
* @link http://php.net/manual/en/countable.count.php | ||
* @return int The custom count as an integer. | ||
* </p> | ||
* <p> | ||
* The return value is cast to an integer. | ||
* @since 5.1.0 | ||
*/ | ||
public function count() | ||
{ | ||
return $this->storage->count(); | ||
} | ||
|
||
protected function marshalClosure(callable $callback): \Closure | ||
{ | ||
if ( method_exists(\Closure::class, 'fromCallable') ) { | ||
return \Closure::fromCallable($callback); | ||
} | ||
|
||
is_callable($callback, true, $target); | ||
|
||
if ( false === strpos('::', $target) || $callback instanceof \Closure ) { | ||
return (new \ReflectionFunction($target))->getClosure(); | ||
} | ||
else { | ||
list($class, $method) = explode('::', $target); | ||
return (new \ReflectionMethod($class, $method)) | ||
->getClosure( | ||
is_array($callback) && is_object($callback[0]) | ||
? $callback[0] | ||
: null | ||
) | ||
; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
<?php declare(strict_types=1); | ||
/** | ||
* This file is part of the PIPECHAIN Project. | ||
* | ||
* (c)2017 Matthias Kaschubowski | ||
* | ||
* This code is licensed under the MIT license, | ||
* a copy of the license is stored at the project root. | ||
*/ | ||
|
||
namespace PipeChain; | ||
|
||
|
||
interface InvokerProcessorInterface | ||
{ | ||
/** | ||
* processes a PipeChainCollection with the provided payload. | ||
* | ||
* @param mixed $payload | ||
* @param PipeChainCollectionInterface $pipeChainCollection | ||
* @return mixed payload | ||
*/ | ||
public function processStack($payload, PipeChainCollectionInterface $pipeChainCollection); | ||
|
||
/** | ||
* processes a single stage and its optionally associated fallback with the provided payload. | ||
* | ||
* @param mixed $payload | ||
* @param callable $stage | ||
* @param callable|null $fallback | ||
* @return mixed payload | ||
*/ | ||
public function process($payload, callable $stage, callable $fallback = null); | ||
} |
Oops, something went wrong.