An interface (Interfaces and Generators) can now declare a property: public int $priceCents { get; } promises a readable one, { get; set; } a writable one. A class satisfies it with a plain, hooked or virtual property, and abstract classes declare abstract properties the same way. Hooks also work on promoted constructor parameters.
<?php
interface Priced { public int $priceCents { get; } } // a property in an interface
final class Book implements Priced {
public function __construct(
public string $title { set => trim($value); }, // hook on a promoted property
public int $priceCents = 0, // a plain property satisfies it
) {}
}
final class Bundle implements Priced {
public function __construct(private array $items) {}
public int $priceCents { // virtual: summed on each read
get => array_sum(array_map(fn(Priced $p) => $p->priceCents, $this->items));
}
}
$b = new Book(' LAMP Stack Development ', 3990);
echo "[{$b->title}] ", new Bundle([$b, new Book('PHP Pocket Guide', 1500)])->priceCents, "\n";Output
[LAMP Stack Development] 5490
The contract hides how the value is produced, so Bundle could switch from computing to caching without touching a caller. Declare interface properties only for data; behavior still belongs in methods.