Menu
Coddy logo textTech

遅延静的束縛

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
ParentClass

ChildClass::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
ChildClass

static::を使うと、PHPは実行時まで、どのクラスを使用するかを決定しません。決定が遅れるため、これは「遅延」バインディングと呼ばれます。factory メソッドや、継承を尊重すべきあらゆる静的メソッドに不可欠です。

重要なポイント: 子クラスが静的な動作を適切に継承してカスタマイズできるようにしたい場合は、self:: ではなく static:: を使用します。

challenge icon

チャレンジ

簡単

late static binding の強力さを示すモデルレジストリシステムを構築しましょう。子クラスが継承できるファクトリ method を持つ基底 Model class を作成します。そして static:: のおかげで、各子クラスは親ではなく、自分自身のインスタンスを正しく作成します。

コードを4つのファイルに分けて整理します。

  • Model.php: 保護された static property $tableName"models" に設定した基底 Model class を Create します。new static() を使用して、それを calling した class の新しい instance を返す static method create() を追加します。さらに、static::$tableName を returns する別の static method getTable() を追加します。最後に、static::classstatic::$tableName を使用して "[ClassName] from table [tableName]" を returns する describe() method を追加します。
  • User.php: Model を extends する User class を Create します。親ファイルを Include します。static property $tableName"users" に Override します。この class は、変更なしですべての method を Model から inherits します。
  • Product.php: Model を extends する Product class を Create します。親ファイルを Include します。static property $tableName"products" に Override します。この class も、親からすべての method を inherits します。
  • main.php: User ファイルと Product ファイルの両方を Include します。inherited した create() factory method を使用して、UserProduct の両方の 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() を出力する

?>
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: PHPオンラインコンパイラ