PHP 繼承 (Inheritance)

繼承讓類別可以繼承另一個類別的屬性和方法,實現程式碼重用和建立類別層級結構。

基本繼承

使用 extends 關鍵字繼承類別:

<?php
class Animal {
    public string $name;
    
    public function __construct(string $name) {
        $this->name = $name;
    }
    
    public function speak(): string {
        return "...";
    }
}

class Dog extends Animal {
    public function speak(): string {
        return "Woof!";
    }
}

class Cat extends Animal {
    public function speak(): string {
        return "Meow!";
    }
}

$dog = new Dog("Buddy");
echo $dog->name;     // Buddy
echo $dog->speak();  // Woof!
?>

parent 關鍵字

使用 parent:: 呼叫父類別的方法:

<?php
class Animal {
    public function __construct(
        public string $name
    ) {}
    
    public function describe(): string {
        return "這是 {$this->name}";
    }
}

class Dog extends Animal {
    public function __construct(
        string $name,
        public string $breed
    ) {
        parent::__construct($name);
    }
    
    public function describe(): string {
        return parent::describe() . ",品種是 {$this->breed}";
    }
}

$dog = new Dog("Buddy", "Golden Retriever");
echo $dog->describe();
// 這是 Buddy,品種是 Golden Retriever
?>

方法覆寫 (Override)

子類別可以覆寫父類別的方法:

<?php
class Shape {
    public function area(): float {
        return 0;
    }
}

class Rectangle extends Shape {
    public function __construct(
        public float $width,
        public float $height
    ) {}
    
    public function area(): float {
        return $this->width * $this->height;
    }
}

class Circle extends Shape {
    public function __construct(
        public float $radius
    ) {}
    
    public function area(): float {
        return M_PI * $this->radius ** 2;
    }
}

$rect = new Rectangle(5, 3);
echo $rect->area();  // 15

$circle = new Circle(5);
echo $circle->area();  // 78.54...
?>

final 關鍵字

final 可以防止類別被繼承或方法被覆寫:

<?php
// final 類別不能被繼承
final class Singleton {
    private static ?self $instance = null;
    
    private function __construct() {}
    
    public static function getInstance(): self {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
}

// final 方法不能被覆寫
class Base {
    final public function important(): void {
        echo "這個方法不能被覆寫";
    }
}

class Child extends Base {
    // public function important(): void {}  // 錯誤!
}
?>

protected 修飾符

protected 成員可以在類別內和子類別中存取:

<?php
class Animal {
    protected string $type = 'Unknown';
    
    protected function getType(): string {
        return $this->type;
    }
}

class Dog extends Animal {
    public function __construct() {
        $this->type = 'Dog';  // 可以存取 protected
    }
    
    public function showType(): string {
        return $this->getType();  // 可以呼叫 protected 方法
    }
}

$dog = new Dog();
echo $dog->showType();  // Dog
// echo $dog->type;     // 錯誤:外部無法存取 protected
?>

多型 (Polymorphism)

<?php
class Animal {
    public function speak(): string {
        return "...";
    }
}

class Dog extends Animal {
    public function speak(): string {
        return "Woof!";
    }
}

class Cat extends Animal {
    public function speak(): string {
        return "Meow!";
    }
}

function makeAnimalSpeak(Animal $animal): void {
    echo $animal->speak() . "\n";
}

$animals = [new Dog(), new Cat(), new Animal()];

foreach ($animals as $animal) {
    makeAnimalSpeak($animal);
}
// Woof!
// Meow!
// ...
?>

繼承與建構子

<?php
class Person {
    public function __construct(
        public string $name,
        public int $age
    ) {}
}

class Employee extends Person {
    public function __construct(
        string $name,
        int $age,
        public string $department,
        public float $salary
    ) {
        parent::__construct($name, $age);
    }
}

$employee = new Employee("Alice", 30, "IT", 50000);
echo $employee->name;       // Alice
echo $employee->department; // IT
?>

檢查類別關係

<?php
class Animal {}
class Dog extends Animal {}

$dog = new Dog();

// instanceof 檢查
var_dump($dog instanceof Dog);     // true
var_dump($dog instanceof Animal);  // true

// get_class 取得類別名稱
echo get_class($dog);  // Dog

// get_parent_class 取得父類別
echo get_parent_class($dog);  // Animal

// is_a 函數
var_dump(is_a($dog, 'Animal'));  // true
?>

實際範例

<?php
class Logger {
    public function log(string $message): void {
        echo "[" . date('Y-m-d H:i:s') . "] $message\n";
    }
}

class FileLogger extends Logger {
    public function __construct(
        private string $filename
    ) {}
    
    public function log(string $message): void {
        $formatted = "[" . date('Y-m-d H:i:s') . "] $message\n";
        file_put_contents($this->filename, $formatted, FILE_APPEND);
    }
}

class DatabaseLogger extends Logger {
    public function __construct(
        private PDO $db
    ) {}
    
    public function log(string $message): void {
        $stmt = $this->db->prepare(
            "INSERT INTO logs (message, created_at) VALUES (?, NOW())"
        );
        $stmt->execute([$message]);
    }
}

function processData(Logger $logger): void {
    $logger->log("開始處理");
    // ... 處理邏輯
    $logger->log("處理完成");
}
?>