🗂

PHPコレクション実装を通じて理解するLate Static Bindings

2024/01/10に公開

Late Static Bindingsとは?

Late Static Bindingsは、実行時にクラスの静的な参照を解決するPHPの機能です。これは特に、メソッドチェーンや継承されたクラスで重要となります。staticキーワードを使うことで、実行時にどのクラスが使用されているかに基づいてメソッドやプロパティを参照できます。

コレクションの実装例

PHPでコレクションを実装する際、メソッドチェーンをサポートするためにLate Static Bindingsを活用することができます。
以下に、基本的なコレクションクラスの例を示します。

class Collection
{
    protected $items = [];

    public function __construct(
        array $items = []
    ) {
        $this->items = $items;
    }

    public function filter (callable $callback): Collection
    {
        return new static(array_filter($this->items, $callback));
    }

    public function map(callable $callback): Collection
    {
        return new static(array_map($callback, $this->items));
    }
}

$collection = new Collection([1, 2, 3, 4, 5]);

$results = $collection->filter(function ($item) {
    return $item % 2 === 0;
})->map(function ($item) {
    return $item * 2;
});

// 実行結果: $results = [4, 8];

static、selfの使い分け

new static()new self()には、次のような違いがあります。

  • new static()
    • 実行時に解決され、サブクラスでオーバーライドされた場合、そのサブクラスのインスタンスを生成します。これは、継承を活用する場合に適しています。
  • new self()
    • 常にselfが定義されたクラス(この例ではCollectionクラス)のインスタンスを生成します。これは、継承を考慮する必要がない場合に使用します。

したがって、UserCollectionのようなサブクラスを考慮するコレクションを実装する場合は、new static()の使用が適切です。一方で、finalクラスなど継承できないクラスに対しては、new self()の使用が効果的です。

以下にUserCollectionの実装例を示します。

class UserCollection extends Collection
{
    // UserCollectionに特有のメソッドやプロパティをここに追加
    public function sortByEmail(): static
    {
        $sorted = $this->items;
        usort($sorted, function ($a, $b) {
            return $a['email'] <=> $b['email'];
        });
        return new static($sorted);
    }
}

// UserCollectionのインスタンスを作成
$userCollection = new UserCollection([
    ['email' => 'user1@example.com', 'name' => 'User 1'],
    ['email' => 'user2@example.com', 'name' => 'User 2'],
]);

// emailでソート
$sortedCollection = $userCollection->sortByEmail();

この実装により、静的解析ツールを使用する際にも、適切な型情報としてUserCollectionを扱うことが可能になります。

まとめ

PHPのコレクション実装を例に、Late Static Bindingsの有効性について解説しました。
適切なキーワード(staticself)を選択することで、継承の柔軟性とコードの明確性のバランスを取ることができます。
設計の意図に基づいてこれらのオプションを選択し、より堅牢で再利用可能なコードを作成しましょう。

参考URL

https://www.php.net/manual/ja/language.oop5.late-static-bindings.php

おまけ

記事の主題とは少し離れますが、PHPのオブジェクト指向プログラミングにおいて重要な、static::self::の違いについても触れておきます。

static::メソッド()は、Late Static Bindingsを利用して、実行時にクラスの解決を行います。これにより、サブクラスでオーバーライドされたメソッドがあれば、そのサブクラスのメソッドが呼び出されます。対してself::メソッド()は、常にそのメソッドが定義されているクラスのメソッドを呼び出します。

class ParentClass {
    public static function test() {
        static::who(); // Late Static Bindingsを使用
        self::who();   // 通常のStatic Bindingsを使用
    }

    public static function who() {
        echo "ParentClass\n";
    }
}

class ChildClass extends ParentClass {
    public static function who() {
        echo "ChildClass\n";
    }
}

class AnotherChildClass extends ParentClass {
    // whoメソッドはオーバーライドしていない
}

ChildClass::test();
// 出力:
// ChildClass
// ParentClass

AnotherChildClass::test();
// 出力:
// ParentClass
// ParentClass

このサンプルコードを通じて、static::self::がPHPのオブジェクト指向プログラミングにおいてどのように機能するかをより深く理解することができると思います。

GitHubで編集を提案

Discussion