Dan Newns.

This post was originally published on the Jump24 blog, where I serve as Technical Director. I've syndicated it here because I wrote it, I'm proud of the work, and I want my personal site to reflect the full scope of what I'm building - both professionally and personally. If you're here for Laravel insights, you'll find them alongside golf breakdowns and life updates. If you found this helpful, you might enjoy Jump24's other content too.

Right, I'm going to be honest with you - when I first heard about PHP 8.5's pipe operator, my initial reaction was "oh great, another operator to remember." and I can't see me needing to use this. But after spending some time playing with it I've completely changed my tune. This little |> operator might just be the best quality-of-life improvement PHP has seen since constructor property promotion.

And here's the thing - this isn't some newfangled concept. The pipe operator has been around since the 1960s when Doug McIlroy introduced it to Unix systems. Languages like F#, Elixir, and OCaml have had it for years. We're not breaking new ground here; we're finally catching up. And that's a good thing.

Let me show you why our team at Jump24 is genuinely excited about this one.

The Problem We've All Faced

You know that feeling when you're writing PHP and you end up with code that looks like this?

php
1<?php
2 
3// The nested nightmare we've all written
4$result = array_values(
5 array_unique(
6 array_filter(
7 array_map(
8 'strtolower',
9 explode(' ', trim($input))
10 ),
11 fn($word) => strlen($word) > 3
12 )
13 )
14);

Yeah, we've all been there. Reading that code is like reading a book backwards - you have to start from the innermost function and work your way out. It's the PHP equivalent of those Russian nesting dolls, except less charming and more likely to give you a headache during code review.

Or maybe you've done something simpler but equally frustrating:

php
1<?php
2 
3// Creating a URL-friendly slug
4$slug = strtolower(
5 str_replace(' ', '-',
6 preg_replace('/[^a-z0-9\s]/i', '',
7 trim($title)
8 )
9 )
10);

The traditional "solution" for readability has been to use temporary variables:

php
1<?php
2 
3$trimmed = trim($title);
4$cleaned = preg_replace('/[^a-z0-9\s]/i', '', $trimmed);
5$hyphenated = str_replace(' ', '-', $cleaned);
6$slug = strtolower($hyphenated);

Better for readability? Sure. But now we've got four intermediate variables cluttering up our namespace, and honestly, coming up with meaningful names for each step gets old fast.

Enter the Pipe Operator

PHP 8.5 introduces the pipe operator (|>), and it's about to change how you think about data transformation. Here's that slug generation with pipes:

php
1<?php
2 
3$title = 'Hello World';
4 
5$slug = $title
6 |> trim(...)
7 |> (fn($title) => preg_replace('/[^a-z0-9\s]/i', '', $title))
8 |> (fn($title) => str_replace(' ', '-', $title))
9 |> strtolower(...);

Look at that flow! You can read it top to bottom, left to right, just like you'd explain the process to someone: "First we trim the title, then we remove special characters, then we replace spaces with hyphens, then we lowercase everything."

The ... syntax you're seeing is PHP's first-class callable syntax (introduced in 8.1), which works perfectly with pipes. The value from the left side gets passed as the first argument to the function on the right.

Real-World Laravel Examples

Let's get practical. Here at Jump24, we're all about Laravel, so let me show you some real examples from our projects.

Processing CSV Data for Import

We process a lot of CSV imports for our clients. Here's how pipes have cleaned up that code:

php
1<?php
2 
3public function importProducts(array $csvRows): Collection
4{
5 return $csvRows
6 |> (fn($rows) => array_map($this->normalizeRow(...), $rows))
7 |> (fn($rows) => array_filter($rows, $this->isValidProduct(...)))
8 |> (fn($rows) => array_map(Product::fromArray(...), $rows))
9 |> collect(...)
10 |> (fn($collection) => $collection->unique('sku'))
11 |> (fn($collection) => $collection->values());
12}
13 
14private function normalizeRow(array $row): array
15{
16 return [
17 'name' => trim($row['name'] ?? ''),
18 'price' => (float) ($row['price'] ?? 0),
19 'sku' => strtoupper(trim($row['sku'] ?? '')),
20 ];
21}
22 
23private function isValidProduct(array $row): bool
24{
25 return $row['price'] > 0 && !empty($row['sku']);
26}

Working with Match Expressions

Remember our post about PHP match expressions and enums? The pipe operator plays beautifully with match:

php
1<?php
2 
3public function formatContent(string $content, ContentFormat $format): string
4{
5 return match($format) {
6 ContentFormat::Markdown => $content
7 |> $this->parseMarkdown(...)
8 |> $this->sanitizeHtml(...)
9 |> $this->addTableOfContents(...),
10 
11 ContentFormat::PlainText => $content
12 |> strip_tags(...)
13 |> nl2br(...)
14 |> $this->linkifyUrls(...),
15 
16 ContentFormat::Html => $content
17 |> $this->sanitizeHtml(...)
18 |> $this->processShortcodes(...)
19 |> $this->lazyLoadImages(...),
20 };
21}

The Testing Benefits

One unexpected benefit we've discovered? Testing becomes easier. When you structure your code with pipes, you naturally end up with smaller, more focused functions:

php
1<?php
2 
3// Before: One big method that's hard to test
4public function processOrder(Order $order): Invoice
5{
6 // 50 lines of nested transformations and business logic
7}
8 
9// After: Smaller, testable pieces
10public function processOrder(Order $order): Invoice
11{
12 return $order
13 |> $this->validateOrder(...)
14 |> $this->calculateTotals(...)
15 |> $this->applyDiscounts(...)
16 |> $this->addTaxes(...)
17 |> $this->generateInvoice(...);
18}

Now we can unit test each step independently.

Type Safety Considerations

The pipe operator respects PHP's type system. When you have strict_types enabled, it enforces type checking at each step:

php
1<?php
2 
3declare(strict_types=1);
4 
5// This will throw a TypeError
6$result = 42
7 |> strlen(...); // TypeError: strlen() expects string, int given
8 
9// This works fine - type coercion in non-strict mode
10$result = 42
11 |> strval(...) // Convert to string first
12 |> strlen(...); // Now it works

This is actually great - it forces us to be explicit about type conversions, making our code more predictable.

The Reality Check

Now, let's be honest about the limitations, because you know we don't sugarcoat things here at Jump24.

Limited to First Parameter

The pipe operator only works cleanly with single-parameter functions or when the piped value should be the first parameter:

php
1<?php
2 
3// This is awkward
4$result = $data
5 |> (fn($x) => array_combine($keys, $x)) // Need value as second param
6 |> (fn($x) => array_merge($defaults, $x)); // Need value as second param

IDE Support Is Getting There

PHPStorm is catching up, but code completion and type inference in pipe chains isn't perfect yet. Though to be fair, JetBrains is usually pretty quick with updates.

Our Migration Strategy

We're not going crazy refactoring everything to use pipes. Here's our approach for when 8.5 is released:

  1. New code: Use pipes where they improve readability

  2. Refactoring: When touching code with deeply nested functions, consider pipes

  3. Hot paths: Leave them alone unless there's a clear benefit

  4. Code reviews: If someone can't understand it, we discuss whether pipes would help

  5. Team education: Everyone needs to understand them, not everyone needs to use them

How to Try It Now

PHP 8.5 releases on November 20, 2025, but you can try it today:

php
1# Using Docker
2docker run -it php:8.5-rc-cli php -a
3 
4# On macOS with Homebrew
5brew tap shivammathur/php
6brew install shivammathur/php/php@8.5-dev
7 
8# In your composer.json for testing
9"require": {
10 "php": "^8.5"
11}

We're running the release candidate in our development environment (not production - we're not that brave), and it's been stable for our use cases.

The Bottom Line

After three weeks of using PHP 8.5's pipe operator in our development environment, I can confidently say it's improved our code quality. Not revolutionised, not transformed, but genuinely improved. Our code is more readable, our junior developers are picking things up faster, and we're naturally writing more testable functions.

Is it going to change the way you write PHP overnight? Probably not. But for those times when you're staring at five levels of nested function calls wondering if there's a better way - well, now there is.

The pipe operator lands with PHP 8.5 on November 20, 2025. If you're like us and can't wait, grab the release candidate and start experimenting. Your future self (and your code reviewers) will thank you.

Your Turn

What's the gnarliest nested function call in your codebase right now? Share it with us - we'd love to see how the pipe operator could clean it up. Drop us a line or share your examples in the comments.

And if you're dealing with complex data transformations in your Laravel applications and want to explore how modern PHP features like this can clean up your codebase, get in touch. We've been refactoring Laravel applications for over 11 years, and we love showing teams how the latest PHP features can solve real-world problems.

Want to stay updated on PHP 8.5 features? Follow us for more deep dives into what's coming and how to use it effectively in your Laravel projects.

12th November 2025
Right, let's talk about a bug pattern we've all created at least once, it's not a pattern that breaks anything as such is more a pattern of we've forgotten to do something.
Read more
5th November 2025
You know that feeling when you need to fetch data from multiple APIs, and you're sitting there watching your application make request... after request... after request... while your users are staring at a loading spinner that's been going for so long it's practically become a screensaver?
Read more
28th October 2025
Hands up — how many of you have a Laravel 9 or 10 application that should be upgraded but the idea of refactoring thousands of lines makes you want to switch careers? We’ve been there. More than once.
Read more
22nd October 2025
PHP 8.5's new URI extension finally fixes 20 years of parse_url() problems.
Read more
18th October 2025
Laravel 12.34 dropped earlier this week with a feature that made me actually say "oh thank god" out loud: automatic queue failover.
Read more
15th October 2025
Right, let's talk about something that's been bugging us for years. If you've been writing PHP for any length of time, you've probably written some variation of "get the first item from this array" more times than you'd care to admit. And if you're a Laravel developer like us, you've probably been spoilt by the Arr::first() and Arr::last() helpers that make this dead simple.
Read more
13th October 2025
Right, I'm going to be honest with you - when I first heard about PHP 8.5's pipe operator, my initial reaction was "oh great, another operator to remember." and I can't see me needing to use this. But after spending some time playing with it I've completely changed my tune. This little |> operator might just be the best quality-of-life improvement PHP has seen since constructor property promotion.
Read more
8th October 2025
I'll be honest with you - we've been using Laravel for over a decade, and it's only the last year or so that we've properly discovered the Pipeline Class. I know, I know, it's been sitting there in the documentation this whole time, but somehow we just never gave it the attention it deserved.
Read more
1st October 2025
I've been thinking a lot lately about what makes Laravel special. Sure, the framework is brilliant. But if I'm being completely honest, it's not the code that keeps us coming back year after year.
Read more
17th September 2025
As a Laravel development team thats been running for 11 years , we've always had a bit of a love-hate relationship with content management systems. For the longest time, WordPress was our go-to solution whenever a project required CMS functionality. It was quick, familiar, and with plugins like Advanced Custom Fields, we could get clients up and running with a content management system that met their needs. But deep down, something never felt quite right about this approach.
Read more
11th September 2025
This week marks 11 years since we started Jump24, and if I'm being completely honest, I never thought we'd still be here. Not because I didn't believe in what we were doing, but because I genuinely had no idea what "here" would look like after more than a decade.
Read more
21st February 2025
I headed to my first Ever PHPUK earlier this week, We were there as an event sponsor. I just thought I would get down some thoughts on the event
Read more
7th February 2025
Its the third week of my top ten things I have found this week in the Laravel and PHP world series.
Read more
6th February 2025
So it’s over that’s it for Laracon EU 2025, it was a great event! The location itself was really nice it’s the first time I’d been to that venue so it was great to experience it for the first time.
Read more
31st January 2025
The second week of top ten things I have found this week in the Laravel and PHP world.
Read more
30th January 2025
Yesterday I was lucky enough to be invited down to London for the day to celebrate the Cactus Rebrand Party.
Read more
24th January 2025
Every Week im going to be putting together a top ten of the best laravel and PHP things I find that week. I've been doing this for a while now on LInkedin but I thought it would be worth documenting things on my own blog
Read more
13th January 2025
Over the years there has been plenty of debates around coding standards. People have argued over things like tabs vs spaces, line lengths and other aspects when it comes to writing code.
Read more
13th January 2025
As PHP developers, we all want to write clean, maintainable code that doesn’t break in production. But with larger projects, keeping track of potential issues and bugs can become increasingly difficult.
Read more