diff --git a/src/Framework/DI/Exceptions/CyclicDependencyException.php b/src/Framework/DI/Exceptions/CyclicDependencyException.php index 61a1195b..d92862d9 100644 --- a/src/Framework/DI/Exceptions/CyclicDependencyException.php +++ b/src/Framework/DI/Exceptions/CyclicDependencyException.php @@ -4,9 +4,8 @@ declare(strict_types=1); namespace App\Framework\DI\Exceptions; -use App\Framework\DI\Initializer; +use App\Framework\DI\Container; use App\Framework\DI\InitializerDependencyAnalyzer; -use App\Framework\Discovery\Results\DiscoveryRegistry; use App\Framework\Exception\ExceptionContext; final class CyclicDependencyException extends ContainerException @@ -108,10 +107,10 @@ final class CyclicDependencyException extends ContainerException // Spezifische Hinweise für Initializer-Zyklen if ($initializerInfo['isInitializerCycle']) { $message .= "⚠️ Initializer-Zyklus erkannt!\n\n"; - + $dependencyAnalysis = $initializerInfo['dependencyAnalysis'] ?? null; $hasContainerGetCalls = $dependencyAnalysis !== null && $dependencyAnalysis['hasContainerGetCalls']; - + // Warnung über container->get() Anti-Pattern if ($hasContainerGetCalls) { $message .= " ⚠️ WICHTIG: Initializer verwendet container->get() Aufrufe!\n\n"; @@ -123,19 +122,19 @@ final class CyclicDependencyException extends ContainerException $message .= " • Das macht Dependencies explizit und nachvollziehbar\n"; $message .= " • Erleichtert die Dependency-Analyse und Fehlerdiagnose\n\n"; } - + // Finde welche Dependency das Interface benötigt $problematicInfo = $this->findProblematicDependency( $initializerInfo['initializerDependencies'] ?? [], $this->cycleStart ); - + $problematicDependency = $problematicInfo['problematicDependency']; $fullPath = $problematicInfo['fullPath'] ?? null; - + if ($problematicDependency !== null) { $message .= " ✅ Problem-Dependency identifiziert: '{$problematicDependency}'\n\n"; - + // Zeige vollständigen Pfad wenn verfügbar if ($fullPath !== null && count($fullPath) > 2) { $pathStr = implode(' → ', $fullPath); @@ -161,7 +160,7 @@ final class CyclicDependencyException extends ContainerException } $message .= "\n"; } - + $message .= "🔧 Spezifische Lösung für Initializer-Zyklen:\n"; $message .= " • Initializer sollte das Interface NICHT im Constructor benötigen\n"; if ($problematicDependency !== null) { @@ -211,19 +210,19 @@ final class CyclicDependencyException extends ContainerException if (!empty($allDependencies)) { $message .= " ↓ benötigt folgende Dependencies:\n"; - + // Finde problematische Dependency für Hervorhebung $problematicInfo = $this->findProblematicDependency($allDependencies, $interface); $problematicDependency = $problematicInfo['problematicDependency']; $fullPath = $problematicInfo['fullPath'] ?? null; - + $constructorDeps = $dependencyAnalysis !== null ? $dependencyAnalysis['constructorDeps'] : []; $containerGetDeps = $dependencyAnalysis !== null ? $dependencyAnalysis['containerGetDeps'] : []; - + foreach ($allDependencies as $index => $dependency) { $isLast = $index === count($allDependencies) - 1; $arrow = $isLast ? '└─→' : '├─→'; - + // Bestimme Quelle der Dependency $source = ''; if (in_array($dependency, $containerGetDeps, true)) { @@ -231,7 +230,7 @@ final class CyclicDependencyException extends ContainerException } elseif (in_array($dependency, $constructorDeps, true)) { $source = ' (Constructor)'; } - + $highlight = ($dependency === $problematicDependency) ? ' ⚠️ (benötigt Interface!)' : ''; $message .= " {$arrow} '{$dependency}'{$source}{$highlight}\n"; } @@ -261,14 +260,14 @@ final class CyclicDependencyException extends ContainerException { // Verwende Analyzer aus Exception (wenn verfügbar) $analyzer = $this->dependencyAnalyzer ?? new InitializerDependencyAnalyzer(); - + // Methode 1: Rekursive Suche - Finde vollständigen Pfad für jede Dependency foreach ($initializerDependencies as $dependency) { // Überspringe Container selbst if ($dependency === Container::class || $dependency === 'App\Framework\DI\Container') { continue; } - + $path = $analyzer->findDependencyPathToInterface($dependency, $interface); if ($path !== null && !empty($path)) { // Der erste Eintrag im Pfad ist die Dependency, die das Interface benötigt @@ -279,7 +278,7 @@ final class CyclicDependencyException extends ContainerException ]; } } - + // Methode 2: Prüfe welche Dependency in der dependency chain vor dem Interface vorkommt foreach ($this->chainBeforeCycle as $classInChain) { if (in_array($classInChain, $initializerDependencies, true)) { @@ -312,7 +311,7 @@ final class CyclicDependencyException extends ContainerException if ($dependency === Container::class || $dependency === 'App\Framework\DI\Container') { continue; } - + if ($this->dependencyNeedsInterface($dependency, $interface)) { return [ 'problematicDependency' => $dependency, @@ -349,18 +348,18 @@ final class CyclicDependencyException extends ContainerException // 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) { return true; } } - + // Union Types (PHP 8.0+) if ($type instanceof \ReflectionUnionType) { foreach ($type->getTypes() as $subType) { @@ -371,7 +370,7 @@ final class CyclicDependencyException extends ContainerException } } } - + // Intersection Types (PHP 8.1+) if ($type instanceof \ReflectionIntersectionType) { foreach ($type->getTypes() as $subType) { @@ -445,7 +444,7 @@ final class CyclicDependencyException extends ContainerException // Analysiere Dependencies des Initializers $analyzer = $this->dependencyAnalyzer ?? new InitializerDependencyAnalyzer(); $dependencyAnalysis = $analyzer->analyze($initializerClass); - + // Kombiniere Constructor- und container->get() Dependencies $allDependencies = array_merge( $dependencyAnalysis['constructorDeps'], @@ -477,13 +476,13 @@ final class CyclicDependencyException extends ContainerException $interfaceName = basename(str_replace('\\', '/', $interface)); $suggestedName = str_replace('Interface', '', $interfaceName) . 'Initializer'; $namespace = substr($interface, 0, strrpos($interface, '\\')); - + // Strategie 1: Suche im gleichen Namespace (falls Interface nicht in Contracts ist) $suggestedClass = $namespace . '\\' . $suggestedName; if (class_exists($suggestedClass)) { return $suggestedClass; } - + // Strategie 2: Entferne "Contracts" aus dem Namespace (Initializer sind normalerweise nicht im Contracts-Namespace) // App\Framework\LiveComponents\Contracts\ComponentRegistryInterface // -> App\Framework\LiveComponents\ComponentRegistryInitializer @@ -494,7 +493,7 @@ final class CyclicDependencyException extends ContainerException return $suggestedClass; } } - + // Strategie 3: Suche im übergeordneten Namespace (einen Level höher) if (strrpos($namespace, '\\') !== false) { $parentNamespace = substr($namespace, 0, strrpos($namespace, '\\')); @@ -503,7 +502,7 @@ final class CyclicDependencyException extends ContainerException return $suggestedClass; } } - + // Strategie 4: Suche mit vollständigem App\Framework\ Präfix // Für: App\Framework\LiveComponents\Contracts\ComponentRegistryInterface // Suche: App\Framework\LiveComponents\ComponentRegistryInitializer @@ -512,7 +511,7 @@ final class CyclicDependencyException extends ContainerException // Entferne 'Contracts' falls vorhanden $filteredParts = array_filter($interfaceParts, fn($part) => $part !== 'Contracts'); $filteredParts = array_values($filteredParts); - + // Baue Namespace ohne Interface-Name, aber mit Initializer-Name $baseNamespace = implode('\\', array_slice($filteredParts, 0, -1)); $suggestedClass = $baseNamespace . '\\' . $suggestedName;