A readonly property does not exist yet; here is the 7.4 version

Immutability in 7.4 is a convention enforced by discipline: a private typed property, a constructor that sets it, and no setter.

final class Money
{
    private int $cents;
    private string $currency;

    public function __construct(int $cents, string $currency)
    {
        $this->cents    = $cents;
        $this->currency = $currency;
    }

    public function add(Money $other): self
    {
        return new self($this->cents + $other->cents, $this->currency);
    }
}

final matters more than it looks: without it a subclass can add a setter and the guarantee is gone. Returning a new instance from every operation is what makes the object safe to pass around, and it is the half people skip when adding a “just this one” mutator. The readonly keyword arrives in 8.1 and enforces at the language level what this does by agreement — which is worth knowing because the migration then is mechanical.