hasOption('json'); $output->writeLine('Checking initializers...', ConsoleColor::BRIGHT_CYAN); $output->newLine(); $initializerResults = $this->discoveryRegistry->attributes->get(Initializer::class); $totalInitializers = count($initializerResults); if ($totalInitializers === 0) { $output->writeLine('⚠️ No initializers found in discovery registry.', ConsoleColor::YELLOW); return ExitCode::FAILURE; } $output->writeLine("Found {$totalInitializers} initializer(s)", ConsoleColor::WHITE); $output->newLine(); // Build dependency graph $dependencyGraph = new InitializerDependencyGraph( $this->reflectionService, $this->dependencyAnalyzer ); $problems = []; $warnings = []; $info = []; // Phase 1: Analyze each initializer foreach ($initializerResults as $discoveredAttribute) { $initializer = $discoveredAttribute->createAttributeInstance(); if ($initializer === null) { $problems[] = [ 'type' => 'error', 'message' => "Failed to instantiate Initializer attribute", 'class' => (string) $discoveredAttribute->className, 'method' => (string) ($discoveredAttribute->methodName ?? 'unknown'), ]; continue; } $methodName = $discoveredAttribute->methodName ?? \App\Framework\Core\ValueObjects\MethodName::invoke(); $returnTypeString = $discoveredAttribute->additionalData['return'] ?? null; try { $returnType = ReturnTypeValue::fromString($returnTypeString, $discoveredAttribute->className); // Skip setup initializers (void return) if ($returnType->hasNoReturn()) { continue; } $concreteReturnType = $returnType->isSelf() ? $returnType->toClassName() : $returnType->toClassName(); // Analyze dependencies $analysis = $this->dependencyAnalyzer->analyze($discoveredAttribute->className->getFullyQualified()); // Check for missing dependencies $missingDeps = []; foreach (array_merge($analysis['constructorDeps'], $analysis['containerGetDeps']) as $dep) { // Skip Container itself if (\App\Framework\DI\InitializerDependencyAnalyzer::isContainerClass($dep)) { continue; } // Check if dependency has an initializer or is a concrete class if (!class_exists($dep) && !interface_exists($dep)) { $missingDeps[] = $dep; } } if (!empty($missingDeps)) { $warnings[] = [ 'type' => 'warning', 'message' => "Potential missing dependencies", 'class' => (string) $discoveredAttribute->className, 'method' => (string) $methodName, 'return_type' => $concreteReturnType->getFullyQualified(), 'missing_dependencies' => $missingDeps, ]; } // Add to graph // Extrahiere explizite Dependencies und Priority aus additionalData $explicitDependencies = $discoveredAttribute->additionalData['dependencies'] ?? null; $priority = (int) ($discoveredAttribute->additionalData['priority'] ?? 0); $dependencyGraph->addInitializer( $concreteReturnType->getFullyQualified(), $discoveredAttribute->className, $methodName, $explicitDependencies, $priority ); $info[] = [ 'type' => 'info', 'class' => (string) $discoveredAttribute->className, 'method' => (string) $methodName, 'return_type' => $concreteReturnType->getFullyQualified(), 'dependencies' => array_merge($analysis['constructorDeps'], $analysis['containerGetDeps']), ]; } catch (\Throwable $e) { $problems[] = [ 'type' => 'error', 'message' => "Failed to analyze initializer: {$e->getMessage()}", 'class' => (string) $discoveredAttribute->className, 'method' => (string) $methodName, 'exception' => get_class($e), ]; } } // Phase 2: Check for cycles try { $executionOrder = $dependencyGraph->getExecutionOrder(); } catch (InitializerCycleException $e) { $cycles = $e->getCycles(); $paths = $e->getDependencyPaths(); foreach ($cycles as $index => $cycle) { $path = $paths[$index] ?? $cycle; $problems[] = [ 'type' => 'error', 'message' => 'Circular dependency detected', 'cycle' => $cycle, 'dependency_path' => $path, ]; } } // Phase 3: Output results if ($jsonOutput) { $this->outputJson($output, [ 'total' => $totalInitializers, 'problems' => $problems, 'warnings' => $warnings, 'info' => $info, ]); } else { $this->outputHumanReadable($output, $problems, $warnings, $info, $dependencyGraph); } // Return exit code based on problems if (!empty($problems)) { return ExitCode::FAILURE; } if (!empty($warnings)) { return ExitCode::PARTIAL_SUCCESS; } $output->writeLine('✅ All initializers are valid', ConsoleColor::GREEN); return ExitCode::SUCCESS; } private function outputHumanReadable( ConsoleOutputInterface $output, array $problems, array $warnings, array $info, InitializerDependencyGraph $graph ): void { // Show problems if (!empty($problems)) { $output->writeLine('❌ Problems:', ConsoleColor::BRIGHT_RED); foreach ($problems as $problem) { $output->writeLine(" • {$problem['message']}", ConsoleColor::RED); if (isset($problem['class'])) { $output->writeLine(" Class: {$problem['class']}", ConsoleColor::GRAY); } if (isset($problem['cycle'])) { $cycleStr = implode(' → ', $problem['cycle']) . ' → ' . ($problem['cycle'][0] ?? ''); $output->writeLine(" Cycle: {$cycleStr}", ConsoleColor::GRAY); } if (isset($problem['dependency_path'])) { $pathStr = implode(' → ', $problem['dependency_path']); $output->writeLine(" Path: {$pathStr}", ConsoleColor::GRAY); } } $output->newLine(); } // Show warnings if (!empty($warnings)) { $output->writeLine('⚠️ Warnings:', ConsoleColor::BRIGHT_YELLOW); foreach ($warnings as $warning) { $output->writeLine(" • {$warning['message']}", ConsoleColor::YELLOW); $output->writeLine(" Class: {$warning['class']}::{$warning['method']}", ConsoleColor::GRAY); if (isset($warning['missing_dependencies'])) { $depsStr = implode(', ', $warning['missing_dependencies']); $output->writeLine(" Missing: {$depsStr}", ConsoleColor::GRAY); } } $output->newLine(); } // Show summary $output->writeLine('📊 Summary:', ConsoleColor::BRIGHT_CYAN); $output->writeLine(" Total initializers: " . count($info), ConsoleColor::WHITE); $output->writeLine(" Problems: " . count($problems), ConsoleColor::RED); $output->writeLine(" Warnings: " . count($warnings), ConsoleColor::YELLOW); // Show dependency graph visualization $output->newLine(); $output->writeLine('🔗 Dependency Graph:', ConsoleColor::BRIGHT_CYAN); try { $executionOrder = $graph->getExecutionOrder(); foreach ($executionOrder as $index => $returnType) { $node = $graph->getNode($returnType); if ($node === null) { continue; } $deps = empty($node->dependencies) ? 'no dependencies' : 'depends on: ' . implode(', ', $node->dependencies); $output->writeLine( sprintf(' %d. %s', $index + 1, $node->toString()), ConsoleColor::WHITE ); } } catch (InitializerCycleException $e) { $output->writeLine(' ⚠️ Cannot show graph due to circular dependencies', ConsoleColor::YELLOW); } } private function outputJson(ConsoleOutputInterface $output, array $data): void { $json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); $output->writeLine($json); } }