PHP/ジェネリックプログラミング

< PHP

PHPでは、他の言語にあるような公式のジェネリックプログラミングサポート(C++やJavaに見られるようなテンプレートやジェネリクスの直接的な文法)はありませんが、いくつかのテクニックを使用してジェネリックな動作を実現できます。特に、PHP 7以降の型宣言やPHP 8.0以降の新機能を駆使して、ジェネリック風のプログラミングが可能です。

ジェネリックに似た動作をクラスで再現

編集

PHPでジェネリックのような動作を模倣するには、たとえば型に依存しないクラスを作成し、特定の型を扱う場合は動的に決定することが考えられます。

例: ArrayCollection クラス

編集

異なる型のデータを格納するための配列操作をジェネリックに扱いたい場合、以下のようなクラスを作成できます。

class ArrayCollection {
    private array $items = [];

    public function add($item): void {
        $this->items[] = $item;
    }

    public function get(int $index) {
        return $this->items[$index];
    }

    public function getAll(): array {
        return $this->items;
    }
}

このクラスはどのような型でも add メソッドに渡せるため、異なる型の要素を格納できます。しかし、型の安全性が保証されていないため、運用時の型チェックが必要です。

型安全なバージョン

編集

PHP 7.4 以降、型指定を行うことで少し型安全な実装が可能です。たとえば、整数専用の ArrayCollection を作成したい場合は、以下のようにします。

class IntCollection {
    private array $items = [];

    public function add(int $item): void {
        $this->items[] = $item;
    }

    public function get(int $index): int {
        return $this->items[$index];
    }

    public function getAll(): array {
        return $this->items;
    }
}

これにより、add メソッドには整数しか渡せず、型の安全性が強化されます。

PHP 8.1以降の「型付き引数」と「交差型 (Intersection Types)」

編集

PHP 8.1 以降では、「交差型」を使って、複数の型を組み合わせたジェネリック的な機能を実装できます。これは、あるクラスやインターフェイスが複数の型に対応できることを意味します。

例: インターフェースとクラスの交差型

編集
interface Cacheable {
    public function cache(): void;
}

interface Loggable {
    public function log(): void;
}

class MyCacheLogger implements Cacheable, Loggable {
    public function cache(): void {
        echo "Caching...";
    }

    public function log(): void {
        echo "Logging...";
    }
}

function handle(Cacheable&Loggable $object): void {
    $object->cache();
    $object->log();
}

$object = new MyCacheLogger();
handle($object);

この例では、handle 関数は CacheableLoggable の両方のインターフェースを実装しているオブジェクトを受け取ることを要求します。これにより、両方の機能を持つオブジェクトでのみ呼び出すことができ、ある意味ジェネリックな動作に似た柔軟性が得られます。

Traitを利用したジェネリックプログラミング風の実装

編集

PHPのTraitを活用することで、コードの再利用性を高め、ジェネリック的な挙動を実現することも可能です。

例: Traitを用いた汎用的な操作

編集
trait Sortable {
    public function sortArray(array $array): array {
        sort($array);
        return $array;
    }
}

class NumberCollection {
    use Sortable;

    private array $numbers;

    public function __construct(array $numbers) {
        $this->numbers = $numbers;
    }

    public function getSortedNumbers(): array {
        return $this->sortArray($this->numbers);
    }
}

$collection = new NumberCollection([4, 2, 8, 1]);
print_r($collection->getSortedNumbers());

この例では、Sortable Traitを利用して配列をソートする機能をどのクラスでも再利用できるようにしています。これにより、異なるクラスで共通のジェネリック的な操作を実現できます。

ジェネリックな関数

編集

PHPは「弱い型付け」の特徴を持っており、汎用的な操作を簡単に記述できます。例えば、以下のように任意の型の配列を操作する関数を作ることができます。

function printItems(array $items): void {
    foreach ($items as $item) {
        echo $item . "\n";
    }
}

printItems([1, 2, 3]);     // 数字の配列
printItems(['a', 'b', 'c']); // 文字列の配列

この関数は、渡された配列の要素をすべて出力するもので、整数でも文字列でも動作します。

まとめ

編集

PHPには、C++ や Java のような公式のジェネリック構文はありませんが、型ヒントや交差型、トレイトを駆使することで、ある程度のジェネリック的なプログラミングを実現できます。また、動的型付けの柔軟性を活かし、型の制約が少ない形で汎用的な関数やクラスを設計することも可能です。