Code Fix

上級

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;
         ^
Fatal error: Uncaught Error: Cannot modify readonly property Point::$x in main.php:9

$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;
              ^
Fatal error: Uncaught Error: Cannot modify readonly property Account::$id in main.php:9

$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";
            ^
Fatal error: Uncaught Error: Cannot modify readonly property Config::$key in main.php:9

$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;
           ^
Fatal error: Uncaught Error: Cannot modify readonly property Book::$isbn in main.php:9

$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;
             ^
Fatal error: Uncaught Error: Cannot modify readonly property Employee::$ssn in main.php:9

$ssnはreadonlyのため書き換えできません。変更したい値は$salaryのようなreadonlyでないプロパティです。

直し方: ssn を salary にします。

この問題を解いてみる →

よくある誤解

「readonlyでもクラスの内部からなら書き換えられるはず」という思い込みは誤りです。readonlyは初期化済みかどうかだけを見るため、クラスの内外を問わず2回目の代入は必ずエラーになります。

実務での勘所

readonlyが禁止しているのは、そのプロパティに別のオブジェクトを再代入することだけです。プロパティがオブジェクトを保持している場合、そのオブジェクト自身が持つ、中身を変更するメソッドを呼び出すことは制限されません。readonly private array $items = [];であれば要素の追加はできませんが、オブジェクトを保持するreadonlyプロパティであれば、差し替えはできなくてもそのオブジェクトの可変なメソッドがあれば内部状態を変える余地は残ります。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)