refactor(di): clean up imports and improve formatting in CyclicDependencyException

This commit is contained in:
2025-11-03 21:14:35 +01:00
parent 247a046f51
commit f1888b0448

View File

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