PHP 8.5 adds pipe operator
38 comments
·August 5, 2025gbalduzzi
I like it.
I really believe the thing PHP needs the most is a rework of string / array functions to make them more consistent and chain able. Now they are at least chainable.
I'm not a fan of the ... syntax though, especially when mixed in the same chain with the spread operator
colecut
PHP string / array functions are consistent.
string functions use (haystack, needle) and array functions use (needle, haystack)
because that's the way the underlying C libraries also worked
Einenlum
They're not though.
array_filter takes (arr, callback)
https://www.php.net/manual/en/function.array-filter.php
array_map takes (callback, arr)
noduerme
Agree, the ... syntax feels confusing when each fn($x) in the example uses $x as the name of its argument.
My initial instinct would be to write like this:
`$result = $arr
|> fn($arr) => array_column($arr, 'tags') // Gets an array of arrays
|> fn($cols) => array_merge(...$cols)`
Which makes me wonder how this handles scope. I'd imagine the interior of some chained function can't reference the input $arr, right? Does it allow pass by reference?Einenlum
You can write it this way. The parameter name is arbitrary. And no, to my knowledge you can't access the var from the previous scope
bapak
Meanwhile the JS world has been waiting for 10 years for this proposal, which is still in stage 2 https://github.com/tc39/proposal-pipeline-operator/issues/23...
wouldbecouldbe
It’s really not needed, syntax sugar. With dots you do almost the same. Php doesn’t have chaining. Adding more and more complexity doesn’t make a language better.
bapak
Nothing is really needed, C89 was good enough.
Dots are not the same, nobody wants to use chaining like underscore/lodash allowed because it makes dead code elimination impossible.
troupo
> With dots you do almost the same.
Keyword: almost. Pipes don't require you to have many different methods on every possible type: https://news.ycombinator.com/item?id=44794656
EGreg
It’s not really chaining
More like thenables / promises
wouldbecouldbe
It looks like chaining, but with possibility of adding custom functions?
te_chris
Dots call functions on objects, pipe passes arguments to functions. Totally missing the point.
lacasito25
in typescript we can do this
let res res = op1() res = op2(res.op1) res = op3(res.op2)
type inference works great, and it is very easy to debug and refactor. In my opinion even more than piping results.
Javascript has enough features.
sandreas
While I appreciate the effort and like the approach in general, in this use case I really would prefer extensions / extension functions (like in Kotlin[1]) or an IEnumerable / iterator approach (like in C#).
$arr = [
new Widget(tags: ['a', 'b', 'c']),
new Widget(tags: ['c', 'd', 'e']),
new Widget(tags: ['x', 'y', 'a']),
];
$result = $arr
|> fn($x) => array_column($x, 'tags') // Gets an array of arrays
|> fn($x) => array_merge(...$x) // Flatten into one big array
|> array_unique(...) // Remove duplicates
|> array_values(...) // Reindex the array.
;
feels much more complex than writing $result = $arr->column('tags')->flatten()->unique()->values()
having array extension methods for column, flatten, unique and values.1: https://kotlinlang.org/docs/extensions.html#extension-functi...
cess11
PHP has traits, just invent that API, put it in a trait and add it to your data classes.
troupo
The advantage is that pipes don't care about the type of the return value.
Let's say you add a reduce in the middle of that chain. With extension methods that would be the last one you call in the chain. With pipes you'd just pipe the result into the next function
phplovesong
The stdlib is so inconsistent this will be a nightmare.
Optionally with a better language you know what order params as passed (array_map / array_filter), but in PHP is a coin coss.
This feels very bolted on and not suited for the stdlib at all.
PHP devs should instead FIRST focus on full unicode support (no, the mb_real_uppercase wont do), and only then focus on a new namespaced stdlib with better design.
Einenlum
This.
We definitely need a better stdlib with appropriate data structures
habibur
I tried to emulate something similar with PHP at one point. But the problem with PHP was parameter order. Especially in functions like array_key_exists() the array element is the 2nd parameter, while pipe operator expects the object to work on be the 1st parameter, the array in these cases.
I believe they have solved this problem by now. Though no idea how.
kijin
The usual solution is to wrap it with a closure.
function($x) { return array_key_exists('needle', $x); }
Or using the arrow function syntax: fn($x) => array_key_exists('needle', $x)
The same trick also helps when you need to use functions with mandatory extra parameters, functions with pass-by-value parameters, etc.librasteve
raku has had feed operators like this since its inception
# pipeline functional style
(1..5)
==> map { $_ * 2 }
==> grep { $_ > 5 }
==> say(); # (6 8 10)
# method chain OO style
(1..5)
.map( * * 2)
.grep( * > 5)
.say; # (6 8 10)
uses ==> and <== for leftwardtrue it is syntax sugar, but often the pipe feed is quite useful to make chaining very obvious
lordofgibbons
Why doesn't PHP remove the horrid $ symbol for variables and the -> symbol for calling methods? I think those alone would do a lot more for its perception and adoption than adding the pipe operator.
phatskat
I actually don’t mind them, and I’ve been out of daily PHP work for a few years now. When I see people denote internal variables with _ or elements with $ in JS, it rubs me the wrong way, but in PHP the $ is kind of nice.
I also prefer the look of ->, it’s _cool_
kijin
Other languages have all sorts of oversized arrows, like ==> and >>>.
-> in PHP and C++ looks clean by comparison.
I'll never forgive them for the brain fart they made of the namespace separator, though.
ChocolateGod
Why not just make types psuedo-objects?
$myString.trim().replace("w", "h");
Which has the advantage of also offering a clean alternative to the fragmented stdlib.
reddalo
I agree. But in PHP it would probably be like this:
$myString->trim()->replace("w", "h");
troupo
Because pipes don't care about the type your function returns. And you don't need hundreds of methods on each type just in case. You just pipe the result of the previous function to the next one.
And those functions can be business logic, or validation, or... Not just object methods
ossusermivami
i wish python had something liek that to be honest
cess11
"A major limitation of the pipe operator is that all the callables in the chain must accept only one required parameter.
For built-in functions, if the function does not accept any parameters, it cannot be used in a chain. For user-land PHP functions, passing a parameter to a function that does not accept any parameters does not cause an error, and it is silently ignored.
With the pipe operator, the return value of the previous expression or the callable is always passed as the first parameter to the next callable. It is not possible to change the position of the parameter."
https://php.watch/versions/8.5/pipe-operator
In the light of these limitations I would not call the Elixir implementation "slightly fancier".
I'm not so sure I'll be upgrading my local PHP version just for this but it's nice that they are adding it, I'm sure there is a lot of library code that would look much better if rewritten into this style.
JaggerJo
Thanks F#!
I love the pipe operator - one of the things I dig about Elixir though many languages have it. It's so much easier to reason about:
VS array_values(array_unique(array_merge(...array_column($arr, 'tags'))));