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

PHP 封裝性實戰:訪問控制、魔術方法 __get/__set/__isset/__unset、屬性保護

     編輯:PHP 綜合知識

封裝核心:隱藏內部細節、受控存取

將屬性設為 private,提供 public 方法(Getter/Setter)或魔術方法進行驗證、轉換、日誌、延遲載入等邏輯。

訪問控制修飾符

修飾符類內部子類外部用途
public✓✓✓公開 API
protected✓✓✗繼承體系共享、內部協作
private✓✗✗核心狀態、不變量、強封裝

魔術方法實現屬性攔截

<?php
class Person {
    private $name;
    private $age;
    private $sex;

    public function __construct($name, $age, $sex) {
        $this->name = $name;
        $this->age = $age;
        $this->sex = $sex;
    }

    // 讀取不可見/不存在屬性時觸發
    public function __get($name) {
        if ($name === 'age') {
            // 業務規則:40 歲以上顯示減 10 歲
            return $this->age > 40 ? $this->age - 10 : $this->age;
        }
        if (property_exists($this, $name)) {
            return $this->$name;
        }
        throw new Exception("Property '$name' not accessible");
    }

    // 寫入不可見/不存在屬性時觸發
    public function __set($name, $value) {
        if ($name === 'age') {
            // 驗證:年齡 0-100
            if ($value < 0 || $value > 100) {
                throw new InvalidArgumentException("Age must be 0-100");
            }
            $this->age = $value;
            return;
        }
        if (property_exists($this, $name)) {
            $this->$name = $value;
            return;
        }
        throw new Exception("Property '$name' not writable");
    }

    // isset() 檢測不可見屬性時觸發
    public function __isset($name) {
        return property_exists($this, $name) && $this->$name !== null;
    }

    // unset() 刪除不可見屬性時觸發
    public function __unset($name) {
        if (property_exists($this, $name)) {
            $this->$name = null;
        }
    }

    // 公開方法替代直接存取(推薦)
    public function setAge($age) {
        if ($age < 0 || $age > 100) return false;
        $this->age = $age;
        return true;
    }

    public function getAge() {
        return $this->age > 40 ? $this->age - 10 : $this->age;
    }

    public function say() {
        echo "我是 {$this->name},{$this->getAge()} 歲,{$this->sex}<br>";
        $this->run(); // 內部調用私有方法
    }

    private function run() {
        echo "跑步中...<br>";
    }
}

$p = new Person("張三", 45, "男");
echo $p->age;        // 觸發 __get -> 輸出 35 (45-10)
$p->age = 50;        // 觸發 __set -> 成功
$p->age = 150;       // 觸發 __set -> 拋出異常
isset($p->name);     // 觸發 __isset -> true
unset($p->name);     // 觸發 __unset -> name 變 null
$p->say();           // 正常調用
// $p->run();        // 錯誤:私有方法不可見
?>

封裝最佳實踐

  1. 優先用 private + 公共 Getter/Setter:語義清晰、IDE 支援、靜態分析友好、可在介面中定義契約。
  2. 魔術方法用於:動態屬性、代理模式、ORM 實體基類、向後相容舊代碼。
  3. 不可變對象:僅提供 Getter、建構子注入、readonly 屬性(PHP 8.1+)。
  4. 資料傳輸對象 (DTO):公共屬性或 readonly,無業務邏輯,純數據載體。

PHP 8.1+ readonly 屬性

<?php
class UserDTO {
    public function __construct(
        public readonly int $id,
        public readonly string $email,
        public readonly string $name
    ) {}
}
// 建構後不可修改,無需 Getter/Setter,完美封裝不可變數據
?>
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved