inmanturbo / pipes
Pipes for php with a simple api based on functional composition
Requires
- php: ^8.2
Requires (Dev)
- illuminate/contracts: ^11.20
- laravel/pint: dev-main
- pestphp/pest: 3.x-dev
- pestphp/pest-plugin-laravel: 3.x-dev
- phpstan/extension-installer: 1.4.x-dev
- phpstan/phpstan-deprecation-rules: 1.2.x-dev
- phpstan/phpstan-phpunit: 1.4.x-dev
README
Simply put, it takes the output of the last one and pipes it to the next one. Sorta like bash cat ./file | grep -e 'waldo'
Installation
You can install the package via composer:
composer require inmanturbo/pipes
Or just copy or download the functions.php
file from this repository.
Usage
pipe()
require __DIR__.'/../vendor/autoload.php'; use function Inmanturbo\Pipes\pipe; $addOne = fn ($number = 0) => $number + 1; $five = pipe($addOne) // 1 ->pipe(fn ($number) => $number + 1) // 2 ->pipe($addOne) // 3 ->pipe($addOne) // 4 ->thenReturn(); // 4
It doesn't delay execution
$fifty = pipe(1); while ($fifty->result() < 50) { $fifty->pipe(fn ($number) => ++$number); } echo $fifty->result(); // 50
You can also pass a class or a class string
use function Inmanturbo\Pipes\pipe; class Subtract { public function __invoke($number) { return $number - 1; } } $addOne = fn ($number = 0) => $number + 1; $six = pipe($addOne, 1) ->pipe($addOne) ->pipe($addOne) ->pipe($addOne) ->then(fn ($number) => ++$number); $five = pipe($six) ->pipe(Subtract::class) ->thenReturn(); $three = pipe(new Subtract, $five) ->pipe(new Subtract) ->thenReturn();
Returning results
results can be returned three ways:
then()
or thenReturn()
both a take final callback and return the result, or result()
, which simply returns the result.
$addOne = fn ($number = 0) => $number + 1; $six = pipe($addOne, 1) ->pipe($addOne) ->pipe($addOne) ->pipe($addOne) ->then(fn ($number) => ++$number); $sixAgain = pipe($addOne, 1) ->pipe($addOne) ->pipe($addOne) ->pipe($addOne) ->thenReturn(fn ($number) => ++$number); $five = pipe($addOne, 1) ->pipe($addOne) ->pipe($addOne) ->pipe($addOne) ->result();
Halting the pipeline
halt()
You can return halt()
from a callback to halt the chain. halt
takes an optional result as an argument which you can pass as the final result()
of the chain. Subsequent calls to ->pipe()
will not affect the final result.
use function Inmanturbo\Pipes\{pipe, halt}; $fortyFive = pipe(1); $count = 1; while ($count < 50) { $fortyFive->pipe(fn ($number) => $number < 45 ? ++$number : halt($number)); $count ++; } echo $fortyFive->result(); // 45 echo $fortyFive->pipe(fn ($number) => ++$number)->result(); // 45
You can also call halt on the pipe itself
use function Inmanturbo\Pipes\{pipe, halt}; $fortyFive = pipe(1); $count = 1; while ($count < 50) { if (($number = $fortyFive->result()) >= 45) { $fortyFive->halt($number); } $fortyFive->pipe(fn ($number) => ++$number); $count ++; } echo $fortyFive->result(); // 45 echo $fortyFive->pipe(fn ($number) => ++$number)->result(); // 45
resume()
You can resume the piping with resume. pipe()->resume()
takes an optional callback and behaves the same as pipe()->pipe()
if a callback is passed
use function Inmanturbo\Pipes\{pipe, halt}; $fortySix = pipe(1); $count = 1; while ($count < 50) { if (($number = $fortySix->result()) >= 45) { $fortySix->halt($number); } $fortySix->pipe(fn ($number) => ++$number); $count ++; } echo $fortySix->result(); // 45 echo $fortySix->resume(fn ($number) => ++$number)->result(); // 46
hop() and Laravel
This package doesn't require laravel to use pipe or hop()
, but hop()
(higher-order-pipe) is a higher order function intended for working with Laravel's Pipeline helper. This higher-order-function takes a callback which takes a single argument, and wraps the $callback
for you in a closure which implements function($next, $passable)
.
use Illuminate\Pipeline\Pipeline; use function Inmanturbo\Pipes\hop; class Add { public function add($number) { return $number +1; } } class InvokeAdd { public function __invoke($number) { return $number +1; } } $five = (new Pipeline)->send(1) ->pipe(hop(fn($number) => $number +1)) ->pipe(hop(new InvokeAdd)) ->pipe(hop(InvokeAdd::class)) ->pipe(hop(fn($number) => (new Add)->add($number))) ->thenReturn(); // 5
You can optionally pass a single middleware
as a second argument to hop()
, and it will get called before the first argument, which allows you to determine if the pipeline should halt before the $callback
ever gets executed.
$limitThreeMiddleware = function ($number, $next) { if($number >= 3) { Log::info('Limit hit'); return $number; } return $next($number); }; $five = (new Pipeline)->send(1) ->pipe(hop(fn($number) => $number +1, $limitThreeMiddleware)) ->pipe(hop(new InvokeAdd, $limitThreeMiddleware)) // Limit hit ->pipe(hop(InvokeAdd::class, $limitThreeMiddleware)) ->pipe(hop(fn($number) => (new Add)->add($number), $limitThreeMiddleware)) ->thenReturn(); // 3