PHP 8.4 was released in November 2024 and brings some of the most interesting language features in years. Property hooks and asymmetric visibility fundamentally change how you model objects with properties. This post demonstrates the key new features with practical examples.
Property Hooks
Property hooks are the highlight of PHP 8.4. They allow you to execute custom logic when reading (get) and writing (set) a property — without explicit getter and setter methods.
Basic Syntax
<?php
declare(strict_types=1);
class Temperature
{
public float $celsius {
get => $this->celsius;
set (float $value) {
if ($value < -273.15) {
throw new \ValueError('Temperature below absolute zero');
}
$this->celsius = $value;
}
}
public function __construct(float $celsius)
{
$this->celsius = $celsius;
}
}
$temp = new Temperature(20.0);
echo $temp->celsius; // 20.0
$temp->celsius = -300; // ValueError: Temperature below absolute zero
Computed Properties
A common use case: properties that are computed from other properties:
<?php
declare(strict_types=1);
class FullName
{
public string $full {
get => trim($this->first . ' ' . $this->last);
}
public function __construct(
public string $first,
public string $last,
) {
}
}
$name = new FullName('Thomas', 'Wunner');
echo $name->full; // "Thomas Wunner"
Property Hooks in Interfaces
Interfaces can define property hooks that implementing classes must fulfill:
<?php
declare(strict_types=1);
interface HasLabel
{
public string $label { get; }
}
class Product implements HasLabel
{
public string $label {
get => strtoupper($this->name);
}
public function __construct(
public readonly string $name,
) {
}
}
Asymmetric Visibility
Asymmetric visibility solves a long-standing problem: properties that should be readable from outside but not (or only restrictedly) writable. Previously, this was only possible via explicit getters.
<?php
declare(strict_types=1);
class Order
{
// Publicly readable, only internally writable
public private(set) string $status = 'pending';
// Publicly readable, writable by subclasses and internally
public protected(set) \DateTimeImmutable $createdAt;
public function __construct()
{
$this->createdAt = new \DateTimeImmutable();
}
public function confirm(): void
{
$this->status = 'confirmed'; // Internal: allowed
}
}
$order = new Order();
echo $order->status; // "pending" — reading ok
$order->status = 'confirmed'; // Error: Cannot modify public(private set) property
$order->confirm(); // Via method: ok
Compared to readonly
readonly allows only a single assignment. private(set) allows any number of assignments, but only internally:
<?php
declare(strict_types=1);
class Counter
{
public private(set) int $count = 0;
public function increment(): void
{
$this->count++; // Allowed, because internal
}
}
$counter = new Counter();
$counter->increment();
$counter->increment();
echo $counter->count; // 2 — reading ok
$counter->count = 5; // Error — not writable from outside
New Array Functions
PHP 8.4 adds four new array functions that simplify common operations.
array_find()
Returns the first element that satisfies the callback — analogous to JavaScript's Array.find():
<?php
declare(strict_types=1);
$users = [
['name' => 'Alice', 'age' => 30],
['name' => 'Bob', 'age' => 25],
['name' => 'Charlie', 'age' => 35],
];
$adult = array_find($users, fn(array $u): bool => $u['age'] >= 30);
// ['name' => 'Alice', 'age' => 30]
// Previously you had to write:
$adult = null;
foreach ($users as $user) {
if ($user['age'] >= 30) {
$adult = $user;
break;
}
}
array_find_key()
Returns the key of the first matching element:
<?php
declare(strict_types=1);
$products = ['apple' => 1.20, 'banana' => 0.50, 'cherry' => 2.00];
$expensiveKey = array_find_key($products, fn(float $price): bool => $price > 1.50);
// 'cherry'
array_any() and array_all()
Check whether at least one element (any) or all elements (all) satisfy a condition:
<?php
declare(strict_types=1);
$scores = [75, 82, 91, 68, 88];
$anyPassed = array_any($scores, fn(int $score): bool => $score >= 90);
// true (91 >= 90)
$allPassed = array_all($scores, fn(int $score): bool => $score >= 60);
// true (all >= 60)
$allExcellent = array_all($scores, fn(int $score): bool => $score >= 90);
// false
#[\Deprecated] Attribute
PHP 8.4 introduces an official #[\Deprecated] attribute that allows you to mark your own functions and methods as deprecated:
<?php
declare(strict_types=1);
class UserService
{
#[\Deprecated(
message: 'Use findByEmail() instead',
since: '2.0',
)]
public function getUserByEmail(string $email): ?User
{
return $this->findByEmail($email);
}
public function findByEmail(string $email): ?User
{
// ...
}
}
When calling a method marked this way, PHP generates an E_USER_DEPRECATED warning — just like with PHP's own deprecated functions.
HTML5 Parser in ext-dom
The DOM extension receives a new, fully HTML5-compliant parser that correctly parses modern HTML documents:
<?php
declare(strict_types=1);
$dom = Dom\HTMLDocument::createFromString('
<html>
<body>
<article>
<h1>Titel</h1>
<p>Inhalt</p>
</article>
</body>
</html>
');
$article = $dom->querySelector('article');
$heading = $article->querySelector('h1');
echo $heading->textContent; // "Titel"
This is particularly useful for web scraping and HTML transformation, where the previous DOMDocument often struggled with modern HTML.
Conclusion
PHP 8.4 is a solid release that significantly improves object orientation with property hooks and asymmetric visibility. The new array functions are welcome additions that shorten common patterns. If you are using Symfony 7 or another modern framework, you can adopt PHP 8.4 without hesitation — compatibility is excellent.
As a first step, the installation is straightforward:
# In DDEV .ddev/config.yaml
php_version: "8.4"
ddev restart
ddev exec php --version
Kommentare
Kommentare werden von Remark42 bereitgestellt. Beim Laden werden Daten an unseren Kommentar-Server übertragen.