In the getters and setters lesson you learned to wrap a property in methods so you can control how it's read and written. PHP 8.4 property hooks let you attach that same get and set logic directly to the property, without writing separate methods. The result is shorter, clearer classes.
This lesson covers the basics so you can start using hooks today. For a deeper dive - virtual properties, interface requirements, and when hooks beat plain getters - read the full PHP 8.4 property hooks guide on the blog.
The problem hooks solve
Say you want a User whose name is always stored trimmed and displayed capitalized. Before hooks, you'd write a private property plus two methods:
<?php
// The old getter/setter approach
class User {
private string $name = '';
public function setName(string $value): void {
$this->name = trim($value);
}
public function getName(): string {
return ucfirst($this->name);
}
}
$user = new User();
$user->setName(' ann ');
echo $user->getName(); // Ann
That works, but it's a lot of code for a simple rule, and callers have to remember to use getName() instead of just $user->name.
The same thing with a property hook
With PHP 8.4, you put the logic in get and set blocks right on the property:
<?php
class User {
public string $name = '' {
get => ucfirst($this->name);
set => $this->name = trim($value);
}
}
$user = new User();
$user->name = ' ann '; // the set hook runs and trims
echo $user->name; // Ann (the get hook capitalizes)
Look at how it reads now: callers use the plain property $user->name, but your rules still run behind the scenes. A few things to notice:
- The
gethook runs whenever the property is read. Here it returns the value capitalized. - The
sethook runs whenever the property is written. The value being assigned is available as$value. - Inside a hook,
$this->namerefers to the stored value - it does not trigger the hook again, so there's no infinite loop.
Virtual properties (a get with no stored value)
A property doesn't even need to store anything. A virtual property is computed on the fly from other properties. This is perfect for things like a full name:
<?php
class Person {
public function __construct(
public string $firstName,
public string $lastName,
) {}
public string $fullName {
get => "{$this->firstName} {$this->lastName}";
}
}
$person = new Person('Ada', 'Lovelace');
echo $person->fullName; // Ada Lovelace
Here $fullName has only a get hook and no backing storage. It's calculated fresh each time you read it. If someone tries to write to it, PHP throws an error - which is exactly what you want for a read-only computed value.
Validating input with a set hook
A set hook is a natural home for validation. If the value is wrong, throw an exception before it's ever stored:
<?php
class Product {
public float $price {
set {
if ($value < 0) {
throw new InvalidArgumentException('Price cannot be negative.');
}
$this->price = $value;
}
}
}
$product = new Product();
$product->price = 19.99; // fine
echo $product->price; // 19.99
$product->price = -5; // throws InvalidArgumentException
The bad value never makes it into the object. That's much harder to get wrong than a separate setPrice() method that a caller might forget to use.
Summary
-
Property hooks (PHP 8.4) let you attach
getandsetlogic directly to a property. - The
gethook runs on read; thesethook runs on write, with the incoming value in$value. - Inside a hook, referring to the property reads its stored value without re-triggering the hook.
-
Virtual properties have a
getwith no stored value - great for computed fields. - A
sethook is the cleanest place to validate input.
Property hooks replace most one-line getters and setters with something callers can use like a normal property. For the advanced details, see the full property hooks guide.
FAQ
What are property hooks in PHP 8.4?
They are get and set blocks attached directly to a property. The get block runs when the property is read, and the set block runs when it's written, so you can transform or validate values without separate getter and setter methods.
Do property hooks cause infinite recursion?
No. Inside a hook, accessing the property (like $this->name) reads or writes the backing storage directly - it does not call the hook again.
What is a virtual property?
A property that has a get hook but stores no value of its own. It's computed from other properties every time it's read, like a fullName built from firstName and lastName.