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

PHP 繼承中獲取真實子類別名稱:__CLASS__ vs get_class() vs get_called_class()

     編輯:PHP 相關知識

問題現場

<?php
class A {
    public function __construct() {
        echo __CLASS__; // 輸出 'A'
    }
    public static function whoami() {
        echo __CLASS__; // 輸出 'A'
    }
}
class B extends A {}

new B();        // 輸出 A (預期 B)
B::whoami();    // 輸出 A (預期 B)
?>

原因:__CLASS__、__METHOD__、__FUNCTION__ 是**編譯期常數**,在定義該類別的檔案中解析,**不具備多態性**。

解決方案對比

方法適用場景PHP 版本回傳值效能
get_class($this)實例方法所有版本實際實例化的類別名稱極快 (內建)
get_called_class()靜態方法 (LSB - Late Static Binding)PHP 5.3+實際呼叫的類別名稱極快 (內建)
static::class靜態方法 (PHP 8+)PHP 8.0+實際呼叫的類別名稱編譯期解析,最快
self::class靜態方法PHP 5.5+定義該方法的類別名稱 (等同 __CLASS__)編譯期解析

修正後代碼

<?php
class A {
    public function __construct() {
        echo get_class($this); // 輸出實際實例類別
    }
    public static function whoami() {
        // PHP 5.3+
        echo get_called_class();
        // PHP 8.0+ 推薦
        // echo static::class;
    }
}
class B extends A {}

new B();        // 輸出 B
B::whoami();    // 輸出 B
?>

PHP 5.2 相容方案:手動實作 get_called_class()

若被迫維護 PHP 5.2 專案 (已 EOL,極不建議),可用 debug_backtrace() 解析呼叫堆疊:

<?php
if (!function_exists('get_called_class')) {
    function get_called_class(): string {
        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
        // 尋找靜態呼叫框架
        foreach ($trace as $frame) {
            if (isset($frame['class'], $frame['type']) && $frame['type'] === '::') {
                return $frame['class'];
            }
        }
        // 回退:解析原始碼行 (效能極差,僅救急)
        $frame = $trace[1] ?? [];
        if (isset($frame['file'], $frame['line'])) {
            $lines = file($frame['file']);
            $line = $lines[$frame['line'] - 1] ?? '';
            if (preg_match('/(\w+)::\w+/', $line, $m)) {
                return $m[1];
            }
        }
        return 'Unknown';
    }
}
?>

警告:此方案**效能極差、依賴原始碼格式、無法在 eval/動態建構類別中運作**。**請升級 PHP ≥ 5.3**。

進階:Late Static Binding (LSB) 完整範例

<?php
abstract class Model {
    protected static $table = 'base_table';

    public static function getTable(): string {
        return static::$table; // LSB: 取得子類別的靜態屬性
    }

    public static function find(int $id): ?self {
        $table = static::getTable();
        // SELECT * FROM $table WHERE id = $id
        return new static(); // LSB: 實例化子類別
    }
}

class User extends Model {
    protected static $table = 'users';
}

class Order extends Model {
    protected static $table = 'orders';
}

echo User::getTable();  // users
echo Order::getTable(); // orders

$user = User::find(1);  // 回傳 User 實例
$order = Order::find(1); // 回傳 Order 實例
?>

結語

  • **實例方法**:永遠用 get_class($this)。
  • **靜態方法**:PHP 5.3+ 用 get_called_class(),PHP 8+ 用 static::class。
  • **棄用 __CLASS__、self::class** 於需要多態的靜態情境。
  • **立即淘汰 PHP 5.2**,手動實作 LSB 是技術債務的深淵。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved