オブジェクト インターフェイス

オブジェクト インターフェイスを使うと、 メソッドの実装を定義せずに、 クラスが実装する必要があるメソッドを指定するコードを作成できます。 インターフェイス は クラス や トレイト と名前空間を共有するので、 それらと同じ名前を使ってはいけません。

インターフェイスは通常のクラスと同様に定義することができますが、 キーワード class のかわりに interface を用います。またメソッドの実装は全く定義しません。

インターフェイス内で宣言される全てのメソッドは public である必要があります。 これは、インターフェイスの特性によります。

インターフェイスには、ふたつの互いを補完する役割があります。

  • 同じインターフェイスを実装していることで、 開発者が交換可能な異なるクラスを作成できるようにします。 同じインターフェイスを持つクラスによくある例として、 複数のデータベースにアクセスするサービスや 決済のゲートウェイ、 異なるキャッシュ戦略が挙げられます。 実装が異なっていても、 それを使うコードに変更を加えることなく、それらを交換することができます。
  • メソッドや関数が、インターフェイスを満たす引数を受け付け、 操作できるようにします。 オブジェクトが何をするのかや、 どう実装されているのかを気にする必要はありません。 振る舞いの重要性を説明するために、 IterableCacheableRenderable のような名前が付けられることがよくあります。

インターフェイスは、 マジックメソッド を宣言しても問題ありません。

注意:

コンストラクタ をインターフェイスで宣言できますが、全くおすすめできません。 そうしてしまうと、 インターフェイスを実装するクラスの柔軟性が大きく損なわれる上、 コンストラクタには継承ルールが適用されないため、 一貫しない、予期せぬ結果を生む可能性があるからです。

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 演算子および 型宣言 を参照ください。