HyperfのInjectアノテーションがnew演算子によるインスタンス生成時にでも動作する仕組みについて、開発者が以下の仕組みを解説しています。
プロキシクラス生成の条件
Hyperfはすべてのクラスに対してプロキシクラスを生成しません。/runtime/container/proxyディレクトリに保存されるプロキシファイルは、AOP(Aspect Oriented Programming)が必要なクラスに限定されます。
class InjectionAspect extends AbstractAspect {
public array $targetAnnotations = [
Injection::class,
];
public function intercept(InvocationInterface $invocation) {
return $invocation->proceed();
}
}
Injectionアノテーションを含むクラスに対してのみプロキシ生成がトリガーされ、このアスペクトは実際のメソッド挙動の変更は行いません。
Composer自動ロードの利用
HyperfはComposerのautoloadメカニズムを利用してプロキシクラスのロードを強制します。
$loader = Composer::getLoader();
$proxyMap = (new ProxyScanner())->scan($loader->getClassMap(), BASE_PATH . '/runtime/proxy');
$loader->addClassMap($proxyMap);
classMap配列にオリジナルクラス→プロキシクラスのマッピングを登録することで、new演算子によるインスタンス生成時に自動的にプロキシが選択されます。
依存解決プロセス
プロキシコンストラクタ内でPropertyHandlerTraitを通じて依存解決が実行されます。
foreach ($properties as $property) {
$annotation = PropertyAnnotationReader::read($property);
if ($annotation instanceof Injection) {
$container = ApplicationContext::getContainer();
$reflection = ReflectionManager::reflectProperty($property);
$reflection->setValue($instance, $container->get($annotation->target));
}
}
ReflectionManager経由でプロパティにアクセスし、DIコンテナからインスタンスを取得してバインディングを行います。
実装構造比較
| 要素 | オリジナルクラス | プロキシクラス |
|---|---|---|
| トレイト使用 | なし | ProxyTrait, PropertyHandlerTrait |
| コンストラクタ | 通常の初期化 | __handlePropertyHandler呼び出し |
| メソッド実装 | 通常実装 | クロージャにラップ |