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

PHP 物件導向進階特性詳解:final、介面、抽象類、物件引用、複製與魔術方法

     編輯:PHP 綜合知識

前言

PHP 5 引入完整物件模型,PHP 7/8 持續強化。本文系統整理final、介面、抽象類、物件引用語意、clone 淺/深複製、屬性存取魔術方法等進階特性,附程式碼示範與常見誤區。

一、 final 關鍵字

<?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:禁止繼承,適用於工具類、值物件、安全敏感類別。

二、 __toString() 物件字串轉換

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。

三、 介面 vs 抽象類

特性介面抽象類
關鍵字interfaceabstract 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(基本型別獨立)
  • PHP 5+:物件預設引用賦值,clone 產生新實例。
  • 基本型別、陣列:賦值為複製(Copy-on-Write)。

五、 clone 淺複製 vs 深複製

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 所有引用型別屬性(物件、資源),基本型別、陣列自動複製。

六、 聚合與 __call() 委派

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

注意事項

  1. 僅攔截不可見屬性: 若類別宣告 public $title,則 __set/__get 不會被呼叫。
  2. 繼承破壞: 子類重寫 __get 會遮蔽父類,需手動 parent::__get($name)。
  3. 效能較差: 魔術方法有額外開銷,熱點路徑建議顯式宣告屬性。
  4. 靜態分析/IDE 不支援: PhpStorm、Psalm、PHPStan 無法推導動態屬性型別,影響重構與錯誤偵測。
  5. 不可用於靜態屬性: __set/__get 僅處理實例屬性。

結語

掌握 PHP 物件模型的引用語意、複製機制、契約定義、動態委派,能寫出符合 SOLID 原則、可測試、可維護的領域模型。魔術方法威力強大但請謹慎使用,優先考慮顯式介面與屬性宣告。

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