遅延静的束縛
CoddyのPHPジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 36/91。
親クラスでself::を使用すると、それは常に実際に使用されているクラスではなく、記述されているクラスを参照します。これにより、継承で予期しない動作が発生することがあります。遅延静的束縛は、static::キーワードを使用してこの問題を解決します。
self:: が期待どおりに動作しない例を考えてみましょう。
<?php
class ParentClass {
public static function create() {
return new self();
}
public static function getClassName() {
return self::class;
}
}
class ChildClass extends ParentClass {}
$obj = ChildClass::create();
echo get_class($obj) . "\n";
echo ChildClass::getClassName();
出力:
ParentClass
ParentClassChildClass::create() を呼び出したにもかかわらず、self:: はコンパイル時に解決されるため、ParentClass オブジェクトが返されました。では、代わりに static:: を使ってみましょう。
<?php
class ParentClass {
public static function create() {
return new static();
}
public static function getClassName() {
return static::class;
}
}
class ChildClass extends ParentClass {}
$obj = ChildClass::create();
echo get_class($obj) . "\n";
echo ChildClass::getClassName();
出力:
ChildClass
ChildClassstatic::を使うと、PHPは実行時まで、どのクラスを使用するかを決定しません。決定が遅れるため、これは「遅延」バインディングと呼ばれます。factory メソッドや、継承を尊重すべきあらゆる静的メソッドに不可欠です。
重要なポイント: 子クラスが静的な動作を適切に継承してカスタマイズできるようにしたい場合は、self:: ではなく static:: を使用します。
チャレンジ
簡単late static binding の強力さを示すモデルレジストリシステムを構築しましょう。子クラスが継承できるファクトリ method を持つ基底 Model class を作成します。そして static:: のおかげで、各子クラスは親ではなく、自分自身のインスタンスを正しく作成します。
コードを4つのファイルに分けて整理します。
Model.php: 保護された static property$tableNameを"models"に設定した基底Modelclass を Create します。new static()を使用して、それを calling した class の新しい instance を返す static methodcreate()を追加します。さらに、static::$tableNameを returns する別の static methodgetTable()を追加します。最後に、static::classとstatic::$tableNameを使用して"[ClassName] from table [tableName]"を returns するdescribe()method を追加します。User.php:Modelを extends するUserclass を Create します。親ファイルを Include します。static property$tableNameを"users"に Override します。この class は、変更なしですべての method をModelから inherits します。Product.php:Modelを extends するProductclass を Create します。親ファイルを Include します。static property$tableNameを"products"に Override します。この class も、親からすべての method を inherits します。main.php: User ファイルと Product ファイルの両方を Include します。inherited したcreate()factory method を使用して、UserとProductの両方の instance を Create します。それぞれの instance についてdescribe()を Call し、result をそれぞれ独立した行に Print します。その後、User::getTable()とProduct::getTable()を使用して各 class の table 名を Print し、それぞれ独立した行に表示します。
ここでの magic は、create() と getTable() method が親の Model class でのみ定義されているにもかかわらず、static:: が binding を runtime まで遅延させるため、子 class でも正しく機能することです。User::create() を Call すると、static:: は実際に Call された class を参照するため、PHP は Model instance ではなく User instance を作成します。
出力には4行が表示されます。User instance の説明、Product instance の説明、User の table 名、そして Product の table 名です。
自分で試してみよう
<?php
require_once 'User.php';
require_once 'Product.php';
// TODO: 継承された create() ファクトリメソッドを使用して User インスタンスを作成する
// TODO: 継承された create() ファクトリメソッドを使用して Product インスタンスを作成する
// TODO: User インスタンスで describe() を呼び出し、結果を出力する
// TODO: Product インスタンスで describe() を呼び出し、結果を出力する
// TODO: User::getTable() を出力する
// TODO: Product::getTable() を出力する
?>このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: PHPオンラインコンパイラ