This is an old revision of the document!

PHP RFC: Pipe Operator


Complex nested expressions can become progressively difficult to read the deeper they go with function returns leading into arguments to functions which return an argument to another call and so on and so forth. This RFC aims to improve code readability by decomposing complex nested expressions into a series of linearly readable chained statements.


Introduce the “Pipe Operator” `|>`, a binary expression which uses the result of the LHS as an input to the RHS expression at an arbitrary point denoted by the additional “Pipe Replacement Variable” expression `$$`. The result of the RHS expression, after substitution, is used as the result of the operator.

This feature is being culled from HackLang 3.13 and the manual page for it may be referenced at: https://docs.hhvm.com/hack/operators/pipe-operator

PSR7 Example

Here's the pseudo-code for a typical request/response dispatch cycle:

  $request = getGlobals()
   |> parseRequest($$)
   |> buildPsr7Request($$);
  $response = loadConfig()
   |> buildDic($$)
   |> getApp($$)
   |> getRouter($$)
   |> getDispatcher($$, $request)
   |> dispatchBusinessLogic($$, $request, new Response())
   |> renderResponse($$)
   |> buildPsr7Response($$)
   |> emit($$);

Here's what it would look like with a separate approach (more business-oriented):

  buildRequest() // (basically the first part of the previous example here)
   |> validate($$)
   |> convertToCommand($$)
   |> execute($$)
   |> convertToViewModel($$)
   |> render($$)
   |> convertToHttpResponse($$)
   |> emit($$);

File collection Example

As an example, consider the following real block of code I wrote while creating a test importer (to migrate HHVM format tests into PHPT format):

$ret = 
        function ($x) use ($arg) { return $arg . '/' . $x; },
          function ($x) { return $x !== '.' && $x !== '..'); }

This block of code is readable, but one must carefully examine the nesting to determine what the initial input it, and what order it traverses the steps involved.

With this proposal, the above could be rewritten as:

$ret = scandir($arg)
    |> array_filter($$, function($x) { return $x !== '.' && $x != '..'; })
    |> array_map(function ($x) use ($arg) { return $arg . '/' . $x; }, $$)
    |> getFileArg($$)
    |> array_merge($ret, $$);

This clearly, and unambiguously shows `scandir()` as the initial source of data, that it goes through an `array_filter` to avoid recursion, an `array_map` to requalify the paths, some local function, and finally a merge to combine the result with a collector variable.

Backward Incompatible Changes

While most ambiguities of `$$` between pipe replacement variable and variable variables are covered in the lexer rule, the following case is not accounted for:

$a = 1;
$b = 'a';
var_dump($$ /* comment */ {'b'});
// Expected: int(1)
// Actual: Use of $$ outside of a pipe expression

This particular quirk of the parser (allowing comments in the middle of a variable-variable-brace-expression) is doubtlessly a rare occurance in the wild, so the current implementation stopped short of trying to resolve it.

Potential resolutions:

  • Use a less-ambiguous token. `$>` which mirrors `|>` is my personal favorite. Downshot: doesn't match HackLang
  • Get very creative in the parser. Since '$' '{' expr '}' is handled by the parser, then perhaps '$' '$' should as well. So far, attempts to resolve this result in conflicts. More work may yet yield results.

Note that HHVM does not handle this case either. Nor, in fact, does it handle mere whitespace between `$$` and `{expr}`, which the attached PHP implementation does.

Update: HackLang is normally supposed to disallow variable-variables, so the use of `$$` was seen as non-conflicting. A bug in the 3.13 implementation of pipe operator meant that variable-variables temporarily wound up working where they should not have. So whatever we propose for Pipe Operator's substitution will face the same issues in both lexers eventually anyhow.

Proposed PHP Version(s)


Open Issues

See BC issues

Future Scope

The current proposal limits use of the `$$` to a single replacement per expression. This feature could potentially be expanded to allow multiple uses of `$$` within a single RHS expression.

Third-party Arguments

In favor

  • Produces cleaner, more readable code
  • Doesn't pollute local symbol table with intermediates of potentially varying types


  • The new tokens are inobvious and difficult to google for
    • Pipe chaining in other languages follows different rules
      (e.g. implicit first arg, rather than explicit placeholder)
    • Potentially confusable with variable-variables
  • No opportunity for error catching/handling
  • Can be implemented using intermediate variables

Proposed Voting Choices

Adopt the PIpe Operator yes/no? Requires a 2/3 + 1 majority.

Patches and Tests

rfc/pipe-operator.1463075053.txt.gz · Last modified: 2017/09/22 13:28 (external edit)