0% прочитано

PHP 8.5 Pipe Operator in Real Laravel Code: Where |> Actually Improves Readability

While building the IndexNow integration in Laravel, I used PHP 8.5's Pipe Operator to clean and normalize a URL list through several transformations. Using real production code, I compare |> with nested function calls and show where the pipeline syntax improves readability and where I would avoid it.

7 вересня 2026 р. 8 хв читанняPHP

While working on IndexNow in Laravel, I needed to clean a list of URLs before submitting it: remove empty values, deduplicate the list, and restore sequential numeric keys.

It was a small, ordinary transformation pipeline, but it became the first place where I used PHP 8.5's Pipe Operator |> as part of real production code rather than as a demo of new syntax.

The result reads in the same direction as the data changes: filter → unique → values.

The short version: `|>` works best for me when the result of one small transformation naturally becomes the input of the next. It does not make older PHP code wrong, it simply lets me read the sequence left to right instead of unpacking nested calls from the inside out.

By that point, ICanUp was already running on PHP 8.5, so I could evaluate the new syntax while building the application instead of experimenting with it in an isolated example.

The Real Use Case Appeared in My IndexNow Client

In my IndexNow implementation for automatically submitting URLs to Bing, the HTTP client receives an array of public URLs and builds the urlList sent to the external service.

Before making that request, I need to guarantee that the list contains no empty values or duplicates and that its indexes are sequential.

This was not a utility invented to demonstrate the Pipe Operator. The cleanup was already part of the IndexNow contract.

PHP - IndexNow (cleaning URLs with PHP 8.5 Pipe Operator)
$urls = array_filter(        $urls,        static fn(mixed $url): bool => is_string($url) && $url !== '',    )        |> array_unique(...)        |> array_values(...);

What I like about this fragment is that it barely needs to be translated into prose: filter the URLs, keep unique values, then reindex the array.

  • array_filter() removes everything that is not a non-empty string

  • array_unique() removes duplicate URLs

  • array_values() restores sequential numeric keys

  • The result is a clean list ready for the urlList JSON payload

What `|>` Actually Does in PHP 8.5

The Pipe Operator takes the result of the expression on its left and passes it to the callable on its right.

I can read the right-hand side as a function that PHP invokes with the previous result.

A simple pipe therefore does not create a new collection type or a special pipeline object. It is an expression provided by the language itself.

PHP - The simplest Pipe Operator equivalence
$result = $value |> transform(...); $result = transform($value);
`array_unique(...)` does not mean argument unpacking here. It is first-class callable syntax: PHP obtains a callable for `array_unique()`, and the Pipe Operator passes the previous expression result into it.

What the Same Code Looks Like Without the Pipe Operator

The same operation can be written with ordinary nested function calls, and it behaves exactly the same way.

PHP - The same transformation with nested calls
$urls = array_values(    array_unique(        array_filter(            $urls,            static fn(mixed $url): bool => is_string($url) && $url !== '',        ),    ),);

There is nothing technically wrong with this version. The difference is the reading order.

I first have to find array_filter(), then move outward to array_unique(), and finally reach the outer array_values() call.

As the number of transformations grows, nesting starts to hide the actual sequence of operations.

Temporary Variables Are Still a Good Alternative

PHP - The transformation with sequential assignments
$urls = array_filter(    $urls,    static fn(mixed $url): bool => is_string($url) && $url !== '',); $urls = array_unique($urls);$urls = array_values($urls);

I do not consider this version worse either.

If I need to place a breakpoint between stages, log an intermediate value, or give each state a meaningful name, temporary variables may be clearer than a pipe.

For me, the question is not whether code can be rewritten with |>. The question is whether doing so makes the intent easier to read.

I use the Pipe Operator not to eliminate variables, but to make a sequence of transformations read in its natural direction.

Why This IndexNow Fragment Fits `|>` So Well

  • Each step is small and easy to understand

  • The result of one step naturally becomes the input of the next

  • The steps do not have separate business meanings that need their own names

  • I do not need the intermediate values after the pipeline finishes

  • The filter → unique → values sequence matters more than function-call nesting

  • The final result remains an ordinary PHP array

There is another detail in this example that makes the order of transformations important.

array_filter() preserves keys. array_unique() can also leave gaps after duplicate values are removed.

After filtering and deduplication, the array indexes are therefore not guaranteed to be 0, 1, 2, ...

How the keys change while cleaning URLs
Input: 0 => https://example.com/a1 => ''2 => https://example.com/a3 => https://example.com/b  After array_filter(): 0 => https://example.com/a2 => https://example.com/a3 => https://example.com/b  After array_unique(): 0 => https://example.com/a3 => https://example.com/b  After array_values(): 0 => https://example.com/a1 => https://example.com/b

For IndexNow, this is not just about making an array dump look nicer.

The array is later passed to Laravel's HTTP client as urlList in a JSON payload. In PHP, an array with non-sequential numeric keys can be encoded as a JSON object with explicit keys instead of a JSON list.

The final array_values() therefore guarantees the data shape I want to send to the external API.

In this pipeline, `array_values()` is not cosmetic formatting. After `array_filter()` and `array_unique()`, it restores sequential keys so that `urlList` remains a JSON array.

Why `array_filter()` Stays Before the Pipe

At first glance, it may seem cleaner to start the entire chain with $urls |> ....

But my array_filter() call needs both the input array and a callback containing the validation rule.

The simplest version was to keep that first call as a normal expression and pipe its result into the one-argument callable stages.

PHP - Starting the entire flow with a pipe
$urls = $urls    |> (static fn(array $urls): array => array_filter(        $urls,        static fn(mixed $url): bool => is_string($url) && $url !== '',    ))    |> array_unique(...)    |> array_values(...);

This version also expresses a pipeline, but it introduces a wrapper closure just for the first step. In my case, starting with a normal `array_filter()` call was simpler.

My Second Real Example Was Even Smaller

Another natural place for `|>` appeared while extracting a slug candidate from a URL path.

PHP - URL path → slug with the Pipe Operator
private function pageSlugCandidate(string $url): ?string{    if ($this->isExternalUrl($url)) {        return null;    }     $path = parse_url($url, PHP_URL_PATH);     if (! is_string($path)) {        return null;    }     $slug = rtrim($path, '/')        |> basename(...)        |> rawurldecode(...);     return $slug !== '' ? $slug : null;}

Here, the pipeline reads almost literally as a transformation path.

First I remove the trailing slash, then take the basename, and finally decode the resulting slug.

Short, pure transformations like this are where the new syntax feels most natural to me.

Where I Would Not Use the Pipe Operator

  • When each step has important domain meaning and deserves a separate name

  • When I need to inspect or log intermediate values

  • When most stages require wrapper closures just to adapt their signatures

  • When the chain becomes so long that several assignments would be easier to scan

  • When transformations have side effects and it is not obvious what value continues through the chain

  • When |> is being added only because it is a new PHP 8.5 feature

PHP - An example I would not over-pipe
$result = $input    |> (fn($value) => transform($value, $config, $locale))    |> (fn($value) => validateValue($value, $rules, $context))    |> (fn($value) => formatOutput($value, $options, $timezone));

If almost every line turns into a closure with additional dependencies, I am no longer getting the simplicity that made the pipeline attractive in the first place.

Before replacing nested calls with `|>`, I look at reading direction rather than character count. If the data flow becomes easier to understand after the refactor, the pipe has earned its place.

There Is One Obvious Boundary: the Runtime Must Be PHP 8.5

The Pipe Operator is PHP 8.5 syntax, so upgrading only my local PHP installation would not be enough.

CLI, PHP-FPM, CI runners, Docker workspaces, Composer platform requirements, and the production runtime all need to agree before |> enters the codebase.

Otherwise, readability is no longer the problem - the older interpreter simply cannot parse the new syntax.

JSON - Composer requirement for a PHP 8.5 project
{    "require": {        "php": "^8.5"    }}

I covered the runtime migration itself separately in my article about the PHP 8.5, Laravel, Laradock, and Composer package upgrade.

`|>` and Laravel Pipeline Are Not the Same Thing

The names are similar, but I use these approaches at very different levels.

PHP's Pipe Operator is a small language-level expression for passing one value through callables.

Laravel Pipeline is useful for a more structured application flow where stages can be separate classes with their own responsibilities. I have already used that approach in a real commission calculation flow built with Laravel Pipeline.

Creating a Laravel Pipeline for three array functions would be excessive. At the other extreme, one long |> chain does not replace architecture for a complex domain flow.

Approach

When I use it

Main advantage

Nested functions

A short, simple expression

Minimal syntax

Temporary variables

Intermediate states or debugging matter

Maximum explicitness

PHP |>

Small sequential transformations

Left-to-right readability

Laravel Pipeline

Multiple application/domain stages

Structure and separate responsibilities

After the Refactor, I Verify Behavior Rather Than Syntax

  1. Non-string values never reach the final URL list.

  2. Empty strings are removed.

  3. Duplicate URLs are removed.

  4. Keys are sequential again after filtering and deduplication.

  5. An empty result does not trigger an HTTP request.

  6. The final urlList remains a JSON array.

  7. The IndexNow HTTP payload keeps the same functional behavior after the syntax refactor.

For me, a successful `|>` refactor is one where tests confirm the same behavior and the change is mainly about making the code flow easier to read.

What I Kept for Next Time

  • |> works best for small sequential transformations

  • Not every nested call needs to be rewritten

  • Temporary variables remain a good solution, especially for debugging

  • A wrapper closure is not worth adding just to make everything look like one pipe chain

  • I need to think about data shape as well as data value between stages—as the final array_values() in the JSON payload demonstrates

  • New PHP syntax should enter the codebase only after the entire runtime is aligned, not just the local environment

Conclusion

I like PHP 8.5's Pipe Operator not because it lets me write the same code with newer syntax.

In the right place, |> changes the direction in which I read the code. Instead of unpacking nested functions from the inside out, I see the data flow in the same order in which it actually executes.

My IndexNow URL cleanup is exactly that kind of case: filter → unique → values. The second rtrim → basename → rawurldecode example confirmed the same pattern.

I do not plan to turn every PHP function chain into a pipe. But for short transformation pipelines without unnecessary side effects, |> has already become a normal tool for me rather than just a PHP 8.5 novelty.

A good Pipe Operator does not make me think about the Pipe Operator. It simply lets me read the transformation in the right direction.