オブジェクト インターフェイス
オブジェクト インターフェイスを使うと、 メソッドの実装を定義せずに、 クラスが実装する必要があるメソッドを指定するコードを作成できます。 インターフェイス は クラス や トレイト と名前空間を共有するので、 それらと同じ名前を使ってはいけません。
インターフェイスは通常のクラスと同様に定義することができますが、
キーワード class
のかわりに interface
を用います。またメソッドの実装は全く定義しません。
インターフェイス内で宣言される全てのメソッドは public である必要があります。 これは、インターフェイスの特性によります。
インターフェイスには、ふたつの互いを補完する役割があります。
- 同じインターフェイスを実装していることで、 開発者が交換可能な異なるクラスを作成できるようにします。 同じインターフェイスを持つクラスによくある例として、 複数のデータベースにアクセスするサービスや 決済のゲートウェイ、 異なるキャッシュ戦略が挙げられます。 実装が異なっていても、 それを使うコードに変更を加えることなく、それらを交換することができます。
-
メソッドや関数が、インターフェイスを満たす引数を受け付け、
操作できるようにします。
オブジェクトが何をするのかや、
どう実装されているのかを気にする必要はありません。
振る舞いの重要性を説明するために、
Iterable
やCacheable
、Renderable
のような名前が付けられることがよくあります。
インターフェイスは、 マジックメソッド を宣言しても問題ありません。
注意:
コンストラクタ をインターフェイスで宣言できますが、全くおすすめできません。 そうしてしまうと、 インターフェイスを実装するクラスの柔軟性が大きく損なわれる上、 コンストラクタには継承ルールが適用されないため、 一貫しない、予期せぬ結果を生む可能性があるからです。
implements
インターフェイスを実装するには、implements
演算子を使用し、
このインターフェイスに含まれる全てのメソッドを実装する必要があります。
実装されていない場合、致命的エラーとなります。
各インターフェイスをカンマで区切って指定することで、
クラスは複数のインターフェイスを実装することができます。
インターフェイスを実装するクラスでは、 インターフェイス内の名前とは異なる名前を メソッドの引数に使うことができます。 しかし、PHP 8.0 以降では 名前付き引数 がサポートされています。 これは、メソッドをコールする側がインターフェイス内の名前に依存する可能性があるということです。 そうした理由から、開発者は実装されるインターフェイスと同じ引数名を使うことを強く推奨します。
注意:
クラスと同様、インターフェイスも extends 演算子で継承することができます。
注意:
インターフェイスを実装したクラスでは、 シグネチャの互換性に関するルール を守った形で、インターフェイス内の全てのメソッドを宣言しなければいけません。 クラスは、同じ名前のメソッドを定義した複数のインターフェイスを実装することが出来ます。 この場合、実装されるメソッドは全て、 シグネチャの互換性に関するルール に従わなければいけません。 そうすることで、共変性と反変性 のルールも適用できます。
定数
インターフェイスに定数を持たせることもできます。 インターフェイス定数は クラス定数 とまったく同じように動作します。 PHP 8.1.0 より前のバージョンでは、 そのインターフェイスを継承したクラスやインターフェイスから定数をオーバーライドすることができませんでした。
例
例1 Interface の例
<?php
// インターフェイス 'Template' を宣言する
interface Template
{
public function setVariable($name, $var);
public function getHtml($template);
}
// インターフェイスを実装する。
// これは動作します。
class WorkingTemplate implements Template
{
private $vars = [];
public function setVariable($name, $var)
{
$this->vars[$name] = $var;
}
public function getHtml($template)
{
foreach($this->vars as $name => $value) {
$template = str_replace('{' . $name . '}', $value, $template);
}
return $template;
}
}
// これは動作しません。
// Fatal error: Class BadTemplate contains 1 abstract methods
// and must therefore be declared abstract (Template::getHtml)
class BadTemplate implements Template
{
private $vars = [];
public function setVariable($name, $var)
{
$this->vars[$name] = $var;
}
}
?>
例2 インターフェイスの継承
<?php
interface A
{
public function foo();
}
interface B extends A
{
public function baz(Baz $baz);
}
// これは動作します。
class C implements B
{
public function foo()
{
}
public function baz(Baz $baz)
{
}
}
// これは動作せず、fatal error となります。
class D implements B
{
public function foo()
{
}
public function baz(Foo $foo)
{
}
}
?>
例3 共変性を保った形で、複数のインターフェイスを実装する
<?php
class Foo {}
class Bar extends Foo {}
interface A {
public function myfunc(Foo $arg): Foo;
}
interface B {
public function myfunc(Bar $arg): Bar;
}
class MyClass implements A, B
{
public function myfunc(Foo $arg): Bar
{
return new Bar();
}
}
?>
例4 複数のインターフェイスの継承
<?php
interface A
{
public function foo();
}
interface B
{
public function bar();
}
interface C extends A, B
{
public function baz();
}
class D implements C
{
public function foo()
{
}
public function bar()
{
}
public function baz()
{
}
}
?>
例5 インターフェイスでの定数
<?php
interface A
{
const B = 'Interface constant';
}
// Interface constant と表示します。
echo A::B;
class B implements A
{
const B = 'Class constant';
}
// Class constant と表示します。
// PHP 8.1.0 より前のバージョンでは、定数をオーバライドできなかったため、これは動作しませんでした。
echo B::B;
?>
例6 抽象クラスとインターフェイス
<?php
interface A
{
public function foo(string $s): string;
public function bar(int $i): int;
}
// 抽象クラスは、インターフェイスの一部のみを実装しても構いません。
// 抽象クラスを継承したクラスは、未実装の残りのメソッドを実装しなければなりません。
abstract class B implements A
{
public function foo(string $s): string
{
return $s . PHP_EOL;
}
}
class C extends B
{
public function bar(int $i): int
{
return $i * 2;
}
}
?>
例7 継承と実装を同時に行う
<?php
class One
{
/* ... */
}
interface Usable
{
/* ... */
}
interface Updatable
{
/* ... */
}
// ここでは、キーワードの順番が重要です。
// 'extends' を始めに置かなければいけません。
class Two extends One implements Usable, Updatable
{
/* ... */
}
?>
インターフェイスと型宣言を組み合わせると、 特定のオブジェクトに特定のメソッドをうまく持たせることができます。 instanceof 演算子および 型宣言 を参照ください。
User Contributed Notes 4 notes
PHP prevents interface a contant to be overridden by a class/interface that DIRECTLY inherits it. However, further inheritance allows it. That means that interface constants are not final as mentioned in a previous comment. Is this a bug or a feature?
<?php
interface a
{
const b = 'Interface constant';
}
// Prints: Interface constant
echo a::b;
class b implements a
{
}
// This works!!!
class c extends b
{
const b = 'Class constant';
}
echo c::b;
?>
Just as all interface methods are public, all interface methods are abstract as well.
In their book on Design Patterns, Erich Gamma and his associates (AKA: "The Gang of Four") use the term "interface" and "abstract class" interchangeably. In working with PHP and design patterns, the interface, while clearly a "contract" of what to include in an implementation is also a helpful guide for both re-use and making changes. As long as the implemented changes follow the interface (whether it is an interface or abstract class with abstract methods), large complex programs can be safely updated without having to re-code an entire program or module.
In PHP coding with object interfaces (as a keyword) and "interfaces" in the more general context of use that includes both object interfaces and abstract classes, the purpose of "loose binding" (loosely bound objects) for ease of change and re-use is a helpful way to think about both uses of the term "interface." The focus shifts from "contractual" to "loose binding" for the purpose of cooperative development and re-use.
This page says that if extending multiple interfaces with the same methods, the signature must be compatible. But this is not all there is to it: the order of `extends` matters. This is a known issue, and while it is disputable whether or not it is a bug, one should be aware of it, and code interfaces with this in mind.
https://bugs.php.net/bug.php?id=67270
https://bugs.php.net/bug.php?id=76361
https://bugs.php.net/bug.php?id=80785