PHP 5 引入完整物件模型,PHP 7/8 持續強化。本文系統整理final、介面、抽象類、物件引用語意、clone 淺/深複製、屬性存取魔術方法等進階特性,附程式碼示範與常見誤區。
<?php
class Base {
public function normal() { echo "可覆寫\n"; }
final public function fixed() { echo "不可覆寫\n"; }
}
class Child extends Base {
public function normal() { echo "已覆寫\n"; } // OK
// public function fixed() { } // Fatal Error: Cannot override final method
}
final class FinalClass { }
// class Sub extends FinalClass {} // Fatal Error: Cannot inherit final class
final method:禁止子類覆寫,保護核心邏輯不被修改。final class:禁止繼承,適用於工具類、值物件、安全敏感類別。class Person {
private $name, $email;
public function __construct(string $name, string $email) {
$this->name = $name; $this->email = $email;
}
public function __toString(): string {
return "{$this->name} <{$this->email}>";
}
}
$p = new Person('Tom', 'tom@example.com');
echo $p; // 直接輸出:Tom
支援 echo, print, sprintf('%s', $obj), 字串連接 "User: $obj"。必須回傳 string,否則拋出 E_RECOVERABLE_ERROR。
| 特性 | 介面 | 抽象類 |
|---|---|---|
| 關鍵字 | interface | abstract class |
| 方法 | 全部 public abstract(可省略) | 至少一個 abstract 方法,可有具體方法 |
| 屬性 | 僅 const 常數 | 可有任意屬性 |
| 多重實作 | implements A, B, C | 單繼承 extends Base |
| 建構子 | 不可有 | 可有 |
| 用途 | 定義契約、行為規範 | 提供共用實作、模板方法 |
interface Logger {
public function log(string $level, string $message);
}
class FileLogger implements Logger {
public function log($level, $msg) { file_put_contents('app.log', "[$level] $msg\n", FILE_APPEND); }
}
abstract class AbstractExporter {
final public function export(array $data) {
$this->open();
foreach ($data as $row) $this->writeRow($row);
$this->close();
}
abstract protected function open();
abstract protected function writeRow(array $row);
abstract protected function close();
}
class CsvExporter extends AbstractExporter {
private $fp;
protected function open() { $this->fp = fopen('php://output', 'w'); }
protected function writeRow($row) { fputcsv($this->fp, $row); }
protected function close() { fclose($this->fp); }
}
$a = new StdClass(); $a->v = 1;
$b = $a; // $b 指向同一物件(引用賦值)
$b->v = 2;
echo $a->v; // 2(同一物件)
$c = clone $a; // 淺複製:新物件,但屬性值為引用複製
$c->v = 3;
echo $a->v; // 2(基本型別獨立)
clone 產生新實例。class Address {
public $city;
}
class User {
public $name;
public $address; // 物件屬性
public function __construct() {
$this->address = new Address();
}
// 深複製:在 __clone 中手動複製引用屬性
public function __clone() {
$this->address = clone $this->address;
}
}
$u1 = new User();
$u1->name = 'Tom';
$u1->address->city = 'Taipei';
$u2 = clone $u1; // 深複製
$u2->name = 'Jerry';
$u2->address->city = 'Kaohsiung';
echo $u1->address->city; // Taipei(未被影響)
echo $u2->address->city; // Kaohsiung
規則: __clone() 在新物件上下文執行,$this 指向新物件。需手動 clone 所有引用型別屬性(物件、資源),基本型別、陣列自動複製。
class Address {
public $city;
public function setCity($c) { $this->city = $c; }
public function getCity() { return $this->city; }
}
class User {
private $address;
public function __construct() { $this->address = new Address(); }
public function __call($method, $args) {
if (method_exists($this->address, $method)) {
return call_user_func_array([$this->address, $method], $args);
}
throw new BadMethodCallException("Method $method not found");
}
}
$u = new User();
$u->setCity('Tokyo'); // 實際委派給 Address
echo $u->getCity(); // Tokyo
__call() 實現方法委派/代理,構建「聚合優於繼承」的靈活結構。
class Entity {
private $data = [];
public function __set($name, $value) { $this->data[$name] = $value; }
public function __get($name) { return $this->data[$name] ?? null; }
public function __isset($name) { return isset($this->data[$name]); }
public function __unset($name) { unset($this->data[$name]); }
}
$e = new Entity();
$e->title = 'Hello'; // __set
echo $e->title; // __get -> Hello
isset($e->title); // __isset -> true
unset($e->title); // __unset
public $title,則 __set/__get 不會被呼叫。__get 會遮蔽父類,需手動 parent::__get($name)。__set/__get 僅處理實例屬性。掌握 PHP 物件模型的引用語意、複製機制、契約定義、動態委派,能寫出符合 SOLID 原則、可測試、可維護的領域模型。魔術方法威力強大但請謹慎使用,優先考慮顯式介面與屬性宣告。