Skip to content(if available)orjump to list(if available)

PHP 8.5 adds pipe operator

PHP 8.5 adds pipe operator

38 comments

·August 5, 2025

abrookewood

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'))));

gbalduzzi

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)

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

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 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

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#!