PHP 8.5's Pipe Operator: Cleaner Data Pipelines in PHP
PHP 8.5's pipe operator (|>) enables clean left-to-right function chaining. Learn practical examples, Laravel integration with Spatie's Piper, and what comes next.
If you have been writing PHP long enough, you have almost certainly written something like this:
$result = array_values(array_unique(array_map('trim', explode(',', $input))));
You read it right to left to understand what is happening, because PHP’s nested function calls work inside out. Developers reach for temporary variables, loop data through a series of steps manually, or wrap transformations in helper methods just to make the logic readable. PHP 8.5, released in November 2025, ships a solution that PHP internals debated for years: the pipe operator (|>).
What the Pipe Operator Does
The pipe operator takes the value on its left side and passes it as the sole argument to the callable on its right side. The result becomes the input to the next callable in the chain. It is straightforward once you see it in action:
$result = " Hello World "
|> trim(...)
|> strtolower(...)
|> str_word_count(...);
Each (...) after the function name is PHP’s first-class callable syntax, introduced in PHP 8.1. It converts a function reference into a proper callable object. The value flows left to right through each step, and the final result is assigned to $result. Compare that to the nested version:
$result = str_word_count(strtolower(trim(" Hello World ")));
Both are correct. The pipe version reads like a recipe. The nested version requires reading inside out.
The Single-Parameter Rule
The most important thing to understand about the pipe operator is that every callable on the right side must accept exactly one required parameter. The piped value is always passed as that single argument.
This works cleanly for functions like trim, strtolower, strtoupper, strlen, json_encode, base64_encode, and array_values. It requires a wrapper for functions where the target value is not the first argument, or where the function needs multiple parameters.
The most common workaround is a short closure:
$result = $input
|> trim(...)
|> fn($s) => explode(',', $s)
|> fn($arr) => array_map('trim', $arr)
|> fn($arr) => array_filter($arr, fn($item) => strlen($item) > 0)
|> array_values(...);
The closure wraps the multi-parameter call and accepts the single piped value. It is a few extra characters, but the chain still reads top to bottom.
Real-World Examples
Slug Generation
Generating a URL slug from a blog post title is a classic multi-step transformation. Without the pipe operator:
function slugify(string $title): string {
return strtolower(
trim(
preg_replace('/[^a-z0-9-]/', '',
preg_replace('/\s+/', '-',
strip_tags($title)))
)
);
}
With the pipe operator:
function slugify(string $title): string {
return $title
|> strip_tags(...)
|> fn($s) => preg_replace('/\s+/', '-', $s)
|> fn($s) => preg_replace('/[^a-z0-9-]/', '', $s)
|> strtolower(...)
|> trim(...);
}
The order of operations is explicit. You can read down the chain and understand each transformation without mentally unwrapping nested calls.
Encoding Payloads
A common pattern for caching serialized data:
$payload = $data
|> json_encode(...)
|> gzcompress(...)
|> base64_encode(...);
Three lines. Three built-in functions. The data flows through each stage in sequence.
Processing API Response Tags
Normalizing a list of tags from an API response:
$tags = $response['tags']
|> fn($tags) => array_map('trim', $tags)
|> fn($tags) => array_map('strtolower', $tags)
|> fn($tags) => array_filter($tags, fn($t) => strlen($t) > 1)
|> array_unique(...)
|> array_values(...);
Each step is a focused, testable transformation. If the tag list comes back malformed, you can isolate exactly which stage produced the bad output.
Laravel Integration with Spatie’s Piper
The Laravel community moved quickly. Spatie released Piper, a package that ports Laravel-style collection and string helper methods as single-argument callables, designed specifically for use with the pipe operator.
composer require spatie/piper
With Piper, multi-parameter operations become clean pipe-compatible callables:
use function Spatie\Piper\Arrays\{map, filter, unique, values};
use function Spatie\Piper\Strings\{lower, trim as strTrim};
$result = $input
|> strTrim()
|> lower()
|> fn($s) => explode(',', $s)
|> map(fn($item) => trim($item))
|> filter(fn($item) => strlen($item) > 1)
|> unique()
|> values();
Piper’s helpers return closures, so each one slots naturally into a pipe chain. This is the most ergonomic integration available today for Laravel applications.
Pipe vs. Laravel Collections
The pipe operator is not a replacement for Laravel Collections. Collections have a richer API for working with lists of objects and integrate deeply with Eloquent. The pipe operator is a general-purpose composition tool for any kind of data transformation: strings, arrays, scalars, objects, or mixed pipelines that cross data type boundaries.
Reach for the pipe operator when you are transforming a single value through a series of functions. Reach for Collections when you are working with a list and want access to groupBy, sortBy, mapToGroups, or other collection-specific operations.
What PHP 8.6 Adds
PHP 8.6, expected later in 2026, ships partial function application, and the two features were intentionally designed to complement each other. Partial application lets you fix some arguments of a function and get back a new callable with fewer parameters. The approved RFC passed 33-0, which is about as close to consensus as PHP internals gets.
Once partial application lands, the ? placeholder fills the gap that closures currently cover:
// PHP 8.6 partial application with pipe
$tags = $response['tags']
|> array_map(trim(...), ?)
|> array_map(strtolower(...), ?)
|> array_filter(?, fn($t) => strlen($t) > 1)
|> array_unique(...)
|> array_values(...);
The ? marks the argument the pipe fills in. Multi-parameter built-ins become pipe-compatible without wrapper closures. The PHP Foundation has been clear that the pipe operator shipped first to validate the design, with partial application as the planned complement.
Should You Use It Today?
PHP 8.5 is on general availability. If your project is running 8.5, the pipe operator is available with no configuration. The single-parameter limitation means you will write wrapper closures for some operations, but that friction is minor once you are used to the pattern.
The pipe operator works well for string and array normalization pipelines, serialization and encoding chains, data sanitization workflows, and anywhere you currently write deeply nested function calls or chains of temporary variables just for readability.
Try converting one nested function chain in your codebase. If the result is easier to scan and explain to a colleague, that is the answer you are looking for.
Sources
- Pipe Operator (|>) - PHP 8.5 on PHP.Watch
- PHP 8.5 Adds Pipe Operator - The PHP Foundation
- The Pipe Operator in PHP 8.5 - stitcher.io
- Introducing Piper: array and string manipulation with the pipe operator - Spatie
- PHP 8.5 Pipe Operator: Real Laravel Examples - jump24
- Seven Real-World Examples of Using the Pipe Operator - Amit Merchant
- RFC: Pipe Operator v3 - PHP Wiki