PHP 介面 (Interface)

介面定義了類別必須實作的方法,但不包含實作內容。介面用於建立契約,確保類別具有特定的行為。

定義介面

<?php
interface Printable {
    public function print(): string;
}

class Document implements Printable {
    public function __construct(
        private string $content
    ) {}

    public function print(): string {
        return $this->content;
    }
}

class Image implements Printable {
    public function __construct(
        private string $path
    ) {}

    public function print(): string {
        return "[Image: {$this->path}]";
    }
}
?>

實作多個介面

<?php
interface Readable {
    public function read(): string;
}

interface Writable {
    public function write(string $data): void;
}

interface Closeable {
    public function close(): void;
}

class File implements Readable, Writable, Closeable {
    private $handle;

    public function __construct(string $path, string $mode) {
        $this->handle = fopen($path, $mode);
    }

    public function read(): string {
        return fread($this->handle, 1024);
    }

    public function write(string $data): void {
        fwrite($this->handle, $data);
    }

    public function close(): void {
        fclose($this->handle);
    }
}
?>

介面繼承

介面可以繼承其他介面:

<?php
interface Countable {
    public function count(): int;
}

interface Collection extends Countable {
    public function add($item): void;
    public function remove($item): void;
    public function contains($item): bool;
}

class ArrayList implements Collection {
    private array $items = [];

    public function count(): int {
        return count($this->items);
    }

    public function add($item): void {
        $this->items[] = $item;
    }

    public function remove($item): void {
        $key = array_search($item, $this->items);
        if ($key !== false) {
            unset($this->items[$key]);
        }
    }

    public function contains($item): bool {
        return in_array($item, $this->items);
    }
}
?>

介面常數

介面可以包含常數:

<?php
interface Status {
    public const PENDING = 'pending';
    public const ACTIVE = 'active';
    public const COMPLETED = 'completed';
}

class Task implements Status {
    private string $status = self::PENDING;

    public function activate(): void {
        $this->status = self::ACTIVE;
    }
}

echo Status::PENDING;  // pending
?>

型別提示

介面可以作為型別提示:

<?php
interface Logger {
    public function log(string $message): void;
}

class ConsoleLogger implements Logger {
    public function log(string $message): void {
        echo "[LOG] $message\n";
    }
}

class FileLogger implements Logger {
    public function log(string $message): void {
        file_put_contents('app.log', $message . "\n", FILE_APPEND);
    }
}

class Application {
    public function __construct(
        private Logger $logger
    ) {}

    public function run(): void {
        $this->logger->log("Application started");
    }
}

// 可以注入任何實作 Logger 的物件
$app = new Application(new ConsoleLogger());
$app->run();
?>

內建介面

PHP 提供了許多內建介面:

<?php
// Iterator - 可迭代
class NumberRange implements Iterator {
    private int $current;

    public function __construct(
        private int $start,
        private int $end
    ) {
        $this->current = $start;
    }

    public function current(): int { return $this->current; }
    public function key(): int { return $this->current - $this->start; }
    public function next(): void { $this->current++; }
    public function rewind(): void { $this->current = $this->start; }
    public function valid(): bool { return $this->current <= $this->end; }
}

foreach (new NumberRange(1, 5) as $num) {
    echo $num;  // 1 2 3 4 5
}

// Countable - 可計數
class Collection implements Countable {
    private array $items = [];

    public function count(): int {
        return count($this->items);
    }
}

// ArrayAccess - 陣列存取
class Config implements ArrayAccess {
    private array $data = [];

    public function offsetExists($offset): bool {
        return isset($this->data[$offset]);
    }

    public function offsetGet($offset): mixed {
        return $this->data[$offset] ?? null;
    }

    public function offsetSet($offset, $value): void {
        $this->data[$offset] = $value;
    }

    public function offsetUnset($offset): void {
        unset($this->data[$offset]);
    }
}

$config = new Config();
$config['debug'] = true;
echo $config['debug'];  // 1
?>

介面 vs 抽象類別

特性介面抽象類別
實作可以有
屬性只能有常數可以有
建構子可以有
多重繼承支援不支援

// 使用介面:定義能力 interface Flyable { public function fly(): void; }

interface Swimmable { public function swim(): void; }

class Duck implements Flyable, Swimmable { public function fly(): void { echo "Flying\n"; } public function swim(): void { echo "Swimming\n"; } } ?>

介面常數的覆寫 (PHP 8.1+)

在 PHP 8.1 之後,類別可以覆寫介面中定義的常數,這讓介面的定義更具彈性。

<?php
interface Config {
    public const VERSION = '1.0';
}

class AppConfig implements Config {
    public const VERSION = '2.0'; // PHP 8.1 之前會報錯
}

echo AppConfig::VERSION; // 2.0
?>