A property hook (PHP 8.4) attaches code to reading (get) or writing (set) a property, so callers keep plain $p->sku syntax. In set, the incoming value is $value, and the short form set => expr; stores the expression's result. Inside its own hooks, a property's name reaches the stored value directly. A property whose hooks never use their own name has no storage: it is virtual.
<?php
final class Product {
public string $sku { set => strtoupper(trim($value)); } // normalize on write
public int $priceCents = 0 { // validate on write
set => $value >= 0 ? $value : throw new ValueError("Price cannot be negative: $value");
}
public string $price { get => sprintf('$%.2f', $this->priceCents / 100); } // virtual
}
$p = new Product();
$p->sku = ' bk-101 '; $p->priceCents = 1999;
echo $p->sku, ' ', $p->price, "\n";
try { $p->priceCents = -5; } catch (ValueError $e) { echo $e->getMessage(), "\n"; }
try { $p->price = '$1'; } catch (Error $e) { echo $e->getMessage(), "\n"; }Output
BK-101 $19.99 Price cannot be negative: -5 Property Product::$price is read-only
Two limits surprise people: $p->tags[] = 'x' on an array property with a hook fails with "Indirect modification of Product::$tags is not allowed", so add an addTag() method, and a hooked property cannot be taken by reference. Keep hooks cheap: nobody reading $p->price expects a database query.