New in PHP 8.4
PHP 8.4 removed a lot of boilerplate from classes: get and set methods are often no longer needed. New array functions appeared too. Everything in this lesson requires version 8.4 or newer.
The set hook
A hook is code that runs when a property is read or written. The set hook receives the new value in the $value variable:
<?php
class User
{
public string $name {
set => trim($value);
}
public function __construct(string $name)
{
$this->name = $name;
}
}
$user = new User(' Anna ');
echo "[$user->name]"; // [Anna]From outside it is an ordinary property, yet it always holds a trimmed string. Previously this took a private property and a setName() method.
The get hook
A property with only a get hook stores no value — it is computed on every access:
<?php
class Rectangle
{
public int $area {
get => $this->width * $this->height;
}
public function __construct(public int $width, public int $height) {}
}
$box = new Rectangle(3, 4);
echo $box->area; // 12Validating in a hook
The full form of a hook — with curly braces — can check a value and reject it:
<?php
class Product
{
public int $price {
set {
if ($value < 0) {
throw new InvalidArgumentException('The price cannot be negative');
}
$this->price = $value;
}
}
}
$product = new Product();
$product->price = 250;
try {
$product->price = -5;
} catch (InvalidArgumentException $e) {
echo $e->getMessage(), ': ', $product->price;
}The program prints The price cannot be negative: 250 — the wrong value was not written.
Asymmetric visibility
A property can be public for reading and private for writing:
<?php
class Account
{
public private(set) int $balance = 0;
public function deposit(int $amount): void
{
$this->balance += $amount;
}
}
$account = new Account();
$account->deposit(500);
echo $account->balance, "\n"; // 500
try {
$account->balance = 1000000;
} catch (Error $e) {
echo 'The balance cannot be changed from outside';
}Unlike readonly, such a property can change any number of times — but only from inside the class.
A method right after new
A new expression used to need parentheses. Now a method is called at once:
<?php
class Text
{
public function __construct(private string $value) {}
public function upper(): string
{
return strtoupper($this->value);
}
}
echo new Text('hello')->upper(); // HELLONew array functions
<?php
$prices = [120, 450, 90, 700];
echo array_find($prices, fn($price) => $price > 400), "\n"; // 450
echo array_find_key($prices, fn($price) => $price > 400), "\n"; // 1
echo array_any($prices, fn($price) => $price > 600) ? 'yes' : 'no', "\n"; // yes
echo array_all($prices, fn($price) => $price > 100) ? 'yes' : 'no'; // no| Function | What it returns |
|---|---|
array_find() | The first matching element or null |
array_find_key() | The key of the first matching element |
array_any() | true if at least one matches |
array_all() | true if all match |
8.4 also added mb_trim(), mb_ucfirst() and the #[\Deprecated] attribute for marking outdated functions.
Common mistake
A property with only a get hook is computed and cannot be written:
<?php
class Rectangle
{
public int $area {
get => $this->width * $this->height;
}
public function __construct(public int $width, public int $height) {}
}
$box = new Rectangle(3, 4);
try {
$box->area = 100;
} catch (Error $e) {
echo 'The area is computed and cannot be assigned';
}To change the area, change the width or the height. And mind the version: on a server with PHP 8.3 a file with hooks does not run at all — check php -v.