rfc:pipe_assignment_operator

PHP RFC: Pipe Assignment Operator

Introduction

PHP 8.5 introduced the pipe operator (|>), enabling functional-style transformation chains. A common pattern is piping a variable through transformations and assigning the result back to the same variable. This RFC proposes |>=, a compound assignment form that eliminates the repetition, consistent with PHP's existing compound assignment operators (+= , .=, ??=, etc.):

// Before
$this->requestData = array_filter($this->requestData);
// or
$this->requestData = $this->requestData |> array_filter(...);
 
$headers = $headers
    |> array_change_key_case(?, CASE_LOWER)
    |> array_filter(?, fn($v) => $v !== '');
 
// After
$this->requestData |>= array_filter(...);
 
$headers |>= array_change_key_case(?, CASE_LOWER)
    |> array_filter(?, fn($v) => $v !== '');

Proposal

The expression $x |>= callable is equivalent to $x = $x |> callable.

When the right-hand side contains a pipe chain, the variable is spliced into the leftmost position:

$x |>= fn1(...) |> fn2(...) |> fn3(...);
// Equivalent to:
$x = $x |> fn1(...) |> fn2(...) |> fn3(...);

This works because |> has higher precedence than |>=. The parser groups the RHS as a pipe chain, and the compiler flattens it, inserting the LHS variable as the initial input.

Variable Targets

All variable types that support compound assignment are supported:

$x |>= strtoupper(...);           // Simple variable
$data["key"] |>= strtoupper(...); // Array dimension
$obj->name |>= strtoupper(...);   // Object property
Foo::$value |>= strtoupper(...);  // Static property

Callable Styles

All callable styles supported by |> work with |>= (including PFA, which eliminates the need for closure wrappers):

$x |>= strtoupper(...);            // First-class callable
$x |>= Formatter::normalize(...);  // Static method
$x |>= $formatter->normalize(...); // Instance method
$x |>= (fn($v) => $v * 2);         // Arrow function (must be parenthesized)
$x |>= $storedCallable;            // Stored callable
$x |>= str_replace('a', 'b', ?);   // Partial function application

Expression Result

Like other compound assignment operators, |>= produces the assigned value as its expression result:

$x = 5;
$y = ($x |>= (fn($v) => $v * 3));
// $x === 15, $y === 15

Single-Evaluation Guarantee

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

$arr[expensive_call()] |>= strtoupper(...);
// expensive_call() is invoked only once

With the manual $x = $x |> ... pattern, sub-expressions are evaluated twice: once on each side of the assignment. |>= is both more correct and more performant. This is the same guarantee that ??= provides over $x = $x ?? default, implemented using the same compile-time memoization mechanism.

Note: for chained property access like $a->b->c |>= strtoupper(...), intermediate fetches (e.g. $a->b) are evaluated once for the read and once for the write-back. This is consistent with ??= and all other compound assignment operators.

Precedence and Associativity

|>= has the same precedence and associativity as all other assignment operators (=, += , .=, ??=, etc.): right-associative, binding tighter than the keyword operators (and, or, xor, yield, print, etc.) but looser than all other operators.

Because |> binds tighter than |>=, pipe chains on the RHS are grouped first. The compiler then splices the LHS variable into the chain. For example:

$foo |>= bar(...) |> baz(...);
// is interpreted as:
$foo = (($foo |> bar(...)) |> baz(...));

Right-associativity with other assignment operators:

// Nested |>=: right-to-left evaluation
$foo |>= $bar |>= double(...);
// is interpreted as:
$foo |>= ($bar |>= double(...));
// 1. $bar = double($bar), expression result is the new $bar
// 2. $foo = ($foo |> new_$bar), i.e., new_$bar is used as the callable
 
// Mixed with other compound assignments
$foo += $bar |>= triple(...);
// is interpreted as:
$foo += ($bar |>= triple(...));
// 1. $bar = triple($bar), expression result is the new $bar
// 2. $foo += new_$bar
 
// |>= result used with another operator
$result = $x |>= double(...) or die('failed');
// is interpreted as:
($result = ($x |>= double(...))) or die('failed');
// because `or` has lower precedence than assignment

Error Conditions

Consistent with |>:

$x |>= fn($v) => $v * 2;      // Fatal error: must be parenthesized
$this |>= trim(...);          // Fatal error: Cannot re-assign $this
getVal() |>= strtoupper(...); // Fatal error: Can't use function return value in write context
$x |>= sort(...);             // Error: Argument #1 could not be passed by reference

Examples

In-Place Transformation

// Before
$input = trim($input) |> strtolower(...);
 
// After
$input |>= trim(...) |> strtolower(...);

Sequential Processing

When transformations are conditional, sequential |>= reads naturally:

// Before
$this->user->displayName = trim($this->user->displayName);
if ($normalize) {
    $this->user->displayName = strtolower($this->user->displayName);
}
$this->user->displayName = ucfirst($this->user->displayName);
 
// After
$this->user->displayName |>= trim(...);
if ($normalize) {
    $this->user->displayName |>= strtolower(...);
}
$this->user->displayName |>= ucfirst(...);

Long Variable Names

The value of |>= scales with variable complexity:

// Before
$this->currentOrder->lineItems = array_unique($this->currentOrder->lineItems) |> array_values(...)
 
// After
$this->currentOrder->lineItems |>= array_unique(...) |> array_values(...);

Backward Incompatible Changes

None. The token |>= is currently a syntax error in all PHP versions.

Proposed PHP Version(s)

Next PHP 8.x (8.6).

RFC Impact

To the Ecosystem

IDEs and static analyzers that support |> would need to add support for |>=. The semantics are identical to $x = $x |> ..., so type inference requires no new logic.

To Existing Extensions

The tokenizer extension gains a new T_PIPE_EQUAL constant. No other extensions are affected.

To SAPIs

No impact.

Open Issues

None.

Future Scope

  • Any future changes to |> (e.g., new callable styles) would automatically apply to |>= since they share the same callable dispatch logic in the compiler.
  • Because |>= has a dedicated AST node, the compiler knows at compile time that the variable is read and immediately overwritten. The Implicit Move Optimisation RFC proposes an opcache optimization for exactly this $x = func($x) pattern, avoiding unnecessary copy-on-write copies. |>= would benefit from that optimization automatically, and could potentially emit the implicit-move flag directly at compile time without requiring opcache's SSA analysis (an optimization the manual $x = $x |> ... form cannot support).

Voting Choices

This RFC requires a 2/3 majority to pass.

Implement the pipe assignment operator as outlined in the RFC?
Real name Yes No Abstain
alexandredaubois   
beberlei   
brzuchal   
crell   
cschneid   
dharman   
duncan3dc   
galvao   
iliaa   
imsop   
jordikroon   
lstrojny   
mbeccati   
mcmic   
ocramius   
ramsey   
sebastian   
timwolla   
weilindu   
Count: 8 8 3
This poll will close on 2026-08-11 04:30:00 UTC.

Patches and Tests

Implementation

After the RFC is implemented, this section will contain:

  1. the version(s) it was merged into
  2. a link to the git commit(s)
  3. a link to the PHP manual entry for the feature

References

Rejected Features

  • Alternative precedence: Giving |>= the same precedence as |> was considered, but $x |>= fn1(...) |> fn2(...) would parse as ($x |>= fn1(...)) |> fn2(...), where only fn1's result is assigned and fn2's result is discarded. Assignment-level precedence with compile-time chain flattening produces the expected behavior.

Changelog

  • 1.0.3 (2026-07-20): PFA examples, single-evaluation clarification, added references.
  • 1.0.2 (2026-07-14): Corrected precedence wording, added implicit move optimisation reference and future scope.
  • 1.0.1 (2026-07-13): Added associativity and precedence examples.
  • 1.0.0 (2026-07-09): Initial draft.
rfc/pipe_assignment_operator.txt · Last modified: by cdwhite3