PHP 8.4: The New Features Actually Worth Learning

Every major PHP release comes with a changelog nobody reads and a handful of features that quietly change how you write code for years. PHP 8.4 is one of those releases. I spent a weekend refactoring a mid-sized Laravel API to see which of the new features held up outside a blog post’s toy examples, and four of them are worth your time immediately.

Property hooks: the end of boilerplate getters and setters

This is the headline feature, and for good reason. Before 8.4, if you wanted a property to run logic on read or write, you wrote a private property plus a public getter and setter, or you reached for magic methods and lost IDE autocompletion. Property hooks fix this directly on the property:

class Product {
    public string $name;

    public string $slug {
        get => $this->slug ??= Str::slug($this->name);
        set (string $value) => $this->slug = strtolower(trim($value));
    }
}

On a real model in that Laravel refactor, this replaced eleven lines of a custom accessor and mutator with four lines that IDEs actually understand, autocomplete correctly, and that show up in static analysis. If you’ve ever built a “computed property” pattern with __get and __set, this is the replacement, and it’s faster because PHP isn’t falling back to magic method dispatch.

Asymmetric visibility: public reads, private writes

This one solves a problem every PHP developer has hit: you want a property publicly readable but only settable from inside the class, and the old answer was always a private property plus a public getter. Now you can just write:

class Order {
    public private(set) string $status = 'pending';

    public function markShipped(): void {
        $this->status = 'shipped';
    }
}

Anything outside the class can read $order->status, but only code inside Order can change it. No getter method needed. I replaced roughly a dozen “dumb getter, no setter” pairs in a domain model with this pattern in about twenty minutes, and the class definitions got noticeably easier to read, because the visibility rule is right there next to the property instead of buried three methods down.

New array functions that fix a real gap

PHP has had array_filter and array_map forever, but checking “does any element match” or “does every element match” always meant writing a loop or awkwardly combining array_filter with count. 8.4 adds four functions that close this gap:

  • array_find() returns the first element matching a callback, or null.
  • array_any() returns true if at least one element matches.
  • array_all() returns true if every element matches.
  • array_find_key() returns the key of the first match instead of the value.
$hasOverdueInvoice = array_any($invoices, fn($i) => $i->isOverdue());
$firstAdmin = array_find($users, fn($u) => $u->role === 'admin');

Small functions, but I replaced six or seven foreach loops with early returns across one codebase in an afternoon, and every one of them became a single readable line. This is the kind of feature that doesn’t make headlines but shows up in every pull request from here on out.

Native HTML5 parsing finally exists

If you’ve ever used DOMDocument::loadHTML() and hit garbled output on anything with modern HTML5 markup, emoji, or unusual encoding, you know the old DOM extension was built for a pre-HTML5 web. 8.4 introduces DomHTMLDocument, a proper HTML5-compliant parser:

$dom = DomHTMLDocument::createFromString($html, LIBXML_NOERROR);
foreach ($dom->querySelectorAll('.price') as $node) {
    echo $node->textContent;
}

Notice querySelectorAll is a native method now, no more pulling in a separate CSS-selector library just to avoid XPath. For anyone doing scraping, feed parsing, or server-side HTML processing, this alone is worth upgrading for. I rebuilt a price-scraping script that used to choke on non-breaking spaces and malformed tags, and the new parser handled the same real-world HTML without a single workaround.

What I’d actually prioritize

If you’re upgrading an existing codebase, don’t try to adopt everything at once. Property hooks and asymmetric visibility are worth using in new classes immediately, but retrofitting them into a large existing codebase is a refactor, not a find-and-replace, budget real time for it. The new array functions are safe to adopt anywhere, today, with zero risk, they’re pure additions. The HTML parser is worth adopting the moment you touch any code that parses external HTML, since the old DOMDocument behavior around encoding has caused enough production bugs that I don’t recommend leaving it in place once 8.4 is available to you.

The takeaway

PHP 8.4 isn’t a flashy release, but it’s a genuinely useful one. Property hooks and asymmetric visibility cut real boilerplate out of domain models, the new array functions replace loops you’re writing every week, and the HTML5 parser fixes a decade-old pain point without a third-party library. None of these require a rewrite. Pick one class you’re already touching this sprint, apply property hooks to it, and you’ll feel the difference immediately.

Faizan Khan
Faizan Khan

Technical PM & PHP developer — the manager who still ships code. 13 years turning “can we build this?” into “it’s live”.

Work with me →

Enough talk. Let’s launch.

One call. An honest scope, a real timeline, and weekly updates until it ships.