zeus / pipe
A simple, flexible middleware pipeline system for PHP with named stages, conditional execution, and error handling.
README
A lightweight, flexible middleware pipeline system for PHP, inspired by Laravel's middleware architecture. Zeus\Pipe
enables you to process data through a series of named middleware functions with features like conditional execution, exception handling, and flow control.
Features
- Named Middleware: Register middleware with unique names for easy management.
- Conditional Execution: Use
only()
andwithout()
to control which middleware runs. - Flow Control: Stop middleware execution with
stop()
. - Exception Handling: Catch and handle errors with
catch()
. - Middleware Monitoring: Track middleware execution with
watch()
. - Pipeline Reset: Clear all middleware with
flush()
. - Closure Support: Convert the pipeline to a reusable closure with
getClosure()
.
Installation
Install via Composer:
composer require zeus/pipe
Alternatively, include it manually:
use Zeus\Pipe\Pipe;
Usage
Basic Example
Create a pipeline and add middleware to process data sequentially:
use Zeus\Pipe\Pipe; $pipe = new Pipe(); $pipe->next('trim', fn($data, $next, $stop) => $next(trim($data))); $pipe->next('to_upper', fn($data, $next, $stop) => $next(strtoupper($data))); $result = $pipe->run(" hello world "); // Returns: "HELLO WORLD"
Each middleware receives the input $data
, a $next
callback to pass data to the next middleware, and a $stop
callback to halt the pipeline.
Conditional Execution
only()
Run only specific middleware:
$pipe->only(['trim']); $result = $pipe->run(" hello "); // Runs only 'trim', returns: "hello"
without()
Skip specific middleware:
$pipe->without(['to_upper']); $result = $pipe->run(" hello "); // Skips 'to_upper', returns: "hello"
Monitoring with watch()
Track middleware execution by logging input and output:
$pipe->watch(function ($name, $before, $after) { printf("| %-12s | %-15s | %-15s |\n", $name, $before, $after); }); $result = $pipe->run(" hello ");
Example Output:
| Middleware | Before | After |
|--------------|-----------------|-----------------|
| trim | " hello " | "hello" |
| to_upper | "hello" | "HELLO" |
Exception Handling with catch()
Handle exceptions thrown during middleware execution:
$pipe->next('error', fn($data, $next, $stop) => throw new Exception("Invalid data")); $pipe->catch(function (Throwable $e) { return "Error: " . $e->getMessage(); }); $result = $pipe->run("test"); // Returns: "Error: Invalid data"
Flow Control with stop()
Halt the pipeline early based on a condition:
$pipe->next('check', function ($data, $next, $stop) { if ($data === 'STOP') { return $stop('Pipeline stopped.'); } return $next($data); }); $result = $pipe->run('STOP'); // Returns: "Pipeline stopped."
Pipeline as Closure with getClosure()
Convert the pipeline to a reusable closure:
$closure = $pipe->getClosure(); $result = $closure(" hello "); // Returns: "HELLO"
Reset Pipeline with flush()
Remove all registered middleware:
$pipe->flush(); // Clears all middleware
Zeus\Pipe\Filter
A simple, chainable filter class using closures.
🚀 Example
use Zeus\Pipe\Filter; $filter = new Filter(); $filter ->add(fn($value) => is_int($value)) // Must be an integer ->add(fn($value) => $value > 10) // Greater than 10 ->add(fn($value) => $value % 2 === 0) ->orAdd(fn($value)=>true);//or operator $data = [5, 12, 15, 20, '30', 25]; $result = $filter->apply($data); //or $closure=$filter->getClosure(); array_filter($array,$closure); // print_r($result); // Output: [1 => 12, 3 => 20]
or Using a named filter
$filter = new Filter(); $filter->defineFilter('even_bigger_than_5', function (Filter $filter) { $filter ->add(static fn($value) => $value % 2 === 0) ->add(static fn($value) => $value > 5); }); $filter->defineFilter('odd_and_not_negative', function (Filter $filter) { $filter ->add(static fn($value) => $value % 2 !== 0) ->add(static fn($value) => $value >= 0); }); $filter->applyTo('even_bigger_than_5', [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); $filtered=$filter->multipleApplyTo( [ 'even_bigger_than_5', 'odd_and_not_negative' ], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], true ); print_r($filtered); print_r($filter->getRejected());
You can extend defineFilter so you can create more dynamic filters.
$filter = new Filter(); $filter->defineMultipleFilter([ 'isEven' => static function (Filter $filter) { $filter ->add(fn(int $value)=>$value % 2 === 0); }, ]); $filter->extendDefinedFilter('isEven', static function (Filter $filter) { $filter ->add(fn(int $value)=>$value>5); }); $filter->extendDefinedFilter('isEven', static function (Filter $filter) { $filter ->orAdd(fn(int $value)=>$value<10); }); $applyTo = $filter->applyTo('isEven', range(1,25)); print_r($applyTo);
! operator in named
To do the opposite of a filter, just use !, for example, we use adult
filter for adult and !adult
for non-adult.
$filter = new Filter(); $filter->defineFilter('even_bigger_than_5', function (Filter $filter) { $filter ->add(static fn($value) => $value > 5); }); $filter->defineFilter('odd_and_not_negative', function (Filter $filter) { $filter ->add(static fn($value) => $value % 2 !== 0) ->add(static fn($value) => $value >= 0); }); $filter->applyTo('!even_bigger_than_5', [1, 2, 3, 4, 5, 6, 7, 8, 9]); //not even_bigger_than_5 $filter->applyTo('even_bigger_than_5', [1, 2, 3, 4, 5, 6, 7, 8, 9]); //even_bigger_than_5 $filter->multipleApplyTo([ '!even_bigger_than_5', 'odd_and_not_negative', ],[ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]);
set the not operator
By default, its value is !
$filter = new Filter(); $filter->notOperator('not_'); $filter->defineFilter('even_bigger_than_5', function (Filter $filter) { $filter ->add(static fn($value) => $value > 5); }); $filter->applyTo('not_even_bigger_than_5', [1, 2, 3, 4, 5, 6, 7, 8, 9]); //not even_bigger_than_5
short using of the define
method
Attention! This method is experimental, may have side effects and has not yet been fully tested.
$filter = new Filter(); $filter ->define('isEven_bigger_than_5') ->add(fn($value) => $value % 2 === 0) ->orAdd(fn($value) => $value % 2 !== 0) ->add(fn($value) => $value > 5); $filter->applyTo('isEven_bigger_than_5', [1, 2, 3, 4, 5, 6, 7, 8, 9]); //even_bigger_than_5
The transformer
You can use the transformer method to make changes to the value before the filter. I only gave a trim example in this example.
$filter = new Filter(); $filter ->transform(function (mixed $value,Closure $set) { $set( trim($value) ); }); $apply = $filter->apply([ ' hello ', ' hi ', ]); //['hello', 'hi'] print_r($apply);
Type casting
You can use the type casting method to make changes to the value before the filter.
$filter = new Filter(); $filter ->using('trim') ->as(Type::INT); $apply = $filter->apply([ ' 85 ', ' 7444 ', ]); print_r($apply); //[85,144]
Zeus\Pipe\Map
A simple, chainable map class using closures.
🚀 Example
use Zeus\Pipe\Map; $map = new Map(); $map ->add(fn($value) => $value * 2) // Multiply by 2 ->add(fn($value) => $value + 1); // Add 1 $data = [1, 2, 3, 4, 5]; $result = $map->apply($data); print_r($result); // Output: [2, 5, 8, 11, 14] //or $closure=$map->getClosure(); array_map($closure,$array);
License
This project is licensed under the MIT License.