$visited Bereits besuchte Klassen (für Cycle-Detection) * @param array $currentPath Aktueller Pfad * @param int $depth Aktuelle Rekursionstiefe * @return array|null Vollständiger Pfad [Dep1, Dep2, ..., Interface] oder null */ public function findPathToInterface( ClassName $dependencyClass, ClassName $targetInterface, array $visited = [], array $currentPath = [], int $depth = 0 ): ?array { // Max. Rekursionstiefe erreicht if ($depth >= self::MAX_RECURSION_DEPTH) { return null; } // Cycle-Detection: Vermeide Endlosschleifen foreach ($visited as $visitedClass) { if ($visitedClass->equals($dependencyClass)) { return null; } } // Prüfe ob diese Klasse direkt das Interface benötigt if ($this->dependencyNeedsInterface($dependencyClass, $targetInterface)) { return array_merge($currentPath, [$dependencyClass, $targetInterface]); } // Rekursiv: Analysiere Dependencies dieser Klasse $dependencies = $this->getClassDependencies($dependencyClass); if (empty($dependencies)) { return null; } $newVisited = array_merge($visited, [$dependencyClass]); $newPath = array_merge($currentPath, [$dependencyClass]); foreach ($dependencies as $subDependency) { // Überspringe Container selbst (würde alle Dependencies auflisten) if ($subDependency->toString() === Container::class || $subDependency->toString() === 'App\Framework\DI\Container') { continue; } $path = $this->findPathToInterface( $subDependency, $targetInterface, $newVisited, $newPath, $depth + 1 ); if ($path !== null) { return $path; } } return null; } /** * Hole Dependencies einer Klasse (Constructor-Parameter) * * @return array */ public function getClassDependencies(ClassName $className): array { try { if (!$className->exists()) { return []; } $reflection = new \ReflectionClass($className->toString()); // Wenn es ein Interface ist, versuche die Implementierung zu finden if ($reflection->isInterface()) { $implClass = $this->interfaceResolver->findImplementation($className); if ($implClass !== null && $implClass->exists()) { $reflection = new \ReflectionClass($implClass->toString()); } else { // Kann keine Dependencies für Interfaces ohne Implementierung finden return []; } } $constructor = $reflection->getConstructor(); if ($constructor === null) { return []; } $dependencies = []; foreach ($constructor->getParameters() as $parameter) { $type = $parameter->getType(); if ($type instanceof \ReflectionNamedType && !$type->isBuiltin()) { try { $depClassName = ClassName::create($type->getName()); $dependencies[] = $depClassName; } catch (\InvalidArgumentException) { // Ungültiger Klassenname - überspringe continue; } } } return $dependencies; } catch (\Throwable) { return []; } } /** * Prüfe ob eine Klasse ein Interface benötigt (über Reflection) */ private function dependencyNeedsInterface(ClassName $dependencyClass, ClassName $interface): bool { try { if (!$dependencyClass->exists()) { return false; } $reflection = new \ReflectionClass($dependencyClass->toString()); $constructor = $reflection->getConstructor(); if ($constructor === null) { return false; } // Prüfe alle Constructor-Parameter foreach ($constructor->getParameters() as $parameter) { $type = $parameter->getType(); if ($type === null) { continue; } // Direkter NamedType if ($type instanceof \ReflectionNamedType && !$type->isBuiltin()) { if ($type->getName() === $interface->toString()) { return true; } } // Union Types (PHP 8.0+) if ($type instanceof \ReflectionUnionType) { foreach ($type->getTypes() as $subType) { if ($subType instanceof \ReflectionNamedType && !$subType->isBuiltin()) { if ($subType->getName() === $interface->toString()) { return true; } } } } // Intersection Types (PHP 8.1+) if ($type instanceof \ReflectionIntersectionType) { foreach ($type->getTypes() as $subType) { if ($subType instanceof \ReflectionNamedType && !$subType->isBuiltin()) { if ($subType->getName() === $interface->toString()) { return true; } } } } } return false; } catch (\Throwable) { return false; } } }