Cannot modify readonly property の原因と直し方
readonlyを付けたプロパティは、コンストラクタの中で一度初期化した後は二度と値を変更できません。クラスの外からはもちろん、クラスの中のメソッドからの再代入も拒否されます。
エラーメッセージの読み方
Fatal error: Uncaught Error: Cannot modify readonly property Point::$x in main.php:9
main.php- ファイル名
9- 行番号 — 実際にクラッシュした行
Error- 例外クラス — 何が起きたか。ここを検索するのが最短です
Cannot modify readonly property Point::$x- 内容 — どの値が問題だったか
このエラーが出る典型パターン
パターン1
1 <?php 2 class Point { 3 public function __construct( 4 public readonly int $x, 5 public int $y 6 ) {} 7 } 8 $p = new Point(1, 2); 9 $p->x = 99; ^
$xはreadonlyなので、コンストラクタで初期化した後は二度と代入できません。書き換えたい値はreadonlyを付けていない$yのようなプロパティにする必要があります。
直し方: x を y にします。
パターン2
1 <?php 2 class Account { 3 public function __construct( 4 public readonly string $id, 5 public int $balance 6 ) {} 7 } 8 $acc = new Account("AC1", 1000); 9 $acc->id = 2000; ^
$idはreadonlyなので再代入できません。変更したい値は$balanceのようなreadonlyでないプロパティに限られます。
直し方: id を balance にします。
パターン3
1 <?php 2 class Config { 3 public function __construct( 4 public readonly string $key, 5 public string $value 6 ) {} 7 } 8 $cfg = new Config("env", "dev"); 9 $cfg->key = "prod"; ^
$keyはreadonlyのため書き換えできません。更新したいのは$valueのようなreadonlyでないプロパティです。
直し方: key を value にします。
パターン4
1 <?php 2 class Book { 3 public function __construct( 4 public readonly string $isbn, 5 public int $stock 6 ) {} 7 } 8 $b = new Book("111", 5); 9 $b->isbn = 99; ^
$isbnはreadonlyなので、コンストラクタで初期化した後は二度と代入できません。書き換えたい値は$stockのようなreadonlyでないプロパティにする必要があります。
直し方: isbn を stock にします。
パターン5
1 <?php 2 class Employee { 3 public function __construct( 4 public readonly string $ssn, 5 public float $salary 6 ) {} 7 } 8 $e = new Employee("123", 50000); 9 $e->ssn = 60000; ^
$ssnはreadonlyのため書き換えできません。変更したい値は$salaryのようなreadonlyでないプロパティです。
直し方: ssn を salary にします。
よくある誤解
「readonlyでもクラスの内部からなら書き換えられるはず」という思い込みは誤りです。readonlyは初期化済みかどうかだけを見るため、クラスの内外を問わず2回目の代入は必ずエラーになります。
実務での勘所
readonlyが禁止しているのは、そのプロパティに別のオブジェクトを再代入することだけです。プロパティがオブジェクトを保持している場合、そのオブジェクト自身が持つ、中身を変更するメソッドを呼び出すことは制限されません。readonly private array $items = [];であれば要素の追加はできませんが、オブジェクトを保持するreadonlyプロパティであれば、差し替えはできなくてもそのオブジェクトの可変なメソッドがあれば内部状態を変える余地は残ります。