*/ private array $nodes = []; /** @var array> */ private array $adjacencyList = []; /** @var array */ private array $visited = []; /** @var array */ private array $inStack = []; public function __construct( private readonly ReflectionProvider $reflectionProvider, ) { } public function addInitializer(string $returnType, ClassName $className, MethodName $methodName): void { if ($returnType === 'null' || $returnType === 'void') { return; // Setup-Initializer werden sofort ausgeführt } $dependencies = $this->analyzeDependencies($className, $methodName); $node = new DependencyGraphNode( returnType: $returnType, className: $className, methodName: $methodName, dependencies: $dependencies ); // Debug RouterSetup specifically if (str_contains($className->toString(), 'RouterSetup')) { error_log("InitializerDependencyGraph: Adding RouterSetup node - " . $className->toString() . " -> " . $returnType); error_log("InitializerDependencyGraph: RouterSetup dependencies: " . implode(', ', $dependencies)); } $this->nodes[$returnType] = $node; $this->adjacencyList[$returnType] = $dependencies; } /** * Berechnet die optimale Ausführungsreihenfolge basierend auf Dependencies * @return array Sortierte Liste von Return-Types */ public function getExecutionOrder(): array { $this->visited = []; $this->inStack = []; $result = []; foreach (array_keys($this->nodes) as $returnType) { if (! isset($this->visited[$returnType])) { $this->topologicalSort($returnType, $result); } } return array_reverse($result); } /** * Prüft ob ein Node für den gegebenen Return-Type existiert */ public function hasNode(string $returnType): bool { $exists = isset($this->nodes[$returnType]); // Debug RouterSetup specifically if ($returnType === 'App\\Framework\\Router\\Router') { error_log("InitializerDependencyGraph: hasNode check for Router - exists: " . ($exists ? 'YES' : 'NO')); error_log("InitializerDependencyGraph: Current node count: " . count($this->nodes)); error_log("InitializerDependencyGraph: Available nodes: " . implode(', ', array_keys($this->nodes))); } return $exists; } /** * Gibt einen spezifischen Node zurück */ public function getNode(string $returnType): ?DependencyGraphNode { return $this->nodes[$returnType] ?? null; } /** * Gibt alle Nodes zurück * * @return array */ public function getNodes(): array { return $this->nodes; } /** * Gibt alle Nodes als Array zurück (backward compatibility) * * @deprecated Use getNodes() for DependencyGraphNode objects * @return array */ public function getNodesAsArray(): array { return array_map(fn ($node) => $node->toArray(), $this->nodes); } /** * Analysiert die Dependencies eines Initializers durch Reflection */ private function analyzeDependencies(CLassName $className, MethodName $methodName): array { try { $reflection = $this->reflectionProvider->getClass($className); #$reflection = new \ReflectionClass($className); $method = $reflection->getMethod($methodName->toString()); ; $dependencies = []; foreach ($method->getParameters() as $parameter) { $paramType = $parameter->getType(); if ($paramType instanceof \ReflectionNamedType) { $typeName = $paramType->getName(); // Nur Klassen/Interfaces als Dependencies, keine primitiven Typen if (! empty($typeName) && ClassName::create($typeName)->exists()) { $dependencies[] = $typeName; } } } return $dependencies; } catch (\Throwable $e) { return []; } } /** * Topologische Sortierung mit Cycle-Detection */ private function topologicalSort(string $returnType, array &$result): void { $this->visited[$returnType] = true; $this->inStack[$returnType] = true; foreach ($this->adjacencyList[$returnType] ?? [] as $dependency) { if (isset($this->nodes[$dependency])) { if (isset($this->inStack[$dependency]) && $this->inStack[$dependency]) { // Cycle detected - ignore this dependency to prevent infinite loops continue; } if (! isset($this->visited[$dependency])) { $this->topologicalSort($dependency, $result); } } } $this->inStack[$returnType] = false; $result[] = $returnType; } private function hasCycle(string $returnType): bool { $this->visited[$returnType] = true; $this->inStack[$returnType] = true; foreach ($this->adjacencyList[$returnType] ?? [] as $dependency) { if (isset($this->nodes[$dependency])) { if (isset($this->inStack[$dependency]) && $this->inStack[$dependency]) { return true; } if (! isset($this->visited[$dependency]) && $this->hasCycle($dependency)) { return true; } } } $this->inStack[$returnType] = false; return false; } }