Skip to content

PHP 8.4 Property Hooks — Complete Guide to Computed Property Accessors

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about PHP 8.4 Property Hooks. We cover key concepts, practical examples, and best practices to help you master this topic.

PHP 8.4 property hooks add get and set accessors to properties, enabling computed values, validation on assignment, Lazy Loading, and asymmetric visibility without separate getter and setter methods.

What You'll Learn

By the end of this tutorial, you'll implement get and set property hooks, create virtual properties, enforce validation on assignment, and use asymmetric visibility for better Encapsulation.

Why Property Hooks Matter

Property hooks replace boilerplate getter/setter methods with declarative syntax, making value computation and validation inline with property declarations.

Real-World Use

A User entity uses a property hook to compute the full name from first and last name, validates email format on assignment, and exposes a virtual property for age calculated from birth date.

Property Hooks Path

flowchart LR
  A[JSON Validation] --> B[Property Hooks]
  B --> C[enums/fibers]
  B --> D[Readonly Classes]
  C --> E[PSR Standards]
  B --> F{You Are Here}
  style F fill:#f90,color:#fff

Get Hooks

Get hooks compute a value when the property is read.

<?php
class User {
    public private(set) string $firstName;
    public private(set) string $lastName;
    public string $fullName {
        get => "{$this->firstName} {$this->lastName}";
    }
    public function __construct(string $firstName, string $lastName) {
        $this->firstName = $firstName;
        $this->lastName = $lastName;
    }
}
$user = new User("Alice", "Johnson");
echo $user->fullName;

Set Hooks with Validation

Set hooks validate or transform values during assignment.

<?php
class Email {
    public string $value {
        set(string $value) {
            if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
                throw new \InvalidArgumentException("Invalid email address");
            }
            $this->value = strtolower($value);
        }
    }
    public function __construct(string $value) {
        $this->value = $value;
    }
}
$email = new Email("Alice@Example.com");
echo $email->value;

Asymmetric Visibility

Read and write visibility can differ for the same property.

<?php
class BankAccount {
    public private(set) float $balance = 0.0;
    public string $accountNumber {
        get => $this->accountNumber;
        set => throw new \LogicException("Account number cannot be changed");
    }
    public function __construct(
        private string $accountNumber,
        float $initialBalance = 0.0,
    ) {
        $this->balance = $initialBalance;
    }
    public function deposit(float $amount): void {
        if ($amount <= 0) throw new \InvalidArgumentException("Amount must be positive");
        $this->balance += $amount;
    }
}

Virtual Properties

Virtual properties have no backing store and are computed entirely from other data.

<?php
class Rectangle {
    public function __construct(
        public float $width,
        public float $height,
    ) {}
    public float $area {
        get => $this->width * $this->height;
    }
    public float $perimeter {
        get => 2 * ($this->width + $this->height);
    }
    public string $type {
        get => $this->width === $this->height ? "Square" : "Rectangle";
    }
}
$rect = new Rectangle(5, 10);
echo "Area: {$rect->area}, Type: {$rect->type}";

Lazy Loading with Property Hooks

Lazy-load expensive properties only when accessed.

<?php
class ExpensiveResource {
    private ?array $data = null;
    public array $processedData {
        get {
            if ($this->data === null) {
                $this->data = $this->loadData();
            }
            return $this->data;
        }
    }
    private function loadData(): array {
        sleep(1);
        return ["result" => "expensive computation"];
    }
}

Common Mistakes

1. Infinite Recursion in Get Hooks

Accessing $this->property in a get hook for the same property causes infinite recursion.

2. Set Hook Without Type Enforcement

Set hooks can accept values that do not match the declared type. Validate type explicitly.

3. Overusing Virtual Properties

Virtual properties are convenient but can hide expensive computations. Consider eager loading for predictable performance.

4. Forgetting Asymmetric Visibility on Constructor

Constructor can set private(set) properties even though external code cannot.

5. Mixing Property Hooks with __get/__set Magic Methods

Properties with hooks bypass __get and __set. Use one pattern consistently.

Practice Questions

1. What is a get hook?

A get hook defines custom logic that runs when a property value is read.

2. What is asymmetric visibility?

Different visibility for reading (public) and writing (private(set)). Useful for read-only public properties.

3. What is a virtual property?

A property with a get hook but no backing field. The value is computed each time.

4. How do property hooks compare to __get and __set?

Hooks are declarative per-property. __get/__set are catch-all magic methods that handle undefined properties.

5. Challenge: Create a Temperature class with property hooks for conversion.

<?php
class Temperature {
    public float $celsius {
        set(float $value) {
            if ($value < -273.15) throw new \InvalidArgumentException("Below absolute zero");
            $this->celsius = $value;
        }
    }
    public float $fahrenheit {
        get => $this->celsius * 9 / 5 + 32;
        set(float $value) {
            $this->celsius = ($value - 32) * 5 / 9;
        }
    }
    public function __construct(float $celsius = 0) {
        $this->celsius = $celsius;
    }
}

FAQ

Are property hooks backward compatible with PHP 8.3?

No. Property hooks require PHP 8.4. Code using hooks will produce parse errors on older versions.

Can I use property hooks with typed properties?

Yes. Hooks respect the declared type. Set hooks can add additional validation.

Do property hooks affect serialization?

Virtual properties (no backing field) are not serialized. Backed properties serialize their backing value.

Can I use hooks on promoted constructor properties?

Yes. Constructor property promotion with hooks is supported in PHP 8.4.

What is the performance impact of property hooks?

Negligible for simple get/set. Expensive operations in hooks can impact performance as they run on every access.

Mini Project: Validated Model Base Class

Build a base model class with property hooks for automatic validation.

<?php
abstract class ValidatedModel {
    protected array $errors = [];
    protected function validate(string $field, mixed $value, array $rules): void {
        foreach ($rules as $rule) {
            if ($rule === "required" && empty($value)) {
                $this->errors[$field][] = "{$field} is required";
            }
            if (str_starts_with($rule, "max:") && is_string($value)) {
                $max = (int) substr($rule, 4);
                if (strlen($value) > $max) $this->errors[$field][] = "{$field} exceeds {$max} characters";
            }
        }
    }
    public function isValid(): bool {
        return empty($this->errors);
    }
    public function getErrors(): array {
        return $this->errors;
    }
}

What's Next

PHP PSR Standards PHP 8.1 Enums PHP 8.2 Readonly Classes

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro