Re: [PHP-DEV] [RFC] Pipe Assignment Operator

Hey Caleb,

On 10.07.26 11:45, Caleb White wrote:

Hi internals,

I'd like to open discussion on my (first!) RFC for the pipe assignment operator (|>=):
PHP: rfc:pipe_assignment_operator

It adds a compound assignment form of the pipe operator, so that
$x |>= callable is shorthand for $x = $x |> callable, with
support for chaining. Implementation with tests is at:
feat: add pipe assignment operator (`|>=`) by calebdw · Pull Request #22633 · php/php-src · GitHub

Looking forward to your feedback.

Thanks!
Caleb

Thanks for the RFC! As you already could guess I am in support. Been running your implementation and didn't run into anything unexpected, nor was I able to break it in different ways than the current pipe behaviour. Though, I'd like to add the following.

1) This was feeling nitpicky at first, but it doesn't leave me alone since I first read your RFC. :slight_smile: Now, given what Vadim added to the discussion, I think it is worth bringing it up. Right now we are doing:

$someVar = $someVar |> array_filter(...);

the RFC allows to omit the noise:

$someVar =                    |> array_filter(...);

you see where I am going; now let's remove the white space:

$someVar =|> array_filter(...);

my point is: why `|>=` and not `=|>`?

I think the latter would be more intuitive: assign the result of piping to the variable. I know that `|>=` follows the established `foo=` convention for compound assignments like `??=` or `.=`; which is an argument in its favour. Still, I think `=|>` better communicates how this particular operation expands. Especially when considering future additions like the ones Vadim proposes in future scope.

Because a side effect of having `=|>` would be that the right hand side of `|>` remains open to extension for potential in-pipe-behaviour modifying features like the ones Vadim proposes -- without the final assignment being affected. As in, `=|>` assigns the pipe result, while `=|>+`, `=|>-`, `=|> .`, `=|>whatever` allow modifying in-pipe behaviour. This would make sense because perhaps these pipe behaviour operators will also be allowed for non-shorthand assigned (`|>+`, `|> -`, `|> .` ) pipes that want the resulting immutable right hand side Larry made an argument for. Having a clear separation between assignment, and potential future additions to modify pipe behaviour makes sense. As in, want to add a modifying operator later? Cool, your assignment remains in tact `=|>`. You just add a modifying operator `=|>-` instead of flipping from `|>=` to `|>-=` which would affect final assignment *and* in-pipe behaviour -- having the modifier out of the final assignment decision is cleaner.

Also, it is not impossible that what Vadim proposes in future scope would result in something like `|>=??=` which IMO makes an even stronger point for strictly separating what is proposed here and potential future additions of modifying operators: `=|> .. |> ??=`. Because these *are* different: `... |> ??=` (for in-pipe operations) and `=|> ... |> ??=` (only for the final assignment). Vadims proposal makes sense on it's own, but it currently mixes two concerns: in-pipe operations and final assignment -- which I believe should be separated. Both of your proposals (especially Vadims future scope) should probably rather complement each other instead of competing; I think what I am writing here potentially could make this easier.

One additional soft argument is that text ligatures will show >= as ≥ which will just add to the confusion if modifying operators as Vadim proposes in future scope would ever be added.

Long story short: `=|>`over `|>=` IMO has a lot of appeal.

Though, I know not having `|>=` would deprive us of the "volcano operator" naming; sorry for being the party pooper.

2) Holly brought up this example:

$foo |>= a(…)
$foo |>= b(…)
$foo |>= c(…)

and it seems you talked past each other. Hollys example didn't end lines with semicolons, while your answer had them. Currently, the non-semicolon variant:

$foo = "hello";
$foo |>= strtoupper(...) // no semicolon
$foo |>= strtolower(...);
var_dump($foo);

throws with "Parse error: syntax error, unexpected variable "$foo" in". In the same way the current

$foo = "hello";
$foo |> strtoupper(...) // no semicolon
$foo |> strtolower(...);
var_dump($foo);

throws. I just wanted to point this out because apparently it caused confusion in the discussion. Perhaps worth to clarify the RFC (even though it's also current pipe behaviour).

3) I recently discovered a bug [1] when pipes are combined with property hooks. Unsurprisingly, with your addition it is the same. However, since this shows that pipes are not behaving everywhere identical it would probably be good to add some tests to confirm the interaction of assigned pipes with property hooks, readonly etc. -- and maybe also to clarify it in the RFC.

---

Cheers
Nick

[1] Fatal errors for property hooks in combination with pipes · Issue #22587 · php/php-src · GitHub

Hi

On 2026-07-20 16:55, Bob Weinand wrote:

I just have one more question to the RFC author, which I see in the implementation, but the RFC is not explicitly noting:
Is it intentional that fetching is repeated?

I would consider that a bug in the implementation, given that the “Single-Evaluation Guarantee” section mentions:

When the LHS contains sub-expressions, they are evaluated exactly once:

and

This is the same guarantee that ??= provides over $x = $x ?? default, implemented using the same compile-time memoization mechanism.

Thus if it behaves differently to `??=`, it's a bug in the implementation :slight_smile:

Best regards
Tim Düsterhus

On Monday, July 20th, 2026 at 14:55, Bob Weinand <bobwei9@hotmail.com> wrote:

I just have one more question to the RFC author, which I see in the
implementation, but the RFC is not explicitly noting:
Is it intentional that fetching is repeated?
It will literally desugar $a->b->c |>= strtolower(...); to
$a->b->c = strtolower($a->b->c); resulting in double execution
of e.g. property get hooks.

On Monday, July 20th, 2026 at 15:24, Tim Düsterhus <tim@bastelstu.be> wrote:

I would consider that a bug in the implementation, given that the
"Single-Evaluation Guarantee" section mentions:

    When the LHS contains sub-expressions, they are evaluated
    exactly once:

Thus if it behaves differently to `??=`, it's a bug in the
implementation :slight_smile:

Hi Bob, Tim,

Thanks for flagging this. I tested the scenario Bob described and
`|>=` behaves identically to `??=` here:

    class Inner {
        public string $c = "hello" {
            get { echo "Inner::c GET\n"; return $this->c; }
            set(string $v) { echo "Inner::c SET\n"; $this->c = $v; }
        }
    }
    class Outer {
        public Inner $b {
            get { echo "Outer::b GET\n"; return $this->b; }
        }
        public function __construct() { $this->b = new Inner(); }
    }

    $a = new Outer();
    $a->b->c |>= strtoupper(...);
    // Outer::b GET (read)
    // Inner::c GET (read)
    // Outer::b GET (write-back)
    // Inner::c SET (write-back)

    $a->b->c ??= "default";
    // Outer::b GET (read)
    // Inner::c GET (read)
    // Outer::b GET (write-back)
    // Inner::c SET (write-back)

Both `|>=` and `??=` fetch the intermediate chain twice (once for the
read, once for the write-back). The single-evaluation guarantee in
the RFC refers to sub-expressions like `$arr[expensive_call()]`,
where the call itself is evaluated only once. That works correctly:

    $arr[track()] |>= strtoupper(...);
    // track() is called exactly once

So the behavior is consistent with `??=`. Bob, your observation about
optimizing intermediate chain fetches with ad-hoc references is
interesting, but that would be an improvement to all compound
assignment operators, not something specific to `|>=`.

I've updated the RFC to clarify this in the Single-Evaluation
Guarantee section.

On Sunday, July 19th, 2026 at 08:35, Nick Sdot <php@nicksdot.dev> wrote:

1) my point is: why `|>=` and not `=|>`?

Hi Nick,

Tim already covered this well, but I agree with him: `|>=` follows
the established `op=` convention for compound assignments (`+=`,
`.=`, `??=`). `=|>` looks like a special form of `=>` rather than
an assignment, and consistency with existing patterns is worth more
than theoretical future extensibility for operators that may never
materialize.

2) Holly's example without semicolons

Yeah, that was just a missing semicolon in the email example;
standard parse error, same as with `|>`. Nothing to address there.

3) I recently discovered a bug [1] when pipes are combined with
property hooks.

Thanks for flagging this. I've added a test (assign_pipe_018.phpt)
that covers `|>=` with get/set hooks, virtual properties, and
readonly properties. The `|>=` side works correctly; the bug you
found (#22587) is specific to the base `|>` operator and is not
introduced or affected by this RFC.

Thanks for testing the implementation and for the detailed feedback!

Best,
Caleb

Hi

On 2026-07-21 06:18, Caleb White wrote:

So the behavior is consistent with `??=`. Bob, your observation about
optimizing intermediate chain fetches with ad-hoc references is
interesting, but that would be an improvement to all compound
assignment operators, not something specific to `|>=`.

I've updated the RFC to clarify this in the Single-Evaluation
Guarantee section.

Thank you. The clarification makes sense to me and I don't have further comments on the RFC.

As indicated in php.internals: Re: [RFC] Pipe Assignment Operator, your RFC could still be included in PHP 8.6. Even with this clarification, which is a “minor change” you would be just in time. Any further change to the RFC text will miss the deadline.

*If* you want to try to get into PHP 8.6, you'll need to open voting on the 28th of July very shortly after 04:18 UTC until August 11th the same time + a bit of buffer. You will also need to send an intent to vote email at least 48 hours before that. The decision is up to you. I just wanted to provide you with all the necessary information as a first-time RFC author to make an adequate decision.

Best regards
Tim Düsterhus

On Wednesday, July 22nd, 2026 at 09:54, Tim Düsterhus <tim@bastelstu.be> wrote:

Thank you. The clarification makes sense to me and I don't have further
comments on the RFC.

As indicated in php.internals: Re: [RFC] Pipe Assignment Operator, your RFC
could still be included in PHP 8.6. Even with this clarification, which
is a “minor change” you would be just in time. Any further change to the
RFC text will miss the deadline.

*If* you want to try to get into PHP 8.6, you'll need to open voting on
the 28th of July very shortly after 04:18 UTC until August 11th the same
time + a bit of buffer. You will also need to send an intent to vote
email at least 48 hours before that. The decision is up to you. I just
wanted to provide you with all the necessary information as a first-time
RFC author to make an adequate decision.

Best regards
Tim Düsterhus

Hi Tim,

Thank you for all your help, patience, and guidance throughout this
process, especially as a first-time RFC author. And I really appreciate
you going to bat for this RFC in the thread---thanks again.

I do intend to try to get this into 8.6. Here's the schedule I'm planning:
  - Intent to vote: within the next day or so
  - Open voting: Monday July 28th ~04:30 UTC
  - Voting closes: Monday August 11th ~04:30 UTC

If the vote passes, what's the process for getting the PR merged?

Best,
Caleb