Table of Contents

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

Voting Choices

This RFC requires a 2/3 majority to pass.

Accept the pipe assignment operator?
Real name Yes No Abstain
Final result: 0 0 0
This poll has been closed.

Patches and Tests

Implementation: https://github.com/php/php-src/pull/22633

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

Changelog