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 容器、文檔生成器的基石。理解類別結構、善用緩存、避開熱路徑,即能安全釋放動態程式設計的威力。