PHP 抽象類別 (Abstract Class)

抽象類別是不能被實例化的類別,用來作為其他類別的基礎。抽象類別可以包含抽象方法(沒有實作)和一般方法。

定義抽象類別

<?php
abstract class Shape {
    abstract public function area(): float;
    abstract public function perimeter(): float;

    public function describe(): string {
        return "這是一個形狀";
    }
}

class Rectangle extends Shape {
    public function __construct(
        private float $width,
        private float $height
    ) {}

    public function area(): float {
        return $this->width * $this->height;
    }

    public function perimeter(): float {
        return 2 * ($this->width + $this->height);
    }
}

// $shape = new Shape();  // 錯誤:不能實例化抽象類別
$rect = new Rectangle(5, 3);
echo $rect->area();       // 15
echo $rect->perimeter();  // 16
?>

抽象方法

抽象方法只有簽名,沒有實作:

<?php
abstract class Database {
    abstract public function connect(): void;
    abstract public function query(string $sql): array;
    abstract public function close(): void;

    // 非抽象方法可以有實作
    public function execute(string $sql): bool {
        $this->query($sql);
        return true;
    }
}

class MySQLDatabase extends Database {
    private $connection;

    public function connect(): void {
        $this->connection = mysqli_connect('localhost', 'user', 'pass', 'db');
    }

    public function query(string $sql): array {
        $result = mysqli_query($this->connection, $sql);
        return mysqli_fetch_all($result, MYSQLI_ASSOC);
    }

    public function close(): void {
        mysqli_close($this->connection);
    }
}
?>

抽象屬性鉤子 (Abstract Property Hooks) (PHP 8.4+)

PHP 8.4 之後,抽象類別也可以定義「抽象屬性鉤子」,要求子類別必須實作特定的讀取或寫入邏輯。

<?php
abstract class User {
    // 要求子類別必須實作 name 的 get 鉤子
    abstract public string $name { get; }
}

class RegisteredUser extends User {
    public function __construct(
        private string $fullName
    ) {}

    public string $name {
        get => $this->fullName;
    }
}
?>

抽象類別 vs 介面

特性抽象類別介面
實例化不可不可
方法實作可以有不可(PHP 8+ 可有預設)
屬性可以有只能有常數
建構子可以有不可
繼承單一多重
存取修飾符可以有預設 public
<?php
// 抽象類別:共享實作
abstract class Animal {
    protected string $name;

    public function __construct(string $name) {
        $this->name = $name;
    }

    abstract public function speak(): string;

    public function getName(): string {
        return $this->name;
    }
}

// 介面:定義能力
interface Swimmable {
    public function swim(): void;
}

class Duck extends Animal implements Swimmable {
    public function speak(): string {
        return "Quack!";
    }

    public function swim(): void {
        echo "{$this->name} is swimming\n";
    }
}
?>

模板方法模式

<?php
abstract class DataExporter {
    // 模板方法
    final public function export(array $data): string {
        $this->validate($data);
        $formatted = $this->format($data);
        return $this->output($formatted);
    }

    protected function validate(array $data): void {
        if (empty($data)) {
            throw new InvalidArgumentException("Data cannot be empty");
        }
    }

    abstract protected function format(array $data): string;
    abstract protected function output(string $content): string;
}

class JsonExporter extends DataExporter {
    protected function format(array $data): string {
        return json_encode($data);
    }

    protected function output(string $content): string {
        return $content;
    }
}

class CsvExporter extends DataExporter {
    protected function format(array $data): string {
        $output = [];
        foreach ($data as $row) {
            $output[] = implode(',', $row);
        }
        return implode("\n", $output);
    }

    protected function output(string $content): string {
        return $content;
    }
}
?>

實際範例

<?php
abstract class Controller {
    protected array $data = [];

    abstract protected function handle(): void;

    public function execute(): string {
        $this->before();
        $this->handle();
        $this->after();
        return $this->render();
    }

    protected function before(): void {
        // 前置處理
    }

    protected function after(): void {
        // 後置處理
    }

    protected function render(): string {
        return json_encode($this->data);
    }
}

class UserController extends Controller {
    protected function handle(): void {
        $this->data = [
            'users' => [
                ['id' => 1, 'name' => 'Alice'],
                ['id' => 2, 'name' => 'Bob'],
            ]
        ];
    }
}

$controller = new UserController();
echo $controller->execute();
?>