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

PHP 反射 API 完整實戰:動態獲取方法參數、屬性、註解與動態調用

     編輯:PHP 綜合知識

PHP Reflection (反射) API 允許程式碼在運行時檢查、分析、操作類別、介面、方法、參數、屬性、註解等元資料。本文從核心類別架構、實戰場景 (參數自動綁定、依賴注入、文檔生成)、完整 API 速查表、效能考量五個維度,系統講解反射的正確使用姿勢。

一、核心類別架構圖

Reflection (基礎工具類)
├── Reflector (介面)
├── ReflectionException
├── ReflectionFunctionAbstract
│   ├── ReflectionFunction (函數/閉包)
│   └── ReflectionMethod (類方法)
├── ReflectionParameter (方法/函數參數)
├── ReflectionClass (類/介面/Trait)
│   ├── ReflectionObject (具體物件實例)
│   └── ReflectionProperty (屬性)
└── ReflectionExtension (擴展模組)

二、實戰場景:根據 $_GET 自動綁定方法參數

需求

方法原型 test($uid, $score),自動從 $_GET['uid'], $_GET['score'] 取值並調用。

實作

getParameters();

// 3. 組裝調用參數
$args = [];
foreach ($params as $param) {
    $name = $param->getName();
    
    // 從 GET 取值
    if (isset($_GET[$name])) {
        $args[] = $_GET[$name];
    } 
    // 使用預設值
    elseif ($param->isOptional()) {
        $args[] = $param->getDefaultValue();
    }
    // 必填參數缺失
    else {
        throw new InvalidArgumentException("Missing required parameter: $name");
    }
}

// 4. 動態調用 (靜態方法)
$method->invoke(null, ...$args); // 或 $method->invokeArgs(null, $args)

// 非靜態方法需傳入實例
// $obj = new TestClass();
// $method->invoke($obj, ...$args);
?>

三、核心 API 速查表

ReflectionClass — 類別檢查

方法回傳說明
getName()string類別名稱
isInternal() / isUserDefined()bool內建 vs 用戶定義
isInstantiable()bool可否實例化 (非 abstract/interface)
hasMethod/Property/Constant(name)bool存在性檢查
getMethod(name)ReflectionMethod單一方法
getMethods()ReflectionMethod[]所有方法
getProperty(name)ReflectionProperty單一屬性
getProperties()ReflectionProperty[]所有屬性
getConstants()array所有常數
getConstructor()ReflectionMethod建構子
getParentClass()ReflectionClass父類別
getInterfaces()ReflectionClass[]實作介面
getTraits()ReflectionClass[]使用 Trait (5.4+)
getDocComment()string類別文檔註解
newInstance(...args)object實例化 (自動調用建構子)
newInstanceWithoutConstructor()object不調用建構子實例化
getStaticProperties()array靜態屬性鍵值對
getStaticPropertyValue(name)mixed讀取靜態屬性
setStaticPropertyValue(name, val)void寫入靜態屬性
isSubclassOf(class)bool繼承關係檢查
implementsInterface(name)bool介面實作檢查

ReflectionMethod — 方法操作

方法說明
isPublic/Private/Protected/Static/Final/Abstract可見性/修飾符檢查
isConstructor/Destructor建構/解構子檢查
getModifiers()修飾符位遮罩 (配合 Reflection::getModifierNames())
getDeclaringClass()宣告此方法的類別
invoke(object, ...args)動態調用
invokeArgs(object, array)陣列參數調用
getParameters()ReflectionParameter[]
getNumberOfParameters()參數總數
getNumberOfRequiredParameters()必填參數數
返回值類型 (7.0+)getReturnType() → ReflectionType

ReflectionParameter — 參數詳情

方法說明
getName()參數名稱
isOptional()是否有預設值
isDefaultValueAvailable()是否可獲取預設值 (內建函數有時不可用)
getDefaultValue()預設值
isPassedByReference()引用傳遞 (&$param)
getType() (7.0+)型別提示 ReflectionNamedType
allowsNull()型別允許 null
isVariadic()可變參數 (...$args)
getClass()型別提示為類別時的 ReflectionClass
isArray()型別提示為 array

ReflectionProperty — 屬性操作

方法說明
isPublic/Private/Protected/Static/Default可見性檢查
getValue(object)讀取屬性值 (繞過可見性)
setValue(object, value)寫入屬性值 (繞過可見性)
getDeclaringClass()宣告類別
getDocComment()屬性文檔註解
getType() (7.4+)型別提示

ReflectionFunction — 函數/閉包

同 ReflectionMethod 非靜態部分,額外:

  • getClosure():返回閉包物件 (5.4+)
  • getClosureThis():閉包綁定的 $this
  • getClosureScopeClass():閉包作用域類別

ReflectionExtension — 擴展資訊

  • getName(), getVersion()
  • getFunctions()、getClasses()、getConstants()、getINIEntries()

四、進階應用場景

1. 依賴注入容器 (自動解析建構子參數)

class Container {
    public function make($class) {
        $ref = new ReflectionClass($class);
        $constructor = $ref->getConstructor();
        if (!$constructor) return $ref->newInstance();
        
        $args = [];
        foreach ($constructor->getParameters() as $param) {
            $type = $param->getType();
            if ($type && !$type->isBuiltin()) {
                // 遞歸解析依賴
                $args[] = $this->make($type->getName());
            } elseif ($param->isOptional()) {
                $args[] = $param->getDefaultValue();
            } else {
                throw new Exception("Cannot resolve {$param->getName()}");
            }
        }
        return $ref->newInstanceArgs($args);
    }
}

2. API 文檔自動生成

$ref = new ReflectionClass('UserController');
foreach ($ref->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
    $doc = $method->getDocComment();
    $params = [];
    foreach ($method->getParameters() as $p) {
        $params[] = [
            'name' => $p->getName(),
            'type' => $p->getType()?->getName() ?? 'mixed',
            'required' => !$p->isOptional(),
            'default' => $p->isOptional() ? $p->getDefaultValue() : null,
        ];
    }
    // 輸出 OpenAPI/Swagger JSON...
}

3. 註解驅動路由/驗證

/**
 * @Route("/user/{id}", methods={"GET"})
 * @Validate({"id": "required|integer|min:1"})
 */
public function show($id) { ... }

// 解析
$doc = $method->getDocComment();
preg_match('@@Route\((.+)\)@', $doc, $m); // 解析路由
preg_match('@@Validate\((.+)\)@', $doc, $m); // 解析驗證規則

五、效能與最佳實踐

原則說明
緩存反射結果反射涉及字串解析、記憶體分配,極慢。生產環境必須緩存 (靜態變數、APCu、Redis)
避免熱路徑使用每秒千次請求的控制器動態調用嚴禁逐請求反射
優先用型別提示PHP 7.0+ 型別提示 + ReflectionType 替代註解解析
內建類別反射受限內建類別無法獲取原始碼行號、文檔註解可能為空
私有成員可訪問ReflectionProperty::setAccessible(true) 可繞過可見性 (慎用)

六、版本演進關鍵點

  • PHP 5.0:引入 Reflection API
  • PHP 5.4:ReflectionClass::getTraits(), ReflectionFunction::getClosure()
  • PHP 7.0:ReflectionParameter::getType(), ReflectionMethod::getReturnType(), 純量/返回型別提示
  • PHP 7.1:ReflectionType::isBuiltin(), ReflectionNamedType
  • PHP 7.4:ReflectionProperty::getType(), 類型屬性
  • PHP 8.0:ReflectionAttribute (原生屬性/註解), ReflectionNamedType::getName() 返回完整命名空間
  • PHP 8.1:ReflectionClass::isReadOnly(), ReflectionProperty::isReadOnly(), ReflectionMethod::hasReturnType()

反射是框架、ORM、DI 容器、文檔生成器的基石。理解類別結構、善用緩存、避開熱路徑,即能安全釋放動態程式設計的威力。

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