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

Caleb White <[email protected]> Tue, 21 Jul 2026 04:18:28 +0000
Newsgroups gmane.comp.php.devel
Message-ID <rgoknlEYQDatFHhOAqRO1XmuWwBAr8MATveWmG2syA8HthXFGJqm-3a78iBGII4O-o2jP_CVdISkiNQAfQ6f8Yp3NoNtx-soRtSCKoyPNhA=@pm.me>
On Monday, July 20th, 2026 at 14:55, Bob Weinand <[email protected]> wrot=
e:
> 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 |>=3D strtolower(...); to
> $a->b->c =3D strtolower($a->b->c); resulting in double execution
> of e.g. property get hooks.

On Monday, July 20th, 2026 at 15:24, Tim D=C3=BCsterhus <[email protected]> =
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 `??=3D`, it's a bug in the
> implementation :-)

Hi Bob, Tim,

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

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

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

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

Both `|>=3D` and `??=3D` 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()] |>=3D strtoupper(...);
    // track() is called exactly once

So the behavior is consistent with `??=3D`. 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 `|>=3D`.

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



On Sunday, July 19th, 2026 at 08:35, Nick Sdot <[email protected]> wrote:
> 1) my point is: why `|>=3D` and not `=3D|>`?

Hi Nick,

Tim already covered this well, but I agree with him: `|>=3D` follows
the established `op=3D` convention for compound assignments (`+=3D`,
`.=3D`, `??=3D`). `=3D|>` looks like a special form of `=3D>` 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 `|>=3D` with get/set hooks, virtual properties, and
readonly properties. The `|>=3D` 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