PHP 8.5 adds pipe operator

138 lemper 80 8/5/2025, 4:13:43 AM thephp.foundation ↗

Comments (80)

bapak · 3h ago
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...
defraudbah · 40s ago
do not let me start on monads in golang...

both are going somewhere and super popular though

fergie · 10m ago
Good- the [real world examples of pipes in js](https://github.com/tc39/proposal-pipeline-operator?tab=readm...) are deeply underwhelming IMO.
avaq · 48m ago
Not only have we been waiting for 10 years, the most likely candidate to go forward is not at all what we wanted when the proposal was created:

We wanted a pipe operator that would pair well with unary functions (like those created by partial function application, which could get its own syntax), but that got rejected on the premise that it would lead to a programming style that utilizes too many closures[0], and which could divide the ecosystem[1].

Yet somehow PHP was not limited by these hypotheticals, and simply gave people the feature they wanted, in exactly the form it makes most sense in.

[0]: https://github.com/tc39/proposal-pipeline-operator/issues/22... [1]: https://github.com/tc39/proposal-pipeline-operator/issues/23...

xixixao · 12m ago
I guess partially my fault, but even in the article, you can see how the Hack syntax is much nicer to work with than the functional one.

Another angle is “how much rewriting does a change require”, in this case, what if I want to add another argument to the rhs function call. (I obv. don’t consider currying and point-free style a good solution)

lexicality · 12m ago
Am I correct in my understanding that you're saying that the developers of the most widely used JS engine saying "hey we can't see a way to implement this without tanking performance" is a silly hypothetical that should be ignored?
wouldbecouldbe · 3h ago
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 · 3h ago
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.

pjmlp · 1h ago
K&R C was good enough for UNIX System V, why bother with C89.
troupo · 2h ago
> 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

te_chris · 2h ago
Dots call functions on objects, pipe passes arguments to functions. Totally missing the point.
EGreg · 3h ago
It’s not really chaining

More like thenables / promises

wouldbecouldbe · 3h ago
It looks like chaining, but with possibility of adding custom functions?
bapak · 2h ago
It's chaining without having to vary the return of each function. In JS you cannot call 3.myMethod(), but you could with 3 |> myMethod
cyco130 · 2h ago
It requires parentheses `(3).myMethod()` but you can by monkey patching the Number prototype. Very bad idea, but you absolutely can.
lacasito25 · 2h ago
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.

gbalduzzi · 3h ago
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 · 2h ago
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 · 1h ago
They're not though.

array_filter takes (arr, callback)

https://www.php.net/manual/en/function.array-filter.php

array_map takes (callback, arr)

https://www.php.net/manual/en/function.array-map.php

exasperaited · 1h ago
This is "english-sentence-order-consistent", as it goes.

Array filter is "filter this array with this function".

Array map is "map this function over this array".

But I agree any replacement function should be consistent with Haskell.

eurleif · 42m ago
One can construct English sentences in the opposite order. There is no singular "English sentence order".

"Filter for this function in this array"

"Map over this array with this function"

exasperaited · 25m ago
Right, but these are both more unwieldy.

One filters something with something else, in the real world. Filter water with a mesh etc.

And (in maths, at least) one maps something onto something else. (And less commonly one maps an area onto paper etc.)

Just because you can make your two sentences does not make them natural word order.

hnlmorg · 9m ago
When you consider that PHP is used by hundreds of thousands of non-native English speakers, I don’t really think you can make a legitimate claim that “English sentence order” trumps “consistent argument ordering”.

There’s enough viral videos online of how even neighbouring European counties order common sentences differently. Even little things like reading the time (half past the previous hour vs half to the next hour) and counting is written differently in different languages.

So modelling the order of parameters based on English vernacular doesn’t make a whole lot of sense for programming languages used by programmers of all nationalities.

quietbritishjim · 20m ago
"Map the integers 0..10 to double their values"

You don't map a function unless that is the thing you're manipulating.

Y-bar · 8m ago
> because that's the way the underlying C libraries also worked

I feel like this is a weak defence of the internally inconsistent behaviour. As someone who has been programming with PHP for over twenty years now, most of them professionally, I still cannot remember the needle/haystack order in these functions, I thank intellisense for keeping me sane here.

As evident with this pipe operator, or with for example Attributes, PHP does not need to religiously follow the C way of doing things, so why not improve it instead of dismissing it as "it is the way it is because that is the way it was"?

noduerme · 2h ago
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 · 1h ago
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
cess11 · 27m ago
You can do

     function ($parameter) use ($data) { ... }
to capture stuff from the local environment.

Edit: And you can pass by reference:

   > $stuff = [1]
   = [
       1,
     ]

   > $fn = function ($par) use (&$stuff) { $stuff[] = $par; }
   = Closure($par) {#3980 …2}

   > $fn(2)
   = null

   > $stuff
   = [
       1,
       2,
     ]

Never done it in practice, though, not sure if there are any footguns besides the obvious hazards in remote mutation.
abrookewood · 1h ago
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:

  $result = $arr
    |> fn($x) => array_column($x, 'tags')
    |> fn($x) => array_merge(...$x)
    |> array_unique(...)
    |> array_values(...)
VS array_values(array_unique(array_merge(...array_column($arr, 'tags'))));
qwertox · 1h ago
I don't see how this is hard to reason about, assuming this is the resulting code when using variables:

  $tags        = ...array_column($arr, 'tags');
  $merged_tags = array_merge($tags);
  $unique_tags = array_unique($merged_tags);
  $tag_values  = array_values($unique_tags);
It also makes it easier to inspect the values after each step.
jeroenhd · 7m ago
Correctly naming things is one of the harder challenges in computer programming. Putting effort into naming intermediates that you're going to throw out is a waste. Plus, the more variables you introduce, the more likely you'll accidentally re-use a variable name somewhere down the line.

With PHP allowing variable initialization in one branch but not the other, and continuing execution by default when an undeclared variable is passed, declaring more variables can lead to an annoying class of bugs that would require significant (breaking) changes to the core language to completely eliminate.

cess11 · 16m ago
Such variable threading tends to be harder to skim through in production code, the intermediates become noise that's harder to filter out than a repeated symbol like |>.

Preferably you should also be sure that the functions are compatible with the data type going in and only rarely have to break it to dump data mid-chain. If you expect that kind of erroring it's likely a builder-chain with -> is a better alternative and do logging in the methods.

r34 · 1h ago
Your version includes 4 variables. Pipes don't create those intermediate variables, so they are more memory efficient.

Readability is mostly matter of habit. One reads easily what he/she is used to read.

qwertox · 57m ago
It's true that pipes are more readable, and for many cases they will be the better option, but the example of nested functions just doesn't hold.

That's like saying someone would use this:

   $result = $arr |> fn($x) => array_column($x, 'tags') |> fn($x) => array_merge(...$x) |> array_unique(...) |> array_values(...)
which is harder to reason about than the nested functions.

   array_values( array_unique( array_merge( ...array_column($arr, 'tags') ) ) );
or

   array_values(
     array_unique(
       array_merge(
         ...array_column($arr, 'tags')
       )
     )
   );
navalino · 20m ago
It is more readable and better option — you have to parse it from the innermost function to the outermost just to understand what it's doing. With the pipe, it's more straightforward: you read it step by step — do this, then that, then the next — just like how you'd naturally read instructions.
girvo · 1h ago
> so they are more memory efficient

They can be. It depends on the language, interpreter, compiler, and whether you do anything with those intermediate variables and the optimiser can get rid of them.

r34 · 34m ago
I thought we are talking about PHP8.5:)
someothherguyy · 1h ago
> It's so much easier to reason about

Is it though? I don't think so.

andrewflnr · 59m ago
It tends to work a little better in Elixir, because you very rarely have to include one-off lambdas in your pipeline. The standard library functions are designed to work with the pipeline operator, where the thing you probably want to thread through is usually the first argument.
sandreas · 3h ago
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 · 2h ago
PHP has traits, just invent that API, put it in a trait and add it to your data classes.
troupo · 2h ago
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

dev_l1x_be · 9m ago
Rust is next? Jokes aside, pipe operators in programming languages have a interesting side effect of enabling railway oriented programming that I miss the most when not working in F#.
mappu · 45m ago
Every single one of those steps buffers into a temporary variable - this isn't efficient like a bash pipe.
quietbritishjim · 39m ago
Genuine question from a non-PHP user:

Does PHP support iterator-like objects? Like Python I mean, where mydict.values() produces values on demand, not immediately realised as a list. Or are all steps necessarily guaranteed to be fully realised into a complete list?

Timwi · 5m ago
The section where the article mentions function composition implies that it doesn't. The article says that compositing the functions before passing them into map would be an optimization. I take that to mean that without the composition, each map fully processes an array passed to it from the previous map, and the first map fully reads the whole file in the example. If it were iterable, the function composition would make no difference compared to a pipeline of multiple maps.

Meanwhile, I'm confused as to why it sometimes says “map” and sometimes “array_map”. The latter is what I'm familiar with and I know that it operates on a whole array with no lazy evaluation. If “map” isn't just a shorthand and actually creates a lazy-evaluated iterable, then I'm confused as to why the function composition would make any difference.

severak_cz · 32m ago
throw_m239339 · 24m ago
PHP does have generators and iterators yes, although I personally rarely use them directly.
phplovesong · 2h ago
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 its an coin toss.

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.

foul · 1h ago
>The stdlib is so inconsistent this will be a nightmare.

I think that callables will end with being useless in this context and everyone will pipe closures to put that $x wherever the stdlib imposes.

Einenlum · 1h ago
This.

We definitely need a better stdlib with appropriate data structures

pknerd · 6m ago
Am I the only one who found it ugly?
librasteve · 3h ago
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 leftward

true it is syntax sugar, but often the pipe feed is quite useful to make chaining very obvious

https://docs.raku.org/language/operators#infix_==%3E

habibur · 2h ago
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 · 1h ago
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.
ChocolateGod · 2h ago
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 · 1h ago
I agree. But in PHP it would probably be like this:

$myString->trim()->replace("w", "h");

troupo · 1h ago
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

someothherguyy · 1h ago
composition would be much nicer than this, maybe soon
lordofgibbons · 2h ago
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.
jeroenhd · 12m ago
Same reason C doesn't introduce classes and C++ doesn't remove pointers: it's a) part of the core language and b) extremely inconsequential for any serious developer.

I actually like the clarity these dollar signs add in a code base. Makes it easier to recognise (dynamic) functions, and makes it harder to accidentally shadow methods.

Other languages will let you do `const Math = {}` and nuke the entire math library, or write stuff like `int fopen = 0;` to make the fopen method call inaccessible in that scope. With PHP, you don't need to restrict your variable name to "something that hopefully won't conflict with an obscure method".

The -> is a leftover from an older programming language that I'd rather have replaced by a ., but not at the cost of breaking existing code (which it surely would).

phatskat · 2h ago
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 · 1h ago
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.

esskay · 30m ago
What would the alternative for a namespace separator be? The backslashes work well with PSR-4 to give a logical visual of the expected directory structure.
LeonM · 47m ago
> I'll never forgive them for the brain fart they made of the namespace separator, though.

You mean the backslash? What's wrong with that?

ahofmann · 27m ago
I honestly don't understand this. The syntax is one of the most boring parts of a programming language. It is solved by the IDE (and now LLMs). I don't care about syntax, I care about what I can build. Since the beginning of time people argue about things like tabs vs. spaces, or the dollar sign and I honestly don't understand why that is. It just doesn't matter.

Just to be clear: consistency does very much matter. The mental load of reading totally different styles of code is awful and a waste of energy.

throw_m239339 · 18m ago
> 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.

Because it simply can't do that in a retro-compatible way. -> isn't so bad, C/C++ uses that as well. as for $ I guess it came from Perl. The point is already used for string concatenation, where other languages would overload the + operator.

DataDaemon · 1h ago
This will be the year of PHP. People are tired of JS.
beardyw · 1h ago
I admire your conviction.
cess11 · 1h ago
"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 · 2h ago
Thanks F#!
ossusermivami · 1h ago
i wish python had something liek that to be honest
keyle · 3h ago
C'mon Dart! Follow up please. Go is a lost cause...
tayo42 · 2h ago
I feel like a kindergartener writing go. I wish another language got popular in the space go is used for.
ioma8 · 1h ago
The syntax is ugly as hell.
frankzander · 23m ago
Amen ... I mean PHP could have been such a good language if the syntax wouldn't be such a show stopper.
JohnKemeny · 11m ago
Thank you for your insight.