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

PHP 類關鍵字深度解析:$this、static、final、const、self 完整使用指南

     編輯:PHP 綜合知識

在 PHP 物件導向程式設計中,$this、static、final、const、self 是五個核心關鍵字,分別控制實例存取、靜態成員、繼承限制、常數定義與類別自我引用。掌握它們的差異與適用場景,是寫出健壯、可維護 PHP 程式碼的關鍵。本文將逐一深度解析並提供實戰範例。


一、$this - 當前實例引用

定義

$this 是在類別內部方法中,指向「當前實例化物件」的特殊變數。只能在非靜態方法中使用。

存取對象

  • 非靜態屬性(實例屬性)
  • 非靜態方法(實例方法)

語法

$this->propertyName;
$this->methodName();

實戰範例

class User {
    private string $name;
    private int $loginCount = 0;

    public function __construct(string $name) {
        $this->name = $name; // 設定實例屬性
    }

    public function greet(): string {
        return "Hello, {$this->name}!"; // 讀取實例屬性
    }

    public function login(): void {
        $this->loginCount++; // 修改實例屬性
        $this->logLogin(); // 呼叫實例方法
    }

    private function logLogin(): void {
        error_log("User {$this->name} logged in. Count: {$this->loginCount}");
    }
}

$user1 = new User('Alice');
$user2 = new User('Bob');

echo $user1->greet(); // Hello, Alice!
$user1->login();
$user1->login();
echo $user1->greet(); // 仍為 Hello, Alice! (loginCount 內部變化)

關鍵規則

  1. 不可在靜態方法中使用:靜態方法無實例上下文
  2. 每個實例擁有獨立的屬性副本:$user1 與 $user2 的 $name、$loginCount 互不干擾
  3. 不可用於存取靜態成員:請用 self::$staticProp 或 ClassName::$staticProp

二、static - 靜態成員與延遲靜態綁定

兩大用途

  1. 聲明靜態屬性/方法:屬於類別本身,非特定實例
  2. 靜態變數(函數內):保持跨呼叫的狀態,僅初始化一次

1. 靜態屬性與方法

class Counter {
    public static int $count = 0; // 靜態屬性

    public static function increment(): void {
        self::$count++; // 類內部存取用 self::
    }

    public static function getCount(): int {
        return self::$count;
    }
}

Counter::increment();
Counter::increment();
echo Counter::getCount(); // 2
echo Counter::$count;     // 2 (公開屬性可直接存取)

靜態成員特性

  • 所有實例共享同一份記憶體
  • 無需實例化即可呼叫:ClassName::staticMethod()
  • 繼承時,子類別若未覆寫,共享父類別的靜態屬性;覆寫則擁有獨立副本

2. 延遲靜態綁定 - static:: vs self::

class ParentClass {
    public static function whoAmI() {
        echo __CLASS__ . "\n";
    }
    public static function testSelf() {
        self::whoAmI();     // 編譯期綁定:永遠是 ParentClass
    }
    public static function testStatic() {
        static::whoAmI();   // 執行期綁定:依實際呼叫類別決定
    }
}

class ChildClass extends ParentClass {
    public static function whoAmI() {
        echo __CLASS__ . "\n";
    }
}

ParentClass::testSelf();   // 輸出:ParentClass
ParentClass::testStatic(); // 輸出:ParentClass
ChildClass::testSelf();    // 輸出:ParentClass (self 綁定父類)
ChildClass::testStatic();  // 輸出:ChildClass (static 延遲綁定到子類)

3. 函數內靜態變數

function generateId(): int {
    static $id = 0; // 僅初始化一次,跨呼叫保持值
    return ++$id;
}

echo generateId(); // 1
echo generateId(); // 2
echo generateId(); // 3

三、final - 防止繼承與覆寫

用途

  • final class:類別不可被繼承
  • final function:方法不可被子類別覆寫
  • final const:PHP 8.1+ 支援,常數不可被子類別覆寫

實戰範例

final class SecurityService {
    // 此類別不可被繼承
    public function encrypt(string $data): string {
        // 加密邏輯
    }
}

// class HackerService extends SecurityService {} // Fatal Error: Cannot extend final class

class ApiController {
    final public function handleRequest(): void {
        // 核心流程不可被子類別修改
        $this->validate();
        $this->process();
        $this->respond();
    }

    protected function validate(): void { /* ... */ }
    protected function process(): void { /* ... */ }
    protected function respond(): void { /* ... */ }
}

class CustomApiController extends ApiController {
    // public function handleRequest() {} // Fatal Error: Cannot override final method
    // 但可覆寫 protected 方法擴充行為
    protected function process(): void {
        parent::process();
        $this->logCustomMetrics();
    }
}

設計原則

  • 預設將類別標記為 final,除非明確設計為可繼承(開放/封閉原則)
  • 核心流程方法標記 final,擴充點提供 protected 方法供覆寫
  • 避免過度使用導致測試困難(無法 Mock),可配合介面解耦

四、const - 類別常數

定義語法

class Math {
    public const PI = 3.14159265359;
    protected const DEFAULT_PRECISION = 2;
    private const INTERNAL_VERSION = '1.0.0';
}

存取方式

位置語法範例
類別內部self::CONST_NAMEself::PI
類別外部 (public)ClassName::CONST_NAMEMath::PI
子類別內部parent::CONST_NAME 或 self::CONST_NAMEparent::PI

特性與限制

  • 值必須為編譯期常數(標量、陣列常數、其他常數),不可為變數、函數呼叫、運算結果
  • 不可在執行期修改
  • 不需 $ 符號:const PI = 3.14(非 const $PI)
  • 可被介面定義,實作類別不可修改值
  • PHP 8.1+ 支援 final const 防止覆寫

實戰範例:狀態機常數

class OrderStatus {
    public const PENDING   = 'pending';
    public const PAID      = 'paid';
    public const SHIPPED   = 'shipped';
    public const COMPLETED = 'completed';
    public const CANCELLED = 'cancelled';

    public static function getValidTransitions(string $from): array {
        return match($from) {
            self::PENDING   => [self::PAID, self::CANCELLED],
            self::PAID      => [self::SHIPPED, self::CANCELLED],
            self::SHIPPED   => [self::COMPLETED],
            default         => [],
        };
    }
}

// 使用
$status = OrderStatus::PAID;
$next = OrderStatus::getValidTransitions($status); // ['shipped', 'cancelled']

五、self - 當前類別引用

定義

self 代表「當前類別」(編譯期綁定),用於存取靜態成員、常數、呼叫靜態方法。與 static(執行期綁定)形成對比。

適用場景

  1. 存取類別常數:self::CONST
  2. 存取靜態屬性:self::$staticProp
  3. 呼叫靜態方法:self::staticMethod()
  4. 實例化當前類別:new self()(工廠模式常見)
  5. 型別宣告:function setSelf(self $obj)(PHP 7.1+,限制為當前類別實例,不含子類)

self vs static vs $this 對比表

特性$thisselfstatic
綁定時機執行期 (實例)編譯期 (類別)執行期 (類別)
存取實例成員✅❌❌
存取靜態成員❌✅✅ (LSB)
存取常數❌✅✅
繼承行為指向實際實例類別永遠指向定義類別指向實際呼叫類別
可用於型別提示❌✅ (PHP 7.1+)❌

實戰範例:工廠模式

abstract class Product {
    abstract public function getName(): string;

    // 靜態工廠方法
    public static function create(string $type): self {
        return match($type) {
            'book'   => new Book(),
            'phone'  => new Phone(),
            default  => throw new InvalidArgumentException("Unknown type: $type"),
        };
    }
}

class Book extends Product {
    public function getName(): string { return 'Book'; }
}
class Phone extends Product {
    public function getName(): string { return 'Phone'; }
}

// 使用
$product = Product::create('book'); // 回傳 Book 實例,型別為 Product
echo $product->getName(); // Book

六、綜合實戰:單例模式結合五大關鍵字

final class Database {
    // 1. const: 配置常數
    private const DEFAULT_DSN = 'mysql:host=localhost;dbname=app';
    private const DEFAULT_USER = 'root';
    private const DEFAULT_PASS = '';

    // 2. static: 單例儲存
    private static ?self $instance = null;

    // 3. 實例屬性
    private PDO $pdo;

    // 4. 私有建構子防止外部 new
    private function __construct() {
        $this->pdo = new PDO(
            self::DEFAULT_DSN,
            self::DEFAULT_USER,
            self::DEFAULT_PASS,
            [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
        );
    }

    // 5. 靜態工廠方法 (使用 self 型別提示)
    public static function getInstance(): self {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    // 6. 禁止克隆與反序列化
    private function __clone() {}
    public function __wakeup() {
        throw new Exception("Cannot unserialize singleton");
    }

    // 7. 代理 PDO 方法
    public function query(string $sql): PDOStatement {
        return $this->pdo->query($sql);
    }

    // 8. final 方法防止覆寫核心邏輯
    final public function beginTransaction(): bool {
        return $this->pdo->beginTransaction();
    }
}

// 使用
$db = Database::getInstance();
$stmt = $db->query('SELECT * FROM users');

七、常見錯誤速查表

在靜態方法中使用 $this
Fatal Error: Using $this when not in object context。修正:改用 self::$prop 或 static::$prop
const $PI = 3.14
Parse Error。修正:移除 $,寫 const PI = 3.14
嘗試覆寫 final 方法
Fatal Error: Cannot override final method。修正:移除子類別的覆寫,或移除父類別 final
self:: 調用被子類別覆寫的靜態方法
永遠呼叫父類別版本。修正:改用 static:: 實現延遲靜態綁定
介面中定義常數,實作類別嘗試修改值
Fatal Error。修正:介面常數不可變,若需可配置請改用靜態屬性

八、版本演進提醒

  • PHP 7.1:self 作為型別提示
  • PHP 8.0:static 回傳型別
  • PHP 8.1:final const、readonly 屬性(可替代部分常數用途)
  • PHP 8.2:readonly 類別
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved