refactor: improve lazy loading and logging in DI container
- Enhance lazy loading fallback with detailed logging of failures - Simplify singleton resolution logic, removing redundancy - Add `canAutoWire` method for cleaner class auto-wiring checks - Optimize logging resolution in `InitializerProcessor` - Remove unused code and redundant debug logs across DI components
This commit is contained in:
@@ -12,6 +12,8 @@ use App\Framework\DI\Exceptions\ContainerException;
|
|||||||
use App\Framework\DI\Exceptions\CyclicDependencyException;
|
use App\Framework\DI\Exceptions\CyclicDependencyException;
|
||||||
use App\Framework\DI\Exceptions\LazyLoadingException;
|
use App\Framework\DI\Exceptions\LazyLoadingException;
|
||||||
use App\Framework\Discovery\Results\DiscoveryRegistry;
|
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\Metrics\FrameworkMetricsCollector;
|
||||||
use App\Framework\Reflection\CachedReflectionProvider;
|
use App\Framework\Reflection\CachedReflectionProvider;
|
||||||
use App\Framework\Reflection\ReflectionProvider;
|
use App\Framework\Reflection\ReflectionProvider;
|
||||||
@@ -93,11 +95,9 @@ final class DefaultContainer implements Container
|
|||||||
$className = (string) $class;
|
$className = (string) $class;
|
||||||
|
|
||||||
// Bereits instanziierte Objekte zurückgeben
|
// Bereits instanziierte Objekte zurückgeben
|
||||||
if ($this->instances->hasSingleton($className)) {
|
$singleton = $this->instances->getSingleton($className);
|
||||||
$singleton = $this->instances->getSingleton($className);
|
if ($singleton !== null) {
|
||||||
if ($singleton !== null) {
|
return $singleton;
|
||||||
return $singleton;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->instances->hasInstance($className)) {
|
if ($this->instances->hasInstance($className)) {
|
||||||
@@ -108,10 +108,23 @@ final class DefaultContainer implements Container
|
|||||||
if ($this->lazyInstantiator->canUseLazyLoading($className, $this->instances)) {
|
if ($this->lazyInstantiator->canUseLazyLoading($className, $this->instances)) {
|
||||||
try {
|
try {
|
||||||
return $this->lazyInstantiator->createLazyInstance($className);
|
return $this->lazyInstantiator->createLazyInstance($className);
|
||||||
} catch (Throwable $e) {
|
} catch (LazyLoadingException $e) {
|
||||||
if (! $e instanceof LazyLoadingException) {
|
// 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;
|
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
|
// Check if class is instantiable using the framework's method
|
||||||
if (! $reflection->isInstantiable()) {
|
if (! $reflection->isInstantiable()) {
|
||||||
$this->throwDetailedBindingException($class/*, $reflection*/);
|
$this->throwDetailedBindingException($class);
|
||||||
}
|
}
|
||||||
|
|
||||||
$dependencies = $this->dependencyResolver->resolveDependencies($className);
|
$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());
|
$availableBindings = array_keys($this->bindings->getAllBindings());
|
||||||
|
|
||||||
@@ -265,7 +278,29 @@ final class DefaultContainer implements Container
|
|||||||
return $this->instances->hasSingleton($class)
|
return $this->instances->hasSingleton($class)
|
||||||
|| $this->instances->hasInstance($class)
|
|| $this->instances->hasInstance($class)
|
||||||
|| $this->bindings->hasBinding($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 */
|
/** @param class-string $class */
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ final class InitializerDependencyGraph
|
|||||||
return; // Setup-Initializer werden sofort ausgeführt
|
return; // Setup-Initializer werden sofort ausgeführt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$dependencies = $this->analyzeDependencies($className, $methodName);
|
$dependencies = $this->analyzeDependencies($className, $methodName);
|
||||||
|
|
||||||
$node = new DependencyGraphNode(
|
$node = new DependencyGraphNode(
|
||||||
@@ -48,7 +47,6 @@ final class InitializerDependencyGraph
|
|||||||
dependencies: $dependencies
|
dependencies: $dependencies
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
$this->nodes[$returnType] = $node;
|
$this->nodes[$returnType] = $node;
|
||||||
$this->adjacencyList[$returnType] = $dependencies;
|
$this->adjacencyList[$returnType] = $dependencies;
|
||||||
}
|
}
|
||||||
@@ -77,16 +75,7 @@ final class InitializerDependencyGraph
|
|||||||
*/
|
*/
|
||||||
public function hasNode(string $returnType): bool
|
public function hasNode(string $returnType): bool
|
||||||
{
|
{
|
||||||
$exists = isset($this->nodes[$returnType]);
|
return 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -121,13 +110,11 @@ final class InitializerDependencyGraph
|
|||||||
/**
|
/**
|
||||||
* Analysiert die Dependencies eines Initializers durch Reflection
|
* Analysiert die Dependencies eines Initializers durch Reflection
|
||||||
*/
|
*/
|
||||||
private function analyzeDependencies(CLassName $className, MethodName $methodName): array
|
private function analyzeDependencies(ClassName $className, MethodName $methodName): array
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$reflection = $this->reflectionProvider->getClass($className);
|
$reflection = $this->reflectionProvider->getClass($className);
|
||||||
#$reflection = new \ReflectionClass($className);
|
|
||||||
$method = $reflection->getMethod($methodName->toString());
|
$method = $reflection->getMethod($methodName->toString());
|
||||||
;
|
|
||||||
$dependencies = [];
|
$dependencies = [];
|
||||||
|
|
||||||
foreach ($method->getParameters() as $parameter) {
|
foreach ($method->getParameters() as $parameter) {
|
||||||
@@ -172,26 +159,4 @@ final class InitializerDependencyGraph
|
|||||||
$this->inStack[$returnType] = false;
|
$this->inStack[$returnType] = false;
|
||||||
$result[] = $returnType;
|
$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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,11 +39,10 @@ final readonly class InitializerProcessor
|
|||||||
*/
|
*/
|
||||||
public function processInitializers(DiscoveryRegistry $results): void
|
public function processInitializers(DiscoveryRegistry $results): void
|
||||||
{
|
{
|
||||||
// Safe Logger resolution - use if available, otherwise rely on error_log
|
$logger = $this->getLogger();
|
||||||
$logger = $this->container->get(Logger::class);
|
|
||||||
$initializerResults = $results->attributes->get(Initializer::class);
|
$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);
|
$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
|
// Context-Filter: Prüfe ob Initializer für aktuellen Context erlaubt ist
|
||||||
// The contexts data is directly in the additionalData
|
// The contexts data is directly in the additionalData
|
||||||
// Wenn contexts null ist, ist der Initializer für alle Contexts verfügbar
|
// 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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +84,7 @@ final readonly class InitializerProcessor
|
|||||||
$dependencyGraph->addInitializer($returnType, $discoveredAttribute->className, $methodName);
|
$dependencyGraph->addInitializer($returnType, $discoveredAttribute->className, $methodName);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$logger?->error(
|
$logger->error(
|
||||||
"Failed to register initializer: {$discoveredAttribute->className->toString()}::{$methodName->toString()}",
|
"Failed to register initializer: {$discoveredAttribute->className->toString()}::{$methodName->toString()}",
|
||||||
LogContext::withExceptionAndData($e, [
|
LogContext::withExceptionAndData($e, [
|
||||||
'class' => $discoveredAttribute->className->toString(),
|
'class' => $discoveredAttribute->className->toString(),
|
||||||
@@ -107,13 +110,10 @@ final readonly class InitializerProcessor
|
|||||||
$executionOrder = $graph->getExecutionOrder();
|
$executionOrder = $graph->getExecutionOrder();
|
||||||
|
|
||||||
foreach ($executionOrder as $returnType) {
|
foreach ($executionOrder as $returnType) {
|
||||||
|
|
||||||
if ($graph->hasNode($returnType)) {
|
if ($graph->hasNode($returnType)) {
|
||||||
/** @var DependencyGraphNode $node */
|
/** @var DependencyGraphNode $node */
|
||||||
$node = $graph->getNode($returnType);
|
$node = $graph->getNode($returnType);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$this->registerLazyService(
|
$this->registerLazyService(
|
||||||
$returnType,
|
$returnType,
|
||||||
$node->getClassName(),
|
$node->getClassName(),
|
||||||
@@ -123,6 +123,12 @@ final readonly class InitializerProcessor
|
|||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
// Fallback: Registriere alle Services ohne spezielle Reihenfolge
|
// 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 */
|
/** @var string $returnType */
|
||||||
foreach ($graph->getNodes() as $returnType => $node) {
|
foreach ($graph->getNodes() as $returnType => $node) {
|
||||||
$this->registerLazyService(
|
$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
|
private function registerLazyService(string $returnType, string $className, string $methodName): void
|
||||||
{
|
{
|
||||||
|
|
||||||
$factory = function ($container) use ($className, $methodName, $returnType) {
|
$factory = function ($container) use ($className, $methodName, $returnType) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$instance = $container->invoker->invoke(ClassName::create($className), $methodName);
|
$instance = $container->invoker->invoke(ClassName::create($className), $methodName);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -174,7 +176,6 @@ final readonly class InitializerProcessor
|
|||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Wenn das ein Interface ist, registriere auch die konkrete Klasse automatisch
|
// Wenn das ein Interface ist, registriere auch die konkrete Klasse automatisch
|
||||||
if (interface_exists($returnType)) {
|
if (interface_exists($returnType)) {
|
||||||
$concreteClass = get_class($instance);
|
$concreteClass = get_class($instance);
|
||||||
@@ -189,12 +190,9 @@ final readonly class InitializerProcessor
|
|||||||
try {
|
try {
|
||||||
// Registriere den Return-Type (Interface oder konkrete Klasse)
|
// Registriere den Return-Type (Interface oder konkrete Klasse)
|
||||||
$this->container->singleton($returnType, $factory);
|
$this->container->singleton($returnType, $factory);
|
||||||
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
// Safe Logger resolution - use if available
|
$logger = $this->getLogger();
|
||||||
$logger = $this->container->has(Logger::class) ? $this->container->get(Logger::class) : null;
|
$logger->error(
|
||||||
|
|
||||||
$logger?->error(
|
|
||||||
"Failed to register lazy service for return type: {$returnType}",
|
"Failed to register lazy service for return type: {$returnType}",
|
||||||
LogContext::withExceptionAndData($e, [
|
LogContext::withExceptionAndData($e, [
|
||||||
'return_type' => $returnType,
|
'return_type' => $returnType,
|
||||||
@@ -206,4 +204,12 @@ final readonly class InitializerProcessor
|
|||||||
// Service registration failed - continue to prevent breaking the entire application
|
// 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user