Code Fix

上級

self:: で作ったインスタンスは継承先のサブクラスにならない(static:: との違い)

戻り値の型を static と宣言したメソッドの中で new self() を使うと、呼び出し元がサブクラスであっても常に定義元のクラスのインスタンスが作られます。呼び出し時点のクラスを使いたい場合は new static() が必要です。

エラーメッセージの読み方

Fatal error: Uncaught TypeError: Base::create(): Return value must be of type Derived, Base returned in main.php:4
main.php
ファイル名
4
行番号 — 実際にクラッシュした行
TypeError
例外クラス — 何が起きたか。ここを検索するのが最短です
Base::create(): Return value must be of type Derived, Base returned
内容 — どの値が問題だったか

このエラーが出る典型パターン

パターン1

 1  <?php
 2  class Base {
 3      public static function create(): static {
 4          return new self  ();
                          ^
 5      }
 6  }
 7  class Derived extends Base {}
 8  $obj = Derived::create();
 9  echo get_class($obj);
Fatal error: Uncaught TypeError: Base::create(): Return value must be of type Derived, Base returned in main.php:4

戻り値の型はstaticと宣言されているのに、new self()は常に定義元のBaseを作ります。呼び出し時点のクラス(この場合Derived)を作るにはnew static()が必要です。

直し方: self を static にします。

この問題を解いてみる →

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

パターン2

 1  <?php
 2  class Animal {
 3      public static function make(): static {
 4          return new self  ();
                          ^
 5      }
 6  }
 7  class Cat extends Animal {}
 8  $obj = Cat::make();
 9  echo get_class($obj);
Fatal error: Uncaught TypeError: Animal::make(): Return value must be of type Cat, Animal returned in main.php:4

self::は定義されたクラス(Animal)に固定されます。呼び出し元のサブクラス(Cat)を返したい場合はstatic(遅延静的束縛)を使う必要があります。

直し方: self を static にします。

この問題を解いてみる →

パターン3

 1  <?php
 2  class Shape {
 3      public static function build(): static {
 4          return new self  ();
                          ^
 5      }
 6  }
 7  class Square extends Shape {}
 8  $obj = Square::build();
 9  echo get_class($obj);
Fatal error: Uncaught TypeError: Shape::build(): Return value must be of type Square, Shape returned in main.php:4

new self()は必ずShapeのインスタンスを作ります。戻り値の型staticが要求するSquareを返すにはnew static()にする必要があります。

直し方: self を static にします。

この問題を解いてみる →

パターン4

 1  <?php
 2  class Vehicle {
 3      public static function spawn(): static {
 4          return new self   ();
                          ^
 5      }
 6  }
 7  class Truck extends Vehicle {}
 8  $obj = Truck::spawn();
 9  echo get_class($obj);
Fatal error: Uncaught TypeError: Vehicle::spawn(): Return value must be of type Truck, Vehicle returned in main.php:4

戻り値の型はstaticと宣言されているのに、new self()は常に定義元のVehicleを作ります。呼び出し時点のクラス(Truck)を作るにはnew static()が必要です。

直し方: self を static にします。

この問題を解いてみる →

パターン5

 1  <?php
 2  class Widget {
 3      public static function make(): static {
 4          return new self  ();
                          ^
 5      }
 6  }
 7  class Button extends Widget {}
 8  $obj = Button::make();
 9  echo get_class($obj);
Fatal error: Uncaught TypeError: Widget::make(): Return value must be of type Button, Widget returned in main.php:4

self::は定義されたクラス(Widget)に固定されます。呼び出し元のサブクラス(Button)を返したい場合はstaticを使う必要があります。

直し方: self を static にします。

この問題を解いてみる →

よくある誤解

「self::とstatic::はどちらも自分のクラスを指すので同じはず」という思い込みは誤りです。selfは定義されたクラスに固定されますが、staticは実際に呼び出されたクラス(遅延静的束縛)を指します。

実務での勘所

「遅延静的束縛」という名前の「遅延」は、staticキーワードがどのクラスを指すかの決定を、メソッドが定義された時点ではなく、実際に呼び出された瞬間まで先延ばしにすることを意味します。selfはメソッドが書かれているクラスに常に固定される「早期」束縛ですが、staticは呼び出しの起点になった実際のクラスを実行時に遡って特定します。PHP 5.3でこの機能が追加される前は、継承先のクラスを正しく返すファクトリメソッドをself::だけで書くことができませんでした。

演習をはじめる

関連するエラー

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