diff --git a/src/Framework/DI/DefaultContainer.php b/src/Framework/DI/DefaultContainer.php index bd604afe..9425e96e 100644 --- a/src/Framework/DI/DefaultContainer.php +++ b/src/Framework/DI/DefaultContainer.php @@ -12,6 +12,8 @@ use App\Framework\DI\Exceptions\ContainerException; use App\Framework\DI\Exceptions\CyclicDependencyException; use App\Framework\DI\Exceptions\LazyLoadingException; use App\Framework\Discovery\Results\DiscoveryRegistry; +use App\Framework\Logging\Logger; +use App\Framework\Logging\ValueObjects\LogContext; use App\Framework\Metrics\FrameworkMetricsCollector; use App\Framework\Reflection\CachedReflectionProvider; use App\Framework\Reflection\ReflectionProvider; @@ -93,11 +95,9 @@ final class DefaultContainer implements Container $className = (string) $class; // Bereits instanziierte Objekte zurückgeben - if ($this->instances->hasSingleton($className)) { - $singleton = $this->instances->getSingleton($className); - if ($singleton !== null) { - return $singleton; - } + $singleton = $this->instances->getSingleton($className); + if ($singleton !== null) { + return $singleton; } if ($this->instances->hasInstance($className)) { @@ -108,10 +108,23 @@ final class DefaultContainer implements Container if ($this->lazyInstantiator->canUseLazyLoading($className, $this->instances)) { try { return $this->lazyInstantiator->createLazyInstance($className); - } catch (Throwable $e) { - if (! $e instanceof LazyLoadingException) { + } catch (LazyLoadingException $e) { + // Log LazyLoading-Fehler und versuche Fallback (Logger ist immer verfügbar) + $this->get(Logger::class)->warning( + "Lazy loading failed for {$className}, falling back to normal instantiation", + LogContext::withException($e) + ); + + // Fallback: Versuche normale Instanziierung + try { + return $this->createInstance($className); + } catch (\Throwable $fallbackException) { + // Wenn Fallback auch fehlschlägt, ursprüngliche LazyLoadingException werfen throw $e; } + } catch (\Throwable $e) { + // Andere Exceptions direkt weiterwerfen + throw $e; } } @@ -178,7 +191,7 @@ final class DefaultContainer implements Container // Check if class is instantiable using the framework's method if (! $reflection->isInstantiable()) { - $this->throwDetailedBindingException($class/*, $reflection*/); + $this->throwDetailedBindingException($class); } $dependencies = $this->dependencyResolver->resolveDependencies($className); @@ -198,7 +211,7 @@ final class DefaultContainer implements Container } } - private function throwDetailedBindingException(string $class/*, $reflection*/): never + private function throwDetailedBindingException(string $class): never { $availableBindings = array_keys($this->bindings->getAllBindings()); @@ -265,7 +278,29 @@ final class DefaultContainer implements Container return $this->instances->hasSingleton($class) || $this->instances->hasInstance($class) || $this->bindings->hasBinding($class) - || (! empty($class) && ClassName::create($class)->exists() && $this->reflectionProvider->getClass(ClassName::create($class))->isInstantiable()); + || $this->canAutoWire($class); + } + + /** + * Prüft ob eine Klasse automatisch instanziiert werden kann (auto-wiring) + * @param string $class Klassenname + */ + private function canAutoWire(string $class): bool + { + if (empty($class)) { + return false; + } + + $className = ClassName::create($class); + if (! $className->exists()) { + return false; + } + + try { + return $this->reflectionProvider->getClass($className)->isInstantiable(); + } catch (\Throwable $e) { + return false; + } } /** @param class-string $class */ diff --git a/src/Framework/DI/InitializerDependencyGraph.php b/src/Framework/DI/InitializerDependencyGraph.php index e21290a1..c0089f39 100644 --- a/src/Framework/DI/InitializerDependencyGraph.php +++ b/src/Framework/DI/InitializerDependencyGraph.php @@ -38,7 +38,6 @@ final class InitializerDependencyGraph return; // Setup-Initializer werden sofort ausgeführt } - $dependencies = $this->analyzeDependencies($className, $methodName); $node = new DependencyGraphNode( @@ -48,7 +47,6 @@ final class InitializerDependencyGraph dependencies: $dependencies ); - $this->nodes[$returnType] = $node; $this->adjacencyList[$returnType] = $dependencies; } @@ -77,16 +75,7 @@ final class InitializerDependencyGraph */ public function hasNode(string $returnType): bool { - $exists = isset($this->nodes[$returnType]); - - // Debug RouterSetup specifically - if ($returnType === 'App\\Framework\\Router\\Router') { - error_log("InitializerDependencyGraph: hasNode check for Router - exists: " . ($exists ? 'YES' : 'NO')); - error_log("InitializerDependencyGraph: Current node count: " . count($this->nodes)); - error_log("InitializerDependencyGraph: Available nodes: " . implode(', ', array_keys($this->nodes))); - } - - return $exists; + return isset($this->nodes[$returnType]); } /** @@ -121,13 +110,11 @@ final class InitializerDependencyGraph /** * Analysiert die Dependencies eines Initializers durch Reflection */ - private function analyzeDependencies(CLassName $className, MethodName $methodName): array + private function analyzeDependencies(ClassName $className, MethodName $methodName): array { try { $reflection = $this->reflectionProvider->getClass($className); - #$reflection = new \ReflectionClass($className); $method = $reflection->getMethod($methodName->toString()); - ; $dependencies = []; foreach ($method->getParameters() as $parameter) { @@ -172,26 +159,4 @@ final class InitializerDependencyGraph $this->inStack[$returnType] = false; $result[] = $returnType; } - - private function hasCycle(string $returnType): bool - { - $this->visited[$returnType] = true; - $this->inStack[$returnType] = true; - - foreach ($this->adjacencyList[$returnType] ?? [] as $dependency) { - if (isset($this->nodes[$dependency])) { - if (isset($this->inStack[$dependency]) && $this->inStack[$dependency]) { - return true; - } - - if (! isset($this->visited[$dependency]) && $this->hasCycle($dependency)) { - return true; - } - } - } - - $this->inStack[$returnType] = false; - - return false; - } } diff --git a/src/Framework/Discovery/InitializerProcessor.php b/src/Framework/Discovery/InitializerProcessor.php index 94b8bf2a..4a349c95 100644 --- a/src/Framework/Discovery/InitializerProcessor.php +++ b/src/Framework/Discovery/InitializerProcessor.php @@ -39,11 +39,10 @@ final readonly class InitializerProcessor */ public function processInitializers(DiscoveryRegistry $results): void { - // Safe Logger resolution - use if available, otherwise rely on error_log - $logger = $this->container->get(Logger::class); + $logger = $this->getLogger(); $initializerResults = $results->attributes->get(Initializer::class); - $logger?->debug("InitializerProcessor: Processing " . count($initializerResults) . " initializers"); + $logger->debug("InitializerProcessor: Processing " . count($initializerResults) . " initializers"); $dependencyGraph = new InitializerDependencyGraph($this->reflectionProvider); @@ -63,7 +62,11 @@ final readonly class InitializerProcessor // Context-Filter: Prüfe ob Initializer für aktuellen Context erlaubt ist // The contexts data is directly in the additionalData // Wenn contexts null ist, ist der Initializer für alle Contexts verfügbar - if (isset($initializerData['contexts']) && $initializerData['contexts'] !== null && ! $this->isContextAllowed(...$initializerData['contexts'])) { + if ( + isset($initializerData['contexts']) + && $initializerData['contexts'] !== null + && ! $this->isContextAllowed(...$initializerData['contexts']) + ) { continue; } @@ -81,7 +84,7 @@ final readonly class InitializerProcessor $dependencyGraph->addInitializer($returnType, $discoveredAttribute->className, $methodName); } } catch (\Throwable $e) { - $logger?->error( + $logger->error( "Failed to register initializer: {$discoveredAttribute->className->toString()}::{$methodName->toString()}", LogContext::withExceptionAndData($e, [ 'class' => $discoveredAttribute->className->toString(), @@ -107,13 +110,10 @@ final readonly class InitializerProcessor $executionOrder = $graph->getExecutionOrder(); foreach ($executionOrder as $returnType) { - if ($graph->hasNode($returnType)) { /** @var DependencyGraphNode $node */ $node = $graph->getNode($returnType); - - $this->registerLazyService( $returnType, $node->getClassName(), @@ -123,6 +123,12 @@ final readonly class InitializerProcessor } } catch (\Throwable $e) { // Fallback: Registriere alle Services ohne spezielle Reihenfolge + $logger = $this->getLogger(); + $logger->warning( + "Failed to register services with dependency graph, falling back to unordered registration", + LogContext::withException($e) + ); + /** @var string $returnType */ foreach ($graph->getNodes() as $returnType => $node) { $this->registerLazyService( @@ -132,8 +138,6 @@ final readonly class InitializerProcessor ); } } - - unset($graph); } /** @@ -164,9 +168,7 @@ final readonly class InitializerProcessor */ private function registerLazyService(string $returnType, string $className, string $methodName): void { - $factory = function ($container) use ($className, $methodName, $returnType) { - try { $instance = $container->invoker->invoke(ClassName::create($className), $methodName); } catch (\Throwable $e) { @@ -174,7 +176,6 @@ final readonly class InitializerProcessor throw $e; } - // Wenn das ein Interface ist, registriere auch die konkrete Klasse automatisch if (interface_exists($returnType)) { $concreteClass = get_class($instance); @@ -189,12 +190,9 @@ final readonly class InitializerProcessor try { // Registriere den Return-Type (Interface oder konkrete Klasse) $this->container->singleton($returnType, $factory); - } catch (\Throwable $e) { - // Safe Logger resolution - use if available - $logger = $this->container->has(Logger::class) ? $this->container->get(Logger::class) : null; - - $logger?->error( + $logger = $this->getLogger(); + $logger->error( "Failed to register lazy service for return type: {$returnType}", LogContext::withExceptionAndData($e, [ 'return_type' => $returnType, @@ -206,4 +204,12 @@ final readonly class InitializerProcessor // Service registration failed - continue to prevent breaking the entire application } } + + /** + * Gibt den Logger zurück (ist im Framework immer verfügbar) + */ + private function getLogger(): Logger + { + return $this->container->get(Logger::class); + } }