objects namespaces and application architecture
Property Hooks
Property hooks let a PHP property run code when it is read or written. They give some of the control of getters and setters while keeping property syntax.
Hooks are useful for small, local rules such as validation, normalisation, and computed values. They should not hide expensive work, database queries, HTTP calls, or surprising side effects behind simple-looking property access.
Set Hooks
A set hook runs when a value is assigned to a property.
<?php
declare(strict_types=1);
final class UserProfile
{
public string $email {
set {
$email = strtolower(trim($value));
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('Email address is not valid.');
}
$this->email = $email;
}
}
}
$profile = new UserProfile();
$profile->email = ' Ada@Example.COM ';
echo $profile->email . PHP_EOL;
// Prints:
// ada@example.com
The assignment still looks like property assignment, but the hook trims, lowercases, and validates before storing the value.
Get Hooks
A get hook runs when a property is read. It can return a computed value.
<?php
declare(strict_types=1);
final class PersonName
{
public function __construct(
public string $firstName,
public string $lastName,
) {
}
public string $fullName {
get => $this->firstName . ' ' . $this->lastName;
}
}
$name = new PersonName('Ada', 'Lovelace');
echo $name->fullName . PHP_EOL;
// Prints:
// Ada Lovelace
This is clearer than storing a separate $fullName property that can fall out of sync.
Hooks Versus Getter Methods
Property hooks are best when the operation feels like property access. A computed display name or a normalised email property can be reasonable.
Use a method when the operation sounds like work:
calculateTotal()loadInvoices()sendEmail()refreshFromApi()
Reading $object->total should not unexpectedly query a database or call a remote service.
Constructor Promotion And Hooks
Promoted properties can use hooks too.
<?php
declare(strict_types=1);
final class Tag
{
public function __construct(
public string $name {
set {
$name = trim($value);
if ($name === '') {
throw new InvalidArgumentException('Tag name cannot be empty.');
}
$this->name = $name;
}
},
) {
}
}
$tag = new Tag(' php ');
echo $tag->name . PHP_EOL;
// Prints:
// php
This keeps the property declaration and its assignment rule together.
Hooks Are Not A Replacement For Good Design
Hooks can make code cleaner, but overusing them creates hidden behaviour. If every property runs complex code, reading the class becomes harder.
Good hook use is:
- local to the property
- quick to execute
- easy to explain
- free of external side effects
- tested with normal and invalid values
Bad hook use hides business workflows, persistence, or network calls behind assignment.
What You Should Be Able To Do
After this lesson, you should be able to use set hooks for validation or normalisation, use get hooks for simple computed properties, and decide when a normal method is clearer.
For junior work, this matters because property hooks make modern PHP more expressive, but they also make it easier to hide behaviour if used carelessly.
Practice
Practice: Normalise A Hooked Property
Create a small PHP example using a set hook.
Task
Build a UserProfile class with an email property that:
- trims the assigned value
- lowercases it
- validates it as an email address
- stores only the normalised value
Use strict types. Keep the expected output in the PHP code block as printed lines or comments.
Check Your Work
Run cases for:
- assigning a valid email with spaces and uppercase letters
- assigning an invalid email
Afterward, explain why this hook is reasonable and what kind of work should not be hidden in a property hook.
Show solution
<?php
declare(strict_types=1);
final class UserProfile
{
public string $email {
set {
$email = strtolower(trim($value));
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('Email address is not valid.');
}
$this->email = $email;
}
}
}
$profile = new UserProfile();
$profile->email = ' Ada@Example.COM ';
echo $profile->email . PHP_EOL;
try {
$profile->email = 'not-an-email';
} catch (InvalidArgumentException $exception) {
echo $exception->getMessage() . PHP_EOL;
}
// Prints:
// ada@example.com
// Email address is not valid.
This hook is reasonable because it performs quick validation and normalisation directly related to the property. It should not send email, query a database, call an API, or trigger a business workflow.