Menu

PHPのインターフェース:implements、定数と例

PHPのインターフェースは、クラスが持つと約束するメソッドを並べたものです:interface Shape { public function area(): float; }とし、class Circle implements Shapeはarea()を定義しなければなりません。インターフェースによる型宣言、複数のインターフェースの実装、インターフェースの定数、インターフェースの継承、Countableなどの組み込みインターフェース、インターフェースと抽象クラスの違いを学びます。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

PHPのインターフェースは、クラスが提供すると約束するメソッドを、コードなしで宣言します:interface Shape { public function area(): float; }。implements Shapeと書いたクラスはarea()を定義しなければならず、Shapeで型宣言した関数はどれもそのクラスを受け付けます。

describe()はどのクラスを受け取るかを知りませんし、気にもしません。インターフェースの約束だけに頼っているので、新しいTriangle implements Shapeも1行も変えずに使えます。

インターフェースに対してプログラムする

インターフェースを書くよくある理由は、コードが特定のクラスではなく能力に依存できるようにすることです。ここでは注文サービスがどんなNotifierでも受け付けるので、同じコードで本番ではメールを送り、テストではメッセージを集められます。

複数のインターフェースを実装する

クラスが継承できる親は1つだけですが、インターフェースは必要なだけ実装できます。implementsのあとにカンマで区切って並べ、そのすべてのメソッドを定義します。

JsonSerializableはPHPの組み込みインターフェースの1つで、実装するとそのオブジェクトに対してjson_encode()が何を出力するかを制御できます。

インターフェースの定数

インターフェースは定数を宣言できます。実装するクラスはすべてそれを受け継ぎ、インターフェース名でもクラス名でも読めます。

PHP 8.3では、インターフェースが持てるのはメソッドと定数だけで、プロパティは持てません。メソッドはstaticでもかまいません。インターフェースにpublic static function fromArray(array $data): static;と書けば、各クラスにその静的メソッドの定義を義務づけられます。関連する決まった値の集合には、インターフェースの定数を並べるよりenumのほうが合っていることがよくあります。

インターフェースはほかのインターフェースを継承できる

インターフェースもextendsを使い、クラスと違って一度に複数を継承できます。子のインターフェースを実装するクラスは、連鎖全体のメソッドを定義しなければなりません。

組み込みインターフェース:Countable、IteratorAggregate、Stringable

PHPには、オブジェクトを言語の機能に結びつけるインターフェースが用意されています。Countableはcount($obj)を、IteratorAggregateはforeachを動くようにし、Stringableは__toString()メソッドを持つすべてのクラスで自動的に実装されます。

メソッドの書き忘れ

インターフェースを実装すると宣言したクラスがメソッドを1つでも欠いていると、PHPは足りないメソッドの名前を示す致命的エラーで止まります。

total()メソッドを加えればスクリプトは動きます。抽象クラスならインターフェースのメソッドを未定義のままにでき、これがエラーメッセージの示す逃げ道です。

インターフェースと抽象クラスの違い

どちらもクラスが持つべきメソッドを定義し、どちらも型宣言に使えます。違いは、ほかに何を含められるかと、1つのクラスがいくつ使えるかです。

インターフェース抽象クラス
メソッドの本体なし(シグネチャのみ)抽象メソッドと完成したメソッド
プロパティなしあり
コンストラクタ宣言できるがあまり役に立たないあり、コード付き
メソッドの可視性publicのみpublic、protected、private
定数ありあり
1クラスあたりの数いくつでも(implements A, B, C)1つ(extends Base)
典型的な用途能力:Exportable、Countableファミリーの共有コード:Model、Controller

2つはうまく組み合わせられます。型宣言用にインターフェースを定義し、共通のコードでそれを実装する抽象基底クラスを用意します。別の基底クラスが必要なクラスは、今まで通りインターフェースを直接実装できます。

よくある質問

PHPのインターフェースとは何ですか?

本体のないpublicなメソッドのシグネチャの一覧です:interface Logger { public function log(string $msg): void; }。implements Loggerと書いたクラスはその中のメソッドをすべて定義しなければならず、Loggerで型宣言したコードはどれもそのクラスを受け付けます。

PHPのクラスは複数のインターフェースを実装できますか?

できます。implementsのあとにカンマで区切って並べます:class File implements Readable, Writable, Countable。クラスはそのすべてのメソッドを定義する必要があります。ほかの言語で多重継承がしていることを、PHPはこの方法でまかないます。

PHPのインターフェースと抽象クラスの違いは何ですか?

インターフェースはpublicなメソッド(と定数)を宣言するだけで、プロパティもメソッドのコードも持たず、クラスはいくつでも実装できます。抽象クラスは抽象メソッドの隣にプロパティ、コンストラクタ、完成したメソッドを持てますが、クラスが継承できるのは1つだけです。能力(「エクスポートできる」)にはインターフェースを、クラスのファミリーで共有するコードには抽象クラスを使います。

PHPのインターフェースはプロパティを持てますか?

PHP 8.3では持てません。インターフェースに書けるのはメソッドのシグネチャと定数(const VERSION = 2;)だけで、プロパティは書けません。インターフェースでのプロパティの宣言(プロパティフック)はPHP 8.4で登場しました。

PHPでオブジェクトがインターフェースを実装しているか確認するには?

instanceofを使います:オブジェクトのクラスがそのインターフェースを直接、または親クラスを通して実装していれば、$obj instanceof Countableはtrueです。class_implements($obj)は、クラスが実装しているすべてのインターフェースの名前を返します。

Coddyのプログラミング言語のイラスト

Coddyでコードを学ぼう

始める