Lesson 9 / 25

Property Hooks, Asymmetric Visibility, Attributes and Magic Methods

Use PHP 8.4 property features, attributes and magic methods sensibly.

The newest object features

PHP 8.4 added property hooks: get and set logic attached directly to a property, so you can validate or compute values without writing separate getter and setter methods, while callers still use plain property syntax ($product->price = 499;). It also added asymmetric visibility, such as public private(set) string $status, meaning anyone can read the property but only the class can change it, which replaces many getter-only patterns. Attributes (PHP 8.0), written #[Route('/orders', methods: ['GET'])], attach structured metadata to classes, methods, properties and parameters; frameworks read them via reflection for routing, validation, ORM mapping and dependency injection, replacing docblock annotations. Magic methods such as __get, __set, __call, __toString, __invoke and __serialize customise behaviour, but overusing __get/__call hides APIs from IDEs and static analysis; prefer explicit methods and the newer typed features. Also note PHP 8.4's ability to chain on new without extra parentheses: new Order()->addItem(...).

Property hooks and asymmetric visibility (PHP 8.4)

Validation in a set hook; status readable everywhere but writable only inside the class.

<?php
declare(strict_types=1);

final class Product
{
    public private(set) string $status = 'draft';      // public read, private write

    public int $pricePaise {
        set (int $value) {
            if ($value < 0) {
                throw new InvalidArgumentException('Price cannot be negative');
            }
            $this->pricePaise = $value;
        }
    }

    public string $displayPrice {
        get => number_format($this->pricePaise / 100, 2) . ' INR';   // computed, no storage
    }

    public function __construct(public readonly string $sku, int $pricePaise)
    {
        $this->pricePaise = $pricePaise;                 // runs the set hook
    }

    public function publish(): void
    {
        $this->status = 'published';
    }
}

$p = new Product('PEN-BLUE', 4950);
echo $p->displayPrice, PHP_EOL;     // 49.50 INR
// $p->status = 'published';        // Error: cannot modify private(set) property
$p->publish();

Check your PHP version before using new syntax

Property hooks and asymmetric visibility require PHP 8.4. Libraries that must support older versions should keep explicit getter and setter methods; applications on 8.4+ can adopt the new syntax gradually.

Quick check: What does `public private(set) string $status` mean in PHP 8.4?

  • The property is private for reading and writing
  • The property is constant
  • Anyone can read the property, but only the class itself can modify it
  • It is a syntax error
Answer

Anyone can read the property, but only the class itself can modify it — Asymmetric visibility separates read access from write access.