Skip to main content
Back to writing

PHP 8.4 released

Krodox Team November 21st, 2024 PHP

PHP 8.4 is the version most Laravel applications are running today, and it is the one that finally removed a great deal of getter-and-setter boilerplate from PHP. Here are the features that matter in practice, with examples.

Key features in PHP 8.4

1. Property hooks

The headline addition. Properties can now intercept their own reads and writes, without the class needing an explicit getter and setter pair.

class Locale
{
    public string $countryCode {
        set (string $countryCode) {
            $this->countryCode = strtoupper($countryCode);
        }
    }

    public string $combinedCode {
        get => sprintf('%s_%s', $this->languageCode, $this->countryCode);
        set (string $value) {
            [$this->languageCode, $this->countryCode] = explode('_', $value, 2);
        }
    }
}

The second property is computed on read and decomposed on write, and callers just treat it as a property. A large amount of accessor code exists purely to normalise a value on the way in; this deletes most of it.

2. Asymmetric visibility

Read and write access can now differ, which expresses "public to read, private to change" without writing a getter for it.

class PhpVersion
{
    public private(set) string $version = '8.4';
}

This pairs naturally with property hooks: hooks control how a property is read or written, asymmetric visibility controls who may do it.

3. The #[\Deprecated] attribute

Deprecation stops being a docblock convention and becomes something the engine and your tooling understand.

#[\Deprecated(
    message: 'use PhpVersion::getVersion() instead',
    since: '8.4',
)]
public function getVersion(): string
{
    // ...
}

4. A modern DOM API with HTML5 support

The old DOM extension predates HTML5 and showed it. PHP 8.4 adds a new API that parses HTML5 correctly and supports CSS selectors:

$dom = Dom\HTMLDocument::createFromString($html, LIBXML_NOERROR);

$node = $dom->querySelector('main > article:last-child');

var_dump($node->classList->contains('featured'));

If you have ever reached for a scraping library purely because DOMDocument mangled modern markup, this is the fix.

5. New array functions

array_find(), array_find_key(), array_any() and array_all() cover the four things everyone previously wrote a foreach for.

$featured = array_find($articles, fn ($article) => $article->featured);

if (array_any($articles, fn ($article) => $article->draft)) {
    // ...
}

6. BCMath object API

Arbitrary-precision numbers become real objects with operator support, rather than strings passed through bcadd() and friends.

use BcMath\Number;

$total = new Number('0.12345') + new Number('2');

7. Smaller quality-of-life changes

Method chaining no longer requires wrapping parentheses around new:

var_dump(new PhpVersion()->getVersion());

PDO also gained driver-specific subclasses — Pdo\Sqlite, Pdo\MySql, Pdo\Pgsql — so driver-specific behaviour is expressed in the type system rather than in documentation.

Why upgrade

  1. Less boilerplate. Property hooks and asymmetric visibility remove a category of code that existed only to guard a property.
  2. Better tooling signals. #[\Deprecated] is machine-readable, so static analysis can act on it.
  3. Fewer workarounds. The new DOM API and the array functions replace common third-party dependencies.
  4. Support runway. Staying current is what keeps future upgrades cheap.

Where it fits now

PHP 8.4 remains a sensible target. Laravel 13 supports PHP 8.3 through 8.5, so 8.4 sits comfortably in the middle of the supported range, and it is what this site runs.

If you are planning further ahead, PHP 8.5 is out and adds the pipe operator, clone with, and a built-in URI parser.

As always, test before upgrading rather than after.