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;
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;