feat(di): improve initializer error handling with FailedInitializerRegistry

- Add FailedInitializerRegistry to track failed initializers
- Add FailedInitializer value object to store failure context
- Enhance exception messages with failed initializer context
- Improve ClassNotInstantiable and ClassResolutionException with detailed context
- Update InitializerProcessor to register failed initializers
This commit is contained in:
2025-11-03 16:48:13 +01:00
parent c4a4f6de07
commit 8c264f3781
7 changed files with 348 additions and 22 deletions

View File

@@ -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\DI\FailedInitializerRegistry;
use App\Framework\DI\ValueObjects\FailedInitializer;
use App\Framework\Logging\Logger;
use App\Framework\Logging\ValueObjects\LogContext;
use App\Framework\Metrics\FrameworkMetricsCollector;
@@ -217,6 +219,10 @@ final class DefaultContainer implements Container
// Try to get DiscoveryRegistry from container and include discovered initializers
$discoveredInitializers = [];
$matchingInitializers = [];
$suggestedInitializer = null;
$failedInitializer = null;
if ($this->has(DiscoveryRegistry::class)) {
try {
$discoveryRegistry = $this->get(DiscoveryRegistry::class);
@@ -227,17 +233,53 @@ final class DefaultContainer implements Container
fn($attr) => $attr->className->getFullyQualified(),
$initializerResults
);
// Spezielle Behandlung für Interfaces: Suche nach Initializern die dieses Interface zurückgeben
if (interface_exists($class)) {
foreach ($initializerResults as $initializer) {
$returnType = $initializer->additionalData['return'] ?? null;
if ($returnType === $class) {
$matchingInitializers[] = $initializer->className->getFullyQualified();
}
}
// Vorschlag basierend auf Interface-Name (z.B. ComponentRegistryInterface -> ComponentRegistryInitializer)
$interfaceName = basename(str_replace('\\', '/', $class));
$suggestedName = str_replace('Interface', '', $interfaceName) . 'Initializer';
foreach ($discoveredInitializers as $initializer) {
if (str_contains($initializer, $suggestedName)) {
$suggestedInitializer = $initializer;
break;
}
}
}
}
} catch (\Throwable $e) {
// Silently ignore errors when trying to access DiscoveryRegistry to avoid masking the original error
}
}
// Prüfe ob ein Initializer für dieses Interface fehlgeschlagen ist
if ($this->has(FailedInitializerRegistry::class)) {
try {
$failedRegistry = $this->get(FailedInitializerRegistry::class);
if ($failedRegistry->hasFailedInitializer($class)) {
$failedInitializer = $failedRegistry->getFailedInitializer($class);
}
} catch (\Throwable $e) {
// Silently ignore errors when trying to access FailedInitializerRegistry
}
}
throw ClassNotInstantiable::fromContainerContext(
class: $class,
dependencyChain: $this->resolving,
availableBindings: $availableBindings,
discoveredInitializers: $discoveredInitializers
discoveredInitializers: $discoveredInitializers,
matchingInitializers: $matchingInitializers,
suggestedInitializer: $suggestedInitializer,
failedInitializer: $failedInitializer
);
}
@@ -261,12 +303,52 @@ final class DefaultContainer implements Container
default => 'object'
};
// Für callable bindings: Prüfe ob ein Initializer fehlgeschlagen ist
$failedInitializer = null;
$matchingInitializer = null;
if ($bindingType === 'callable') {
// Prüfe ob ein fehlgeschlagener Initializer für diese Klasse existiert
if ($this->has(FailedInitializerRegistry::class)) {
try {
$failedRegistry = $this->get(FailedInitializerRegistry::class);
if ($failedRegistry->hasFailedInitializer($class)) {
$failedInitializer = $failedRegistry->getFailedInitializer($class);
}
} catch (\Throwable $registryError) {
// Silently ignore errors when trying to access FailedInitializerRegistry
}
}
// Suche auch in DiscoveryRegistry nach Initializern die diese Klasse zurückgeben
if ($failedInitializer === null && $this->has(DiscoveryRegistry::class)) {
try {
$discoveryRegistry = $this->get(DiscoveryRegistry::class);
$initializerResults = $discoveryRegistry->attributes->get(Initializer::class);
if (! empty($initializerResults)) {
foreach ($initializerResults as $initializer) {
$returnType = $initializer->additionalData['return'] ?? null;
if ($returnType === $class) {
$matchingInitializer = $initializer->className->getFullyQualified();
break;
}
}
}
} catch (\Throwable $registryError) {
// Silently ignore errors when trying to access DiscoveryRegistry
}
}
}
throw ClassResolutionException::fromBindingResolution(
class: $class,
previous: $e,
availableBindings: array_keys($this->bindings->getAllBindings()),
dependencyChain: $this->resolving,
bindingType: $bindingType
bindingType: $bindingType,
failedInitializer: $failedInitializer,
matchingInitializer: $matchingInitializer
);
}
}