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

PHP 類三大可見性修飾符深度解析:public、protected、private 訪問控制與繼承規則

     編輯:PHP 綜合知識

核心概念:封裝性

面向對象三大特性之一封裝性,通過可見性修飾符控制屬性與方法的訪問權限,實現「高內聚、低耦合」。PHP 5 引入三種修飾符,PHP 8 新增 readonly、final 等輔助關鍵字。

三大修飾符對比表

修飾符本類子類實例(外部)典型用途
public✅✅✅對外接口、API 入口、配置屬性
protected✅✅❌內部邏輯、模板方法、子類擴展點
private✅❌❌核心算法、敏感數據、實現細節

代碼演示:訪問控制實戰

<?php
class User {
    public string $name = 'Guest';           // 公有:誰都能訪問
    protected string $email = '';            // 受保護:本類+子類
    private string $passwordHash = '';       // 私有:僅本類
    
    public function __construct(string $name, string $email, string $password) {
        $this->name = $name;
        $this->email = $email;
        $this->setPassword($password); // 內部調用私有方法
    }
    
    // 公有介面:註冊入口
    public function register(): bool {
        return $this->validate() && $this->save();
    }
    
    // 受保護:子類可覆蓋驗證邏輯
    protected function validate(): bool {
        return filter_var($this->email, FILTER_VALIDATE_EMAIL) 
            && strlen($this->passwordHash) > 0;
    }
    
    // 私有:核心加密邏輯,禁止覆蓋、禁止外部調用
    private function setPassword(string $plain): void {
        $this->passwordHash = password_hash($plain, PASSWORD_BCRYPT, ['cost' => 12]);
    }
    
    // 公有:安全驗證介面
    public function verifyPassword(string $plain): bool {
        return password_verify($plain, $this->passwordHash);
    }
    
    // 受保護:持久化模板方法
    protected function save(): bool {
        // 模擬數據庫保存
        return true;
    }
}

class AdminUser extends User {
    private array $permissions = [];
    
    public function __construct(string $name, string $email, string $password, array $perms) {
        parent::__construct($name, $email, $password);
        $this->permissions = $perms;
    }
    
    // 覆蓋受保護方法:擴展驗證邏輯
    protected function validate(): bool {
        return parent::validate() && !empty($this->permissions);
    }
    
    // 子類新增公有介面
    public function hasPermission(string $perm): bool {
        return in_array($perm, $this->permissions);
    }
    
    // ❌ 錯誤示範:無法訪問父類私有屬性/方法
    // public function debug() { echo $this->passwordHash; } // Fatal Error
    // public function resetPwd() { $this->setPassword('123'); } // Fatal Error
}

// 實例測試
$user = new User('Alice', 'alice@example.com', 'secret123');
echo $user->name;                    // ✅ public
// echo $user->email;                // ❌ protected
// echo $user->passwordHash;         // ❌ private
$user->register();                   // ✅ public
// $user->validate();                // ❌ protected
// $user->setPassword('x');          // ❌ private
$user->verifyPassword('secret123');  // ✅ public

$admin = new AdminUser('Bob', 'bob@admin.com', 'admin123', ['user:manage', 'log:view']);
$admin->hasPermission('user:manage'); // ✅ 子類公有方法
?>

關鍵規則詳解

1. 繼承中的訪問規則

  • 子類可訪問:父類 public、protected 成員
  • 子類不可訪問:父類 private 成員(包括屬性與方法)
  • 覆蓋規則:子類覆蓋父類方法時,可見性只能擴大不能縮小
    class Parent {
        protected function foo() {}
    }
    class Child extends Parent {
        public function foo() {} // ✅ protected → public 擴大
        // private function foo() {} // ❌ 縮小會報錯
    }

2. self vs parent vs $this

關鍵字含義訪問範圍典型場景
$this當前實例對象實例屬性/方法(受可見性限制)調用對象自身方法、訪問實例屬性
self當前類(靜態上下文)類常量、靜態屬性、靜態方法(含 private)self::CONST、self::$staticProp、self::staticMethod()
parent父類(靜態上下文)父類 public/protected 靜態成員、父類構造函數parent::__construct()、parent::CONST、parent::staticMethod()
static晚靜態綁定(調用類)運行時實際調用的類的靜態成員靜態工廠模式、ActiveRecord 查詢
class A {
    public static $name = 'A';
    public static function who() { echo self::$name; } // 總是 A
    public static function whoLate() { echo static::$name; } // 運行時類
}
class B extends A {
    public static $name = 'B';
}
B::who();      // 輸出 A
B::whoLate();  // 輸出 B

3. 靜態屬性與可見性

class Counter {
    private static int $count = 0; // 僅本類訪問
    protected static string $prefix = 'CNT_'; // 子類可訪問
    public static function increment(): void {
        self::$count++;
    }
    public static function get(): string {
        return self::$prefix . self::$count;
    }
}
class SubCounter extends Counter {
    public static function reset(): void {
        // self::$count = 0; // ❌ 無法訪問父類 private static
        parent::$count = 0; // ❌ parent 也無法訪問 private
        // 正確做法:父類提供 protected 重置方法
    }
}

最佳實踐:可見性設計原則

  1. 默認 private:所有內部實現細節優先設為私有
  2. 需要擴展時 protected:模板方法、鉤子方法、子類共享數據
  3. 對外契約 public:構成類公共 API 的方法、只讀屬性(配合 readonly)
  4. 屬性封裝:避免直接暴露屬性,提供 getter/setter 控制邏輯
    class Product {
            private float $price = 0;
            public function getPrice(): float { return $this->price; }
            public function setPrice(float $price): void {
                if ($price < 0) throw new InvalidArgumentException('價格不能為負');
                $this->price = $price;
            }
        }
  5. 不可變對象:PHP 8.1+ 使用 readonly 修飾符
    class DTO {
            public function __construct(
                public readonly string $id,
                public readonly string $name,
                public readonly DateTimeImmutable $createdAt
            ) {}
        }

常見錯誤與避坑

錯誤原因修正
子類嘗試覆蓋 private 方法private 不參與繼承,子類定義同名方法是新方法改為 protected 或重新設計
靜態上下文用 $this$this 僅在實例方法中可用改用 self:: 或 static::
parent:: 訪問私有靜態成員parent 同受可見性限制父類提供 protected 存取器
接口中聲明 private 方法接口只能定義 public 契約移除修飾符或改用 abstract class

Reflection 突破可見性(僅調試/測試/框架內部)

$user = new User('Test', 'test@test.com', 'pwd');
$ref = new ReflectionClass($user);

// 訪問私有屬性
$prop = $ref->getProperty('passwordHash');
$prop->setAccessible(true);
echo $prop->getValue($user); // 獲取私有屬性值

// 調用私有方法
$method = $ref->getMethod('setPassword');
$method->setAccessible(true);
$method->invoke($user, 'newpass');

警告:業務代碼嚴禁使用 Reflection 破壞封裝,僅用於單元測試、序列化庫、ORM 框架、依賴注入容器等基礎設施層。

總結

public、protected、private 構成 PHP 封裝性的基石。核心原則:實現細節私有化、擴展點保護化、對外契約公有化。配合 readonly、final、static 晚綁定、介面隔離,即可設計出高內聚、低耦合、易維護、可擴展的類體系。記住:可見性是給開發者看的約束,不是給機器看的限制——良好的設計讓正確的用法最簡單,錯誤的用法最困難。

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