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

PHP 反射機制 Reflection 實戰:動態代理、註解解析、依賴注入核心原理

     編輯:PHP 綜合知識

核心類別速查

類別用途關鍵方法
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
ReflectionAttributePHP 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 解析。

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