PHPのインターフェイス

インターフェイスとは、クラスに特定のメソッドの実装を義務づけるとともに、そのクラスがそれらのメソッドを持っていることを保証するオブジェクト指向の仕組みです。機能的には抽象クラス(アブストラクトクラス)に似ていますが、インターフェイスは継承関係に縛られることなく複数のクラスに共通の仕様と資格を与えることができます。

インターフェイスの定義

インターフェイスは、interfaceキーワードを使って定義します。インターフェイスに定義するメソッドは、実装部分を記述せずに宣言だけを行います。メソッドのアクセス修飾子は、publicにする必要があります。

PHP

<?php
interface IPlayer
{
  public function play();
}
interface IWorker
{
  public function work($where);
}
?>

インターフェイスの実装

インターフェイスは、implementsキーワードを使って実装します。extendsと違い、複数のインターフェイスを同時に実装することができます。インターフェイスを実装したクラスでは、インターフェイスに定義されているメソッドを必ず実装する必要があります。その際、引数も同じにする必要がありますが、デフォルト値を持つ引数を追加することは許容されます。

PHP

<?php
class Foo implements IPlayer, IWorker
{
  private $name = 'Foo';
  public function play($what = 'tennis')
  {
    echo $this->name . ' is playing ' . $what . '.', PHP_EOL;
  }
  public function work($where)
  {
    echo $this->name . ' is working in ' . $where . '.', PHP_EOL;
  }
}
$foo = new Foo();
$foo->play('baseball'); // 出力:Foo is playing baseball.
$foo->work('office'); // 出力:Foo is working in office.
?>

インターフェイスの継承

インターフェイスは、クラスと同様にextendsで継承して定義することができます。インターフェイスの場合は、クラスの継承と違い、複数のインターフェイスを同時に継承できます。

下記の例でPiyoクラスは、implementsしたIBarインターフェイスのメソッドは実装していますが、IBarインターフェイスの継承元であるIFoo、IHogeインターフェイスのメソッドを実装していないのでエラーになります。

PHP

<?php
interface IFoo
{
  public function foo();
}
interface IHoge
{
  public function hoge();
}
interface IBar extends IFoo, IHoge
{
  public function bar();
}
class Piyo implements IBar
{
  public function bar()
  {
    echo 'bar', PHP_EOL;
  }
}
/* 出力:
Fatal error: Class Piyo contains 2 abstract methods 
and must therefore be declared abstract or implement the remaining methods 
(IFoo::foo, IHoge::hoge)
*/
?>

インターフェイス定数

クラスと同様にインターフェイスにも定数を定義することができます。インターフェイス定数は、実装したクラスや継承したインターフェイスから上書きすることはできません。

PHP

<?php
interface IFoo
{
  const name = 'IFoo';
  public function foo();
}
class Hoge implements IFoo
{
  public function foo()
  {
    echo IFoo::name, PHP_EOL;
  }
}
$hoge = new Hoge();
$hoge->foo(); // 出力:IFoo
?>

タイプヒンティングでの型指定

インターフェイスは、タイプヒンティングの型に指定することができます。下記の例では、doTask関数の引数にタイプヒンティングでIWorkerを指定しています。結果として、IWorkerインターフェイスを実装していないBarクラスのインスタンスを渡した場合はエラーになります。

一方、IWorkerインターフェイスを実装したFooクラスとHogeクラスのインスタンスではエラーになりません。このように、インターフェイスを利用することで、継承関係のない別のクラスのインスタンスの機能を保証し、共通の資格を与えることができます。

PHP

<?php
interface IWorker
{
  public function work();
}
class Foo implements IWorker
{
  public function work()
  {
    echo "Foo's work.", PHP_EOL;
  }
}
class Hoge implements IWorker
{
  public function work()
  {
    echo "Hoge's work.", PHP_EOL;
  }
}
class Bar
{
  public function work()
  {
    echo "Bar's work.", PHP_EOL;
  }
}
function doTask(IWorker $worker)
{
  $worker->work();
}
$foo = new Foo();
doTask($foo); // 出力:Foo's work.
$hoge = new Hoge();
doTask($hoge); // 出力:Hoge's work.
$bar = new Bar();
doTask($bar); // 出力:Catchable fatal error: Argument 1 passed to doTask() must implement interface IWorker
?>

型演算子(instanceof)による判定

型演算子(instanceof)を使用することにより、オブジェクトが特定のインターフェイスを実装しているかを調べることができます。インターフェイスを実装していればtrue、実装していなければfalseが返されます。

PHP

<?php
interface IWorker
{
  public function work();
}
class Foo implements IWorker
{
  public function work()
  {
    echo "Foo's work.", PHP_EOL;
  }
}
class Hoge
{
  public function work()
  {
    echo "Hoge's work.", PHP_EOL;
  }
}
$foo = new Foo();
$hoge = new Hoge();
var_dump($foo instanceof IWorker); // 出力:bool(true)
var_dump($hoge instanceof IWorker); // 出力:bool(false)
?>