Readonly vlastnosti měly od začátku jedno nenápadné omezení: nesměly mít výchozí hodnotu. PHP 8.6 ho ruší.
Když jsem tu před lety psal o readonly vlastnostech v PHP 8.1, tenhle kód skončil kompilační chybou (a v 8.5 pořád ještě skončí):
<?php
final readonly class ExportFaktur
{
public string $nazev = 'export-faktur';
}
// Fatal error: Readonly property ExportFaktur::$nazev cannot have default value
Původní RFC to zakázalo s tím, že taková vlastnost je vlastně konstanta, takže je zbytečná. Technická překážka v tom nikdy nebyla a mezitím se ukázalo, že využití existuje.
K čemu je to dobré?
Hlavní důvod jsou interface s vlastnostmi, které přišly s property hooks v PHP 8.4. Konstantou takový kontrakt nesplníte, vlastností ano:
<?php
interface Migrace
{
public string $nazev { get; }
}
final readonly class VytvorTabulkuUzivatelu implements Migrace
{
public string $nazev = '2026_01_01_vytvor_tabulku_uzivatelu';
}
Dřív se musel napsat konstruktor, který nedělá nic jiného, než že do vlastnosti nasype napevno danou hodnotu:
<?php
final readonly class VytvorTabulkuUzivatelu implements Migrace
{
public string $nazev;
public function __construct()
{
$this->nazev = '2026_01_01_vytvor_tabulku_uzivatelu';
}
}
Na co si dát bacha?
Výchozí hodnota se počítá jako první a zároveň poslední přiřazení. Vlastnost je inicializovaná ještě před během konstruktoru, takže v něm už ji přepsat nejde:
<?php
final readonly class ExportFaktur
{
public string $nazev = 'export-faktur';
public function __construct(string $nazev)
{
$this->nazev = $nazev; // Error: Cannot modify readonly property
}
}
Z toho plynou dvě další věci. Takovou vlastnost nejde unset(), na rozdíl od neinicializované readonly vlastnosti. A splňuje jen kontrakt { get; }, ne { get; set; }.
RFC prošlo v poměru 24 : 0 (a 5 se zdrželo). Malá změna, ale ubyde konstruktorů, které existují jenom proto, že to jinak nešlo.