PHP 8.4 released
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
- Less boilerplate. Property hooks and asymmetric visibility remove a category of code that existed only to guard a property.
- Better tooling signals.
#[\Deprecated]is machine-readable, so static analysis can act on it. - Fewer workarounds. The new DOM API and the array functions replace common third-party dependencies.
- 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.