The cursor blinked mockingly on a fresh line of code, a digital embodiment of the unfulfilled potential lurking within a freshly updated PHP version.
So, Laravel 13 showed up, and if you bothered to follow the upgrade path — and honestly, who has the energy anymore? — you’re now ostensibly running PHP 8.4. The upgrade guide likely told you to do it, maybe even mentioned a few Symfony components that demand it. Fine. But does hitting ‘upgrade runtime’ equate to actually using the shiny new toys PHP 8.4 decided to bestow upon us?
Spoiler alert: absolutely not. Most of us. Myself included, probably. We update, we run the tests (praying they don’t spontaneously combust), and then we go right back to writing PHP 8.1 code. It works. It’s familiar. But it’s also lazy. And it’s leaving real, tangible improvements on the table. PHP 8.4 shipped with six features that could actually make writing code for your Laravel app less of a chore. Let’s talk about them.
Property Hooks: Less Boilerplate, More Sanity
Forget those redundant getter and setter methods for simple property manipulations. Property hooks let you define get and set behavior directly on a class property. This is like the universe finally realizing that private float $priceInCents; public function getPriceInCents() { return $this->priceInCents; } is just… sad.
Before (PHP 8.3):
class PriceCalculator
{
private float $priceInCents;
public function getPriceInCents(): float
{
return $this->priceInCents;
}
public function setPriceInCents(float $value): void
{
if ($value < 0) {
throw new \InvalidArgumentException('Price cannot be negative.');
}
$this->priceInCents = $value;
}
public function getPriceInDollars(): float
{
return $this->priceInCents / 100;
}
}
After (PHP 8.4):
class PriceCalculator
{
public float $priceInCents {
set {
if ($value < 0) {
throw new \InvalidArgumentException('Price cannot be negative.');
}
$this->priceInCents = $value;
}
}
public float $priceInDollars {
get => $this->priceInCents / 100;
}
}
The $priceInDollars property is now purely virtual. It doesn’t store a thing; it just calculates on the fly. The set hook on $priceInCents now handles validation without requiring a dedicated, ceremony-laden method. This is particularly useful in service classes, value objects, and DTOs where you’d typically find yourself writing verbose transformation logic. Naturally, Eloquent models have their own accessor/mutator system, so this isn’t a direct replacement there. But for everything else in your app — and trust me, there’s plenty — this feature scrubs away a mountain of boilerplate.
Asymmetric Visibility: Because Read-Only Isn’t Always Enough
Ever needed a property that anyone can read, but only the class itself can touch? Before PHP 8.4, you were stuck with making it private and adding a getter, or using readonly. The latter is fine for once-set values, but what if the value needs to change? It’s a common enough scenario that deserves a cleaner solution.
Before (PHP 8.3):
class OrderStatus
{
private string $status = 'pending';
public function getStatus(): string
{
return $this->status;
}
public function markAsShipped(): void
{
$this->status = 'shipped';
}
}
// Usage
$order->getStatus(); // 'pending'
After (PHP 8.4):
class OrderStatus
{
public private(set) string $status = 'pending';
public function markAsShipped(): void
{
$this->status = 'shipped';
}
}
// Usage
$order->status; // 'pending' - direct access, no getter needed
$order->status = 'cancelled'; // Error: Cannot modify private(set) property
The public private(set) declaration is elegant. External code can read $status directly. But changing it? Nope. Only the class itself can modify it. This bypasses the readonly restriction, allowing for internal changes via methods like markAsShipped(), while keeping external meddling at bay. This is tailor-made for DTOs in your Laravel app – think API response objects, configuration settings, form data structures. Anywhere you want clean, direct reading access without granting the keys to the kingdom.
And yes, you can also use public protected(set) if you want to allow child classes to fiddle with the property while still keeping the riff-raff out.
array_find() and Pals: Finding Needles in Haystacks, Finally
PHP’s array_filter() has been around since the dawn of time, but if you just needed the first element that met a condition, you were stuck writing verbose, multi-step operations. It felt like using a bulldozer to pick up a single grain of rice.
Before (PHP 8.3):
$users = [
['name' => 'Alice', 'role' => 'admin'],
['name' => 'Bob', 'role' => 'editor'],
['name' => 'Charlie', 'role' => 'admin'],
];
$firstAdmin = array_values(array_filter(\n $users,\n fn ($user) => $user['role'] === 'admin'
))[0] ?? null;
This monstrosity runs the whole array through array_filter, then array_values to re-index, and then you pick the first element, or null if nothing was found. Three functions for what should be a single, focused operation.
After (PHP 8.4):
$firstAdmin = array_find($users, fn ($user) => $user['role'] === 'admin');
Boom. array_find() finds the first matching element and stops. No unnecessary processing, no re-indexing. If nothing matches, it returns null. Simple, direct, and finally, sensible. It’s joined by its siblings: array_find_key() (which returns the key instead of the value), array_any() (checks if any element matches, akin to Collection::contains() for arrays), and array_all() (checks if every element matches, similar to Collection::every()). These are small wins, but they add up to cleaner, more readable code.
Why Does This Matter for Laravel Developers?
Look, nobody wants to rewrite their codebase just because a new PHP version dropped. But when Laravel 13 forces your hand to PHP 8.4, it’s an opportune moment to take stock. These aren’t obscure, edge-case features. Property hooks and asymmetric visibility directly tackle common PHP annoyances that plague app development. array_find() and its kin simplify everyday array manipulation. Ignoring them means continuing to write more code than necessary, code that’s harder to read and maintain. It’s like showing up to a coding contest with a calculator when everyone else has a supercomputer. It’s not just inefficient; it’s a choice.
My one unique insight here? This is less about PHP evolving and more about PHP finally catching up to common patterns developers have been faking for years with boilerplate. The language is maturing, and these additions are less ‘revolutionary’ and more ‘sensible’. The real criticism lies with the frameworks and developers who are slow to adopt these fundamental improvements. They’re holding back progress.
So, Are You Actually Using Them?
It’s easy to nod along, agree these features are neat, and then go back to your old habits. The real test for any developer is integrating these tools into their daily workflow. Do you reach for property hooks when defining a new DTO? Do you use array_find instead of the old array_filter dance? If the answer is no, you’re not really on PHP 8.4. You’re just running a newer version of PHP 8.1. And that’s a shame.
**
🧬 Related Insights
- Read more: Rust 1.97: New CUDA Target Baseline in 2026 [Analysis]
- Read more: Linux Kernel 7.0-rc7 Drops: Your Devices Get Battle-Hardened Stability
Frequently Asked Questions**
What are property hooks in PHP 8.4?
Property hooks allow you to define custom get and set logic directly on class properties, reducing boilerplate code for validation and transformation.
Can asymmetric visibility be used with Eloquent models?
No, Eloquent models have their own accessor and mutator system (Attribute::make()) that property hooks do not directly replace. However, asymmetric visibility is highly beneficial for non-Eloquent classes.
What does array_find() do?
array_find() searches an array for the first element that matches a given condition and returns that element, or null if no match is found. It stops processing the array as soon as a match is found.