anarchitecture/pipe is a small PHP library that provides functions returning unary callables,
designed to be used with the PHP 8.5 pipe operator (|>).
The goal is to make it easy to leverage existing functions in pipe expressions without having to write inline closures.
- PHP 8.5+ (pipe operator support)
composer require anarchitecture/pipeuse Anarchitecture\pipe as p;
$a = range(1, 8)
|> p\array_map(fn ($x) => $x ** $x)
|> p\array_chunk(4)
|> p\array_map(array_sum(...));
// [288, 17650540]Each helper provides a unary callable:
use Anarchitecture\pipe as p;
p\array_map(fn ($x) => $x * 2);
// fn (array $input) => array_map(fn ($x) => $x * 2, $input)p\array_all(callable $callback)p\array_any(callable $callback)p\array_chunk(int $length, bool $preserve_keys = false)p\array_dissoc(string|int ...$keys)— returns a copy of the array without the given key(s)p\array_filter(callable $callback)— filters an array, callback takes array values as single argumentp\array_flatten(array $arrays)— flattens one level (array of arrays to single array). String-key clashes: later arrays overwrite earlier; numeric keys are reindexedp\array_map(callable $mapper)— maps each value to a new value (like \array_map); preserves keys for single-array mappingp\array_map_recursive(callable $mapper)— recursively maps leaf values in nested arrays; preserves keys at every levelp\array_map_recursive_with_path(callable $mapper)— recursively maps leaf values in nested arrays; mapper is called as$mapper($value, $path)where$pathis the list of keys from root to leaf (preserves keys)p\array_nth(int $i)— nth element ornull(supports negative indexes:-1is last,-2is second-last, etc.)p\array_reduce(callable $reducer, mixed $initial = null)— reduces to a single value; reducer is called asreducer($carry, $value)(no key); empty input returns $initial (ornullif omitted)p\array_reduce_until(callable $reducer, callable $until, mixed $initial = null)— reduces left-to-right until$until($carry, $value, $key) === true; returns[$carry, $key, $value]or[$carry, null, null]if never triggeredp\array_slice(int $offset, ?int $length = null, bool $preserve_keys = false)p\array_transpose()— transpose a 2D array (matrix), preserving row/column keys (pads missing values withnull)p\array_sum(callable $callback)— map each element over $callback then sum numeric resultsp\array_unique(int $flags = SORT_STRING)p\sort(int $flags = SORT_REGULAR)p\rsort(int $flags = SORT_REGULAR)p\uasort(callable $comparator)— sorts values and preserves keys (like PHP\uasort)p\usort(callable $comparator)— sorts values and reindexes keys (like PHP\usort)
p\explode(string $separator, int $limit = PHP_INT_MAX)p\implode(string $separator = "")p\str_replace(string|array $search, string|array $replace)p\str_starts_with(string $prefix)p\preg_match(string $pattern, int $flags = 0, int $offset = 0)— returns the$matchesarray (empty array when no match)p\preg_match_all(string $pattern, int $flags = 0, int $offset = 0)— returns the$matchesarray (empty array when no match)p\preg_replace(string|array $pattern, string|array $replacement, int $limit = -1)
p\collect(iterable $iterable)— terminal: collect any iterable into an array (preserves keys)p\iterable_all(?callable $callback = null)— returnstrueif all items match (or no item is!== truewhen callback isnull); short-circuitsp\iterable_any(?callable $callback = null)— returnstrueif any item matches (or is=== truewhen callback isnull); short-circuitsp\iterable_filter(callable $callback)— yields matching items for which$callbackreturnstruep\iterable_first(iterable $iterable)— returns first item ornull(consumes one element)p\iterable_map(callable $callback)— yields items mapped over$callback. Preserves keys.p\iterable_nth(int $n)— returns the nth item (0-based); consumes up to n+1 items; returnsnullif out of rangep\iterable_reduce(callable $callback, $initial = null)— reduces an iterable to a single valuep\iterable_string(int $size = 1)— lazily iterate over a string as bytes ($size = 1) or byte-chunks ($size > 1).p\iterable_take(int $count)— yields first$countitemsp\iterable_ticker(int $start = 0)— infinite counter generatorp\iterable_window(int $size, bool $circular = false)– sliding windows over iterables (optionally circular)p\iterable_zip(iterable ...$right)— lazily zips the left iterable with one or more right iterables; yields tuples and stops at the shortest (preserves left keys)p\iterate(callable $callback, bool $include_seed = true)— infinite sequence by repeated application (yields seed first by default)
p\if_else(callable $predicate, callable $then, callable $else)— applies$then($value)when$predicate($value) === true, otherwise$else($value)p\unless(callable $predicate, callable $callback)— applies$callbackonly when$predicate($value) !== true(otherwise returns the input unchanged)p\when(callable $predicate, callable $callback)— applies$callbackonly when$predicate($value) === true(otherwise returns the input unchanged)
p\equals(mixed $value)— returns true ifitem === $valuep\value(mixed $value)— constant function returns$valuep\not(callable $callable)— boolean invert the$callable's return value
p\apply(callable $callback)— applies an array of arguments to a callable (numeric keys => positional, string keys => named; mixed keys rejected)p\increment(int|float $by = 1)p\tap(callable $callback)— “tap” helper: calls$callback($value)for side effects and returns$valueunchangedp\zip_map(?callable $callback)— zip semantics over multiple arrays
A few helpers differ from their underlying built-ins to make pipelines pleasant:
p\apply($callback)rejects arrays with mixed numeric and string keys (to avoid PHP’s “positional after named” edge cases).p\array_dissoc()removes one or more keys and returns the modified array (missing keys are ignored).p\preg_match()andp\preg_match_all()return the$matchesarray (like the third arg of\preg_match()), not the match count; no match =>[].p\sort(),p\rsort(),p\usort(),p\uasort()return the sorted array (native functions returntrue/false).p\usort()reindexes keys (it returns a list). Usep\uasort()when you need to preserve key associations.p\zip_map($callback)([])returns[](avoids callingarray_map()with no arrays).- For debugging,
p\tap(\var_dump(...))is the direct replacement for the removedp\var_dump().
use Anarchitecture\pipe as p;
// numeric keys => positional
$out1 = [10 => "a", 20 => "b", 30 => "c"]
|> p\apply(fn (string $a, string $b, string $c) => $a . $b . $c);
// "abc"
// string keys => named
$out2 = ["b" => 2, "a" => 1]
|> p\apply(fn (int $a, int $b) => $a - $b);
// -1use Anarchitecture\pipe as p;
$out = " Hello "
|> p\when(is_string(...), trim(...))
|> p\when(p\equals("Hello"), p\value("bye"));
// "bye"use Anarchitecture\pipe as p;
$out = "Hello"
|> p\if_else(
p\equals("Hello"),
p\value("bye"),
p\value("unknown")
);
// "bye"use Anarchitecture\pipe as p;
$result = 0
|> p\iterate(static fn(int $x) : int => $x + 1)
|> p\iterable_take(4)
|> p\collect(...);
// [0, 1, 2, 3]use Anarchitecture\pipe as p;
$sumPairs = [[6, 7, 8], [10, 20, 30]]
|> p\zip_map(fn ($a, $b) => $a + $b);
// [16, 27, 38]use Anarchitecture\pipe as p;
$user = [
'id' => 123,
'email' => 'a@example.com',
'password_hash' => '...',
];
$public = $user
|> p\array_dissoc('password_hash');
// ['id' => 123, 'email' => 'a@example.com']use Anarchitecture\pipe as p;
$out = " Hello "
|> trim(...)
|> p\tap(\var_dump(...)) // debug
|> strtoupper(...);
// prints "Hello", returns "HELLO"For numeric matrices it behaves like a regular transpose:
use Anarchitecture\pipe as p;
$matrix = [
[1, 2, 3],
[4, 5, 6],
];
$t = $matrix
|> p\array_transpose();
// [
// [1, 4],
// [2, 5],
// [3, 6]
//]For string keyed matrices, it pads missing cells with null:
use Anarchitecture\pipe as p;
$matrix = [
'r1' => ['a' => 1, 'b' => 2],
'r2' => ['a' => 3, 'c' => 4],
];
$t = $matrix
|> p\array_transpose();
// [
// 'a' => ['r1' => 1, 'r2' => 3],
// 'b' => ['r1' => 2, 'r2' => null],
// 'c' => ['r1' => null, 'r2' => 4],
// ]use Anarchitecture\pipe as p;
$result = [1, 2]
|> p\iterable_zip([10, 20], [100, 200])
|> p\collect(...);
// [
// [1, 10, 100],
// [2, 20, 200]
//]use Anarchitecture\pipe as p;
// linear (default): full windows only, no wraparound
$linear = [1, 2, 3, 4, 5, 6]
|> p\iterable_window(3)
|> p\collect(...);
// [
// [1, 2, 3],
// [2, 3, 4],
// [3, 4, 5],
// [4, 5, 6],
// ]
// circular: adds the boundary-crossing windows (end -> start)
$circular = [0, 1, 2, -2, -1]
|> p\iterable_window(size: 4, circular: true)
|> p\collect(...);
// [
// [0, 1, 2, -2],
// [1, 2, -2, -1],
// [2, -2, -1, 0],
// [-2, -1, 0, 1],
// [-1, 0, 1, 2],
// ]- functions return unary callables
- explicitly designed for pipe expressions
- thin wrappers around existing PHP functions
- small, predictable, and composable
- no magic, just functions
Experimental — based on PHP 8.5 pipes.
MIT