ReflectionClass 允許在運行時檢查、操作類的元數據:屬性、方法、常量、接口、父類、註解等。它是框架實現依賴注入、路由分發、插件系統、ORM 映射的核心基礎。
<?php
class Foo {
public $name = 'Foo';
private $secret = 'hidden';
const VERSION = '1.0';
public function bar($arg) {
return "bar called with $arg";
}
protected function internal() {
return 'internal';
}
}
$ref = new ReflectionClass('Foo');
// 1. 獲取類基本信息
echo "類名: " . $ref->getName() . "\n";
echo "文件: " . $ref->getFileName() . "\n";
echo "起始行: " . $ref->getStartLine() . "\n";
echo "結束行: " . $ref->getEndLine() . "\n";
echo "是否抽象: " . ($ref->isAbstract() ? 'yes' : 'no') . "\n";
echo "是否接口: " . ($ref->isInterface() ? 'yes' : 'no') . "\n";
echo "是否可實例化: " . ($ref->isInstantiable() ? 'yes' : 'no') . "\n";
// 2. 獲取常量
print_r($ref->getConstants());
// 3. 獲取屬性 (含訪問控制)
foreach ($ref->getProperties() as $prop) {
echo "屬性: {$prop->getName()} 訪問: " . implode(', ', Reflection::getModifierNames($prop->getModifiers())) . "\n";
$prop->setAccessible(true); // 突破 private/protected
echo " 值: " . var_export($prop->getValue(new Foo()), true) . "\n";
}
// 4. 獲取方法
foreach ($ref->getMethods() as $method) {
echo "方法: {$method->getName()} 參數數: {$method->getNumberOfParameters()}\n";
}
?>
<?php
$ref = new ReflectionClass('Foo');
// 方式 1:newInstance() 等同 new Foo()
$obj1 = $ref->newInstance();
// 方式 2:newInstanceArgs() 支持構造函數參數
class Bar {
public function __construct(public string $name, public int $age) {}
}
$refBar = new ReflectionClass('Bar');
$obj2 = $refBar->newInstanceArgs(['Tom', 25]);
// 方式 3:無構造函數實例化 (不調用 __construct)
$obj3 = $ref->newInstanceWithoutConstructor();
// 獲取方法對象並調用
$method = $ref->getMethod('bar');
$method->setAccessible(true); // 若為 private/protected
$result = $method->invoke($obj1, 'test argument'); // 等同 $obj1->bar('test argument')
echo $result; // bar called with test argument
// 靜態方法調用
$staticMethod = $ref->getMethod('staticMethod');
$staticMethod->invoke(null, 'arg'); // 第一個參數為 null
?>
模擬 MVC 框架路由分發核心邏輯:
<?php
interface IController {
public function execute();
}
class UserController implements IController {
public function execute() { echo "UserController::execute\n"; }
public function profile() { echo "UserController::profile\n"; }
}
class Dispatcher {
public function dispatch(string $controllerName, string $action = 'execute') {
try {
// 1. 類是否存在
if (!class_exists($controllerName)) {
throw new Exception("Controller '$controllerName' not found");
}
// 2. 構建反射類
$rc = new ReflectionClass($controllerName);
// 3. 驗證實現了接口
if (!$rc->implementsInterface('IController')) {
throw new Exception("Controller must implement IController");
}
// 4. 驗證方法存在
if (!$rc->hasMethod($action)) {
throw new Exception("Action '$action' not found");
}
// 5. 實例化控制器
$controller = $rc->newInstance();
// 6. 獲取方法並調用
$method = $rc->getMethod($action);
$method->invoke($controller);
} catch (Exception $e) {
// 統一異常處理:404、500、日誌記錄
echo "Dispatch Error: " . $e->getMessage();
}
}
}
// 使用
$dispatcher = new Dispatcher();
$dispatcher->dispatch('UserController', 'profile'); // 輸出: UserController::profile
?>
<?php
/**
* @Route("/api/user")
* @Auth(role="admin")
*/
class ApiController {}
$ref = new ReflectionClass('ApiController');
$docComment = $ref->getDocComment();
// 解析 @Route、@Auth 等註解實現路由註冊、權限檢查
?>
<?php
class Container {
private $bindings = [];
public function bind(string $abstract, $concrete) {
$this->bindings[$abstract] = $concrete;
}
public function make(string $abstract) {
if (!isset($this->bindings[$abstract])) {
throw new Exception("No binding for $abstract");
}
$concrete = $this->bindings[$abstract];
$ref = new ReflectionClass($concrete);
$constructor = $ref->getConstructor();
if (!$constructor) return $ref->newInstance();
$params = $constructor->getParameters();
$args = [];
foreach ($params as $param) {
$type = $param->getType();
if ($type && !$type->isBuiltin()) {
$args[] = $this->make($type->getName()); // 遞歸解析依賴
} elseif ($param->isDefaultValueAvailable()) {
$args[] = $param->getDefaultValue();
} else {
throw new Exception("Cannot resolve parameter {$param->getName()}");
}
}
return $ref->newInstanceArgs($args);
}
}
// 使用
$container = new Container();
$container->bind('db', 'PDO');
$container->bind('UserRepository', 'MySQLUserRepository'); // 假設構造函數需 PDO
$repo = $container->make('UserRepository');
?>
| 場景 | 建議 |
|---|---|
| 高頻調用 (如每請求路由分發) | 緩存 ReflectionClass 對象 (APCu/靜態變量),避免重複反射解析 |
| 大量實例化 | 優先用 new Class(),僅在動態參數時用 newInstanceArgs() |
| 訪問私有成員 | 謹慎使用 setAccessible(true),破壞封裝,僅用於測試/框架內部 |
| 獲取方法參數類型 | PHP 7.0+ 支持 $param->getType(),結合 ReflectionNamedType 完整解析 |
ReflectionClass::newInstance() 無法實例化帶有必填構造函數參數的類 → 用 newInstanceArgs()getMethods() 返回繼承鏈所有方法,含父類方法 → 用 $method->getDeclaringClass()->getName() === $ref->getName() 過濾當前類方法new ReflectionClass('Outer\\Inner')) 需使用完整限定名