fix: DockerSecretsResolver - don't normalize absolute paths like /var/www/html/...
Some checks failed
Deploy Application / deploy (push) Has been cancelled
Some checks failed
Deploy Application / deploy (push) Has been cancelled
This commit is contained in:
275
src/Framework/Console/Commands/InitializersCheckCommand.php
Normal file
275
src/Framework/Console/Commands/InitializersCheckCommand.php
Normal file
@@ -0,0 +1,275 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Console\Commands;
|
||||
|
||||
use App\Framework\Console\CommandGroup;
|
||||
use App\Framework\Console\ConsoleColor;
|
||||
use App\Framework\Console\ConsoleCommand;
|
||||
use App\Framework\Console\ConsoleInput;
|
||||
use App\Framework\Console\ConsoleOutputInterface;
|
||||
use App\Framework\Console\ExitCode;
|
||||
use App\Framework\Core\ValueObjects\ReturnTypeValue;
|
||||
use App\Framework\DI\Exceptions\InitializerCycleException;
|
||||
use App\Framework\DI\Initializer;
|
||||
use App\Framework\DI\InitializerDependencyAnalyzer;
|
||||
use App\Framework\DI\InitializerDependencyGraph;
|
||||
use App\Framework\Discovery\Results\DiscoveryRegistry;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveredAttribute;
|
||||
use App\Framework\Reflection\ReflectionService;
|
||||
|
||||
#[CommandGroup(
|
||||
name: 'Framework',
|
||||
description: 'Framework diagnostics and health checks',
|
||||
icon: '🔧',
|
||||
priority: 90
|
||||
)]
|
||||
final readonly class InitializersCheckCommand
|
||||
{
|
||||
public function __construct(
|
||||
private DiscoveryRegistry $discoveryRegistry,
|
||||
private ReflectionService $reflectionService,
|
||||
private InitializerDependencyAnalyzer $dependencyAnalyzer,
|
||||
private ConsoleOutputInterface $output
|
||||
) {
|
||||
}
|
||||
|
||||
#[ConsoleCommand('initializers:check', 'Check all initializers for problems (dependencies, cycles, etc.)')]
|
||||
public function check(ConsoleInput $input, ConsoleOutputInterface $output): ExitCode
|
||||
{
|
||||
$jsonOutput = $input->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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user