PHPのインターフェース(interface)は、クラスが実装すべきメソッドの「契約」を定義する仕組みです。インターフェース自体には処理の実装は含まれず、メソッドのシグネチャ(名前・引数・戻り値の型)だけを定義します。
クラスは implements でインターフェースを実装し、定義されたすべてのメソッドを実装する義務を負います。抽象クラスと異なり、1つのクラスが複数のインターフェースを実装できるのが大きな特徴です。
基本的な使い方
PHP
<?php
interface Printable {
public function toString(): string;
}
interface Calculatable {
public function calculate(): float;
}
class Invoice implements Printable, Calculatable {
public function __construct(
private string $customerName,
private array $items // [["name" => ..., "price" => ..., "qty" => ...], ...]
) {}
public function toString(): string {
$lines = ["請求書: {$this->customerName}"];
foreach ($this->items as $item) {
$lines[] = " {$item['name']} x{$item['qty']} = " . ($item['price'] * $item['qty']) . "円";
}
$lines[] = " 合計: {$this->calculate()}円";
return implode("\n", $lines);
}
public function calculate(): float {
$total = 0;
foreach ($this->items as $item) {
$total += $item['price'] * $item['qty'];
}
return $total;
}
}
$invoice = new Invoice("田中商事", [
["name" => "ノートPC", "price" => 80000, "qty" => 2],
["name" => "マウス", "price" => 3000, "qty" => 5],
]);
echo $invoice->toString() . "\n";
実行結果
請求書: 田中商事
ノートPC x2 = 160000円
マウス x5 = 15000円
合計: 175000円
Invoice クラスは Printable と Calculatable の2つのインターフェースを同時に実装しています。すべてのメソッドを実装しなければコンパイルエラーになります。
型としてのインターフェース
インターフェースは型宣言に使えます。異なるクラスでも同じインターフェースを実装していれば、同じ型として扱えます。
PHP
<?php
interface Logger {
public function log(string $message): void;
}
class ConsoleLogger implements Logger {
public function log(string $message): void {
echo "[CONSOLE] {$message}\n";
}
}
class FileLogger implements Logger {
public function log(string $message): void {
echo "[FILE] {$message}(ファイルに書き込み)\n";
}
}
// インターフェース型で受け取る
function processOrder(string $item, Logger $logger): void {
$logger->log("注文受付: {$item}");
$logger->log("在庫確認中...");
$logger->log("注文確定: {$item}");
}
echo "--- コンソール出力 ---\n";
processOrder("PHP入門書", new ConsoleLogger());
echo "\n--- ファイル出力 ---\n";
processOrder("PHP入門書", new FileLogger());
実行結果
--- コンソール出力 ---
[CONSOLE] 注文受付: PHP入門書
[CONSOLE] 在庫確認中...
[CONSOLE] 注文確定: PHP入門書
--- ファイル出力 ---
[FILE] 注文受付: PHP入門書(ファイルに書き込み)
[FILE] 在庫確認中...(ファイルに書き込み)
[FILE] 注文確定: PHP入門書(ファイルに書き込み)
関数 processOrder() は Logger インターフェース型で受け取るため、ConsoleLogger でも FileLogger でも動作します。実装の切り替えが容易です。
インターフェースの継承
PHP
<?php
interface Readable {
public function read(): string;
}
interface Writable {
public function write(string $data): void;
}
// インターフェース同士の継承(複数可)
interface ReadWritable extends Readable, Writable {
public function seek(int $position): void;
}
class MemoryStream implements ReadWritable {
private string $buffer = "";
private int $position = 0;
public function read(): string {
return substr($this->buffer, $this->position);
}
public function write(string $data): void {
$this->buffer .= $data;
}
public function seek(int $position): void {
$this->position = $position;
}
}
$stream = new MemoryStream();
$stream->write("Hello, ");
$stream->write("PHP!");
echo $stream->read() . "\n";
$stream->seek(7);
echo $stream->read() . "\n";
実行結果
Hello, PHP!
PHP!
実用的な例
PHP
<?php
interface Repository {
public function findById(int $id): ?array;
public function findAll(): array;
public function save(array $data): bool;
}
class InMemoryUserRepository implements Repository {
private array $users = [];
private int $nextId = 1;
public function findById(int $id): ?array {
return $this->users[$id] ?? null;
}
public function findAll(): array {
return array_values($this->users);
}
public function save(array $data): bool {
$data["id"] = $this->nextId++;
$this->users[$data["id"]] = $data;
return true;
}
}
$repo = new InMemoryUserRepository();
$repo->save(["name" => "田中", "age" => 30]);
$repo->save(["name" => "佐藤", "age" => 25]);
$user = $repo->findById(1);
echo "ID=1: {$user['name']}\n";
$all = $repo->findAll();
echo "全件: " . count($all) . "件\n";
実行結果
ID=1: 田中
全件: 2件
インターフェースと抽象クラスの使い分け
インターフェース:契約のみ定義。複数実装可能。「何ができるか」を定義する。
抽象クラス:共通処理も提供。単一継承のみ。「何であるか」を定義する。
両方を組み合わせて使うことも多いです。
注意
インターフェースのメソッドは必ず public でなければなりません。また、プロパティは定義できませんが、定数は定義可能です(PHP 8.1以降)。
まとめ
interfaceはメソッドの契約(シグネチャのみ)を定義するimplementsで実装し、定義されたすべてのメソッドを実装する義務がある- 1つのクラスが複数のインターフェースを実装できる
- 型宣言に使うことで、実装の差し替えが容易な柔軟な設計ができる
- 抽象クラスとは目的が異なり、組み合わせて使うことも多い