核心類別速查
| 類別 | 用途 | 關鍵方法 |
ReflectionClass | 類別資訊 | getMethods, getProperties, getConstructor, getAnnotations, newInstanceArgs |
ReflectionMethod | 方法資訊 | getParameters, getReturnType, invoke, invokeArgs, isPublic, isStatic |
ReflectionProperty | 屬性資訊 | getValue, setValue, isPublic, isStatic, getType |
ReflectionParameter | 參數資訊 | getName, getType, isOptional, getDefaultValue, getAttributes |
ReflectionFunction | 函式資訊 | getParameters, invoke, invokeArgs |
ReflectionAttribute | PHP 8 屬性/註解 | getName, newInstance, getArguments |
實戰一:動態代理模式 (文章範例優化)
targets[] = $target;
}
public function __call(string $name, array $args): mixed {
foreach ($this->targets as $target) {
$refl = new ReflectionClass($target);
if ($refl->hasMethod($name)) {
$method = $refl->getMethod($name);
if ($method->isPublic() && !$method->isAbstract()) {
return $method->invoke($target, ...$args);
}
}
}
throw new BadMethodCallException("Method $name not found in any target");
}
}
// 使用
$proxy = new DynamicProxy();
$proxy->addTarget(new ServiceA());
$proxy->addTarget(new ServiceB());
echo $proxy->doWork('test'); // ServiceA processed: test (第一個匹配)
?>
實戰二:依賴注入容器簡易實現
bindings[$abstract] = $concrete;
}
public function singleton(string $abstract, callable $concrete): void {
$this->bind($abstract, function() use ($concrete) {
static $instance = null;
return $instance ??= $concrete();
});
}
public function make(string $abstract): mixed {
if (isset($this->instances[$abstract])) {
return $this->instances[$abstract];
}
if (!isset($this->bindings[$abstract])) {
// 自動解析類別建構子依賴
return $this->resolve($abstract);
}
$concrete = $this->bindings[$abstract];
$instance = $concrete($this);
if ($concrete instanceof Closure && $this->isShared($abstract)) {
$this->instances[$abstract] = $instance;
}
return $instance;
}
private function resolve(string $class): object {
$refl = new ReflectionClass($class);
$constructor = $refl->getConstructor();
if (!$constructor) {
return new $class();
}
$params = $constructor->getParameters();
$args = [];
foreach ($params 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 parameter {$param->getName()} for $class");
}
}
return $refl->newInstanceArgs($args);
}
}
// 使用
$container = new Container();
$container->singleton(Database::class, fn() => new Database('mysql:host=...'));
$container->bind(Logger::class, fn($c) => new FileLogger('/var/log/app.log'));
class UserService {
public function __construct(
private Database $db,
private Logger $logger
) {}
}
$service = $container->make(UserService::class); // 自動注入 Database & Logger
?>
實戰三:PHP 8 Attributes 註解路由 (現代替代 DocBlock)
getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
$attrs = $method->getAttributes(Route::class);
foreach ($attrs as $attr) {
$route = $attr->newInstance();
$routes[] = [
'path' => $route->path,
'methods' => $route->methods,
'handler' => [$reflClass->getName(), $method->getName()],
];
}
}
print_r($routes);
?>
效能考量與快取
- 反射涉及 C 層查找,**比直接呼叫慢 10-100 倍**
- 生產環境**必須快取**反射結果 (檔案、Redis、APCu)
- Composer
ClassLoader 內建快取機制
- DI 容器編譯期生成工廠代碼 (如 Symfony 容器編譯)
常見陷阱
| 陷阱 | 解決 |
getProperties() 不含父類 private 屬性 | 用 getProperty() 單獨查找或遍歷父類 |
invoke() 靜態方法傳 null | $method->invoke(null, ...$args) |
| 無法存取 private 屬性值 | $prop->setAccessible(true) 後再 getValue/setValue |
| PHP 8.1+ readonly 屬性 | $prop->isReadOnly() 判斷,不可 setValue |
總結
PHP Reflection 是**框架級功能的基石**:DI 容器、ORM、路由、序列化、測試 Mock 皆建立於此。核心模式:**ReflectionClass → getMethod/Property/Constructor → 參數型別解析 → 遞歸構建依賴圖**。生產環境務必配合**快取、編譯期優化**,避免運行時反射效能損耗。PHP 8 Attributes 讓元資料成為一等公民,徹底取代 DocBlock 解析。