首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 面向物件程式設計完整指南:從基礎類別到進階模式實戰

     編輯:PHP 綜合知識

前言:現代 PHP 的 OOP 基石

自 PHP 5 引入完整物件模型以來,OOP 已成為企業級開發標準。PHP 8.x 更加入了屬性提升建構子、唯讀屬性、聯集型別、match 運算式等現代特性。本文系統梳理從類別定義、可見性、魔術方法,到介面、抽象類別、Trait、最終類別等核心概念,並提供生產級程式碼範例。

一、類別結構與成員可見性

<?php
declare(strict_types=1);

namespace App\Domain\HR;

final class Employee {
    // 唯讀屬性(PHP 8.1+):初始化後不可變,適合 ID、建立時間
    public readonly string $id;
    
    // 型別宣告 + 可見性
    private string $name;
    protected string $title;
    public float $salary;
    
    // 類別常數
    public const string TAX_RATE = '0.05';
    
    // 靜態屬性:屬於類別而非實例
    private static int $instanceCount = 0;
    
    // 建構子屬性提升(PHP 8.0+):簡化屬性宣告與賦值
    public function __construct(
        string $id,
        string $name,
        string $title,
        float $salary
    ) {
        $this->id       = $id;
        $this->name     = $name;
        $this->title    = $title;
        $this->salary   = $salary;
        self::$instanceCount++;
    }
    
    // 解構子:資源釋放、日誌記錄
    public function __destruct() {
        self::$instanceCount--;
        error_log("Employee {$this->id} destroyed");
    }
    
    // 公開方法
    public function clockIn(): void {
        echo "Employee {$this->name} ({$this->id}) clocked in at " . date('H:i:s') . PHP_EOL;
    }
    
    // 存取器模式(PHP 8.2+ 可用 #[Property] 屬性)
    public function getName(): string {
        return $this->name;
    }
    
    public function setName(string $name): void {
        if (strlen($name) < 2) {
            throw new \InvalidArgumentException('Name too short');
        }
        $this->name = $name;
    }
    
    // 靜態方法
    public static function getInstanceCount(): int {
        return self::$instanceCount;
    }
    
    // final 方法:禁止子類別覆寫
    final public function getTaxRate(): float {
        return (float)self::TAX_RATE;
    }
}
?>

關鍵概念對照表

關鍵字作用範圍繼承行為典型用途
public任意處保持 publicAPI 介面、DTO 屬性
protected類別內部 + 子類別保持 protected內部邏輯、模板方法
private僅目前類別不可見、不可覆寫核心狀態、實作細節
static類別層級共享同一儲存空間計數器、快取、單例
final類別/方法禁止繼承/覆寫領域模型、安全關鍵邏輯
readonly屬性子類別可繼承但不可寫不可變物件、值物件

二、繼承與多型實戰

<?php
abstract class Manager extends Employee {
    // 抽象方法:強制子類別實作
    abstract public function approveLeave(string $employeeId, int $days): bool;
    
    // 模板方法:定義流程骨架,延遲具體步驟到子類別
    final public function processLeaveRequest(string $employeeId, int $days): array {
        $this->validateLeaveDays($days);
        $approved = $this->approveLeave($employeeId, $days);
        $this->notifyApplicant($employeeId, $approved);
        return ['approved' => $approved];
    }
    
    private function validateLeaveDays(int $days): void {
        if ($days <= 0 || $days > 30) {
            throw new \DomainException('Invalid leave days');
        }
    }
    
    protected function notifyApplicant(string $employeeId, bool $approved): void {
        // 實際對接通知服務
        error_log("Notify {$employeeId}: " . ($approved ? 'Approved' : 'Rejected'));
    }
}

class SeniorManager extends Manager {
    public function approveLeave(string $employeeId, int $days): bool {
        // 高階主管自動批准 ≤ 7 天
        return $days <= 7;
    }
}

class Director extends Manager {
    public function approveLeave(string $employeeId, int $days): bool {
        // 總監全部批准
        return true;
    }
}

// 使用範例
$mgr = new SeniorManager('M001', 'Alice', 'Senior Manager', 150000);
$result = $mgr->processLeaveRequest('E100', 5); // ['approved' => true]
?>

三、魔術方法:物件生命週期與動態行為

<?php
class FlexibleEntity {
    private array $data = [];
    
    // 屬性不存在時讀取
    public function __get(string $name): mixed {
        return $this->data[$name] ?? null;
    }
    
    // 屬性不存在時寫入
    public function __set(string $name, mixed $value): void {
        $this->data[$name] = $value;
    }
    
    // isset() / empty() 檢查
    public function __isset(string $name): bool {
        return isset($this->data[$name]);
    }
    
    // unset() 操作
    public function __unset(string $name): void {
        unset($this->data[$name]);
    }
    
    // 物件當作字串輸出
    public function __toString(): string {
        return json_encode($this->data, JSON_UNESCAPED_UNICODE);
    }
    
    // 物件當作函數呼叫
    public function __invoke(string $action, ...$args): mixed {
        return match($action) {
            'dump' => $this->data,
            'keys' => array_keys($this->data),
            default => throw new \BadMethodCallException("Unknown action: $action"),
        };
    }
    
    // 序列化控制(PHP 7.4+ 建議用 __serialize/__unserialize)
    public function __serialize(): array {
        return ['data' => $this->data, 'version' => 1];
    }
    
    public function __unserialize(array $data): void {
        $this->data = $data['data'] ?? [];
    }
    
    // 克隆時深拷貝關聯物件
    public function __clone(): void {
        foreach ($this->data as $k => $v) {
            if (is_object($v) && method_exists($v, '__clone')) {
                $this->data[$k] = clone $v;
            }
        }
    }
    
    // 呼叫不存在的方法
    public function __call(string $name, array $arguments): mixed {
        throw new \BadMethodCallException("Method $name not found");
    }
    
    // 靜態呼叫不存在的方法
    public static function __callStatic(string $name, array $arguments): mixed {
        throw new \BadMethodCallException("Static method $name not found");
    }
}
?>

四、介面 vs 抽象類別:設計決策指南

維度介面抽象類別
多重實作一個類別可 implements 多個介面單一繼承
方法實作PHP 8.0+ 可有預設實作可包含具體與抽象方法混合
屬性定義不可定義屬性(僅常數)可定義屬性、建構子
適用場景定義「行為契約」、跨領域複用定義「核心骨架」、共享實作邏輯
經典例子LoggerInterface, CacheInterface, SerializableBaseEntity, AbstractController, BaseRepository

介面範例:策略模式

<?php
interface PaymentGateway {
    public function charge(float $amount, string $currency): PaymentResult;
    public function refund(string $transactionId, float $amount): RefundResult;
}

class StripeGateway implements PaymentGateway { /* ... */ }
class PayPalGateway implements PaymentGateway { /* ... */ }

class PaymentService {
    public function __construct(
        private PaymentGateway $gateway // 建構子注入,依賴抽象
    ) {}
    
    public function processOrder(Order $order): void {
        $result = $this->gateway->charge($order->total, $order->currency);
        // ...
    }
}
?>

抽象類別範例:Active Record 基類

<?php
abstract class BaseModel {
    protected \PDO $db;
    protected string $table;
    protected array $fillable = [];
    
    public function __construct(\PDO $db) {
        $this->db = $db;
    }
    
    // 具體方法:共享 CRUD 邏輯
    public function find(int $id): ?static {
        $stmt = $this->db->prepare("SELECT * FROM {$this->table} WHERE id = ?");
        $stmt->execute([$id]);
        $row = $stmt->fetch(\PDO::FETCH_ASSOC);
        return $row ? $this->hydrate($row) : null;
    }
    
    public function save(): bool {
        if (empty($this->id)) {
            return $this->insert();
        }
        return $this->update();
    }
    
    // 抽象方法:子類別定義表名與欄位
    abstract protected function getTable(): string;
    abstract protected function getFillable(): array;
    
    // 受保護輔助方法
    protected function hydrate(array $row): static {
        $obj = new static($this->db);
        foreach ($row as $k => $v) {
            $obj->$k = $v;
        }
        return $obj;
    }
}

class UserModel extends BaseModel {
    protected function getTable(): string { return 'users'; }
    protected function getFillable(): array { return ['name', 'email', 'password_hash']; }
}
?>

五、Trait:橫向複用利器

<?php
trait Timestampable {
    public \DateTimeImmutable $createdAt;
    public \DateTimeImmutable $updatedAt;
    
    public function touch(): void {
        $this->updatedAt = new \DateTimeImmutable();
    }
}

trait SoftDeletes {
    public ?\DateTimeImmutable $deletedAt = null;
    
    public function delete(): void {
        $this->deletedAt = new \DateTimeImmutable();
    }
    
    public function isDeleted(): bool {
        return $this->deletedAt !== null;
    }
}

class Article {
    use Timestampable, SoftDeletes;
    
    public function __construct() {
        $this->createdAt = new \DateTimeImmutable();
        $this->updatedAt = $this->createdAt;
    }
}
?>

注意:Trait 衝突解決用 insteadof、as 關鍵字;避免過度使用導致「Trait 爆炸」。

六、現代 PHP 8.x 新特性速覽

  • 唯讀類別:readonly class Config { public string $key; } — 所有屬性自動唯讀
  • Enum:enum Status: string { case Draft = 'draft'; case Published = 'published'; } — 替代常數類別
  • 屬性:#[Route('/api/users', methods: ['GET'])] — 取代 DocBlock 註解
  • 建構子屬性提升:public function __construct(public string $name) {}
  • match 運算式:嚴格比較、回傳值、無 fall-through

七、SOLID 原則在 PHP 中的落地

  1. 單一職責 (SRP):一個類別只負責一個業務領域(UserService ≠ UserRepository ≠ UserValidator)。
  2. 開放封閉 (OCP):透過介面 + 策略模式,新增支付方式不修改 PaymentService。
  3. 里氏替換 (LSP):子類別必須能無縫替換父類別(避免拋出父類別未宣告的例外)。
  4. 介面隔離 (ISP):拆分大介面(UserInterface → Authenticatable, Authorizable, Notifiable)。
  5. 依賴反轉 (DIP):高層模組依賴抽象(介面),不依賴具體實作(使用容器注入)。

總結

PHP OOP 已演進為功能完整、型別嚴謹的現代物件系統。掌握「可見性封裝、介面契約、抽象骨架、Trait 橫向複用、魔術方法動態行為」五大支柱,並結合 PHP 8.x 新特性,即可建構可維護、可測試、可擴充的企業級應用。下一步建議深入學習 設計模式、DDD 戰術模式 與 PHPStan 靜態分析,將程式碼品質推向更高層次。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved