Fix: Enhance exception handling in DefaultContainer with detailed diagnostics

- Add comprehensive error messages showing dependency resolution chains
- Include available bindings in error output for troubleshooting
- Fix compatibility with framework's WrappedReflectionClass system
- Use proper isInstantiable() method instead of native reflection methods
- Provide detailed binding analysis for missing dependencies
- Include similar binding suggestions for interface resolution issues

This resolves the 500 errors by providing proper diagnostics when DI container
cannot resolve dependencies, helping identify missing bindings or configuration issues.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-12 18:37:58 +02:00
parent 8fe569a3df
commit 8040d3e7a5

View File

@@ -127,10 +127,63 @@ final class DefaultContainer implements Container
return $this->resolveBinding($class, $this->bindings->getBinding($class));
}
// Enhanced diagnostics for missing bindings
try {
$reflection = $this->reflectionProvider->getClass($className);
$dependencies = $this->dependencyResolver->resolveDependencies($className);
// Check if class is instantiable using framework's method
if (!$reflection->isInstantiable()) {
$this->throwDetailedBindingException($class, $reflection);
}
$dependencies = $this->dependencyResolver->resolveDependencies($className);
return $reflection->newInstance(...$dependencies->toArray());
} catch (\RuntimeException $e) {
// If it's already our detailed exception, just re-throw
if (str_contains($e->getMessage(), 'Dependency resolution chain:')) {
throw $e;
}
// Otherwise, wrap with binding information
throw new \RuntimeException(
"Cannot resolve class '{$class}': {$e->getMessage()}. " .
"Available bindings: " . implode(', ', array_keys($this->bindings->getAllBindings())) .
". Dependency chain: " . implode(' -> ', $this->resolving),
0,
$e
);
} catch (\ReflectionException $e) {
throw new \RuntimeException(
"Cannot resolve class '{$class}': {$e->getMessage()}. " .
"Available bindings: " . implode(', ', array_keys($this->bindings->getAllBindings())) .
". Dependency chain: " . implode(' -> ', $this->resolving),
0,
$e
);
}
}
private function throwDetailedBindingException(string $class, $reflection): never
{
$availableBindings = array_keys($this->bindings->getAllBindings());
$dependencyChain = implode(' -> ', $this->resolving);
// Look for similar interface bindings
$similarBindings = array_filter($availableBindings, function($binding) use ($class) {
return str_contains($binding, basename(str_replace('\\', '/', $class)));
});
$message = "Cannot instantiate class '{$class}': class is not instantiable (interface, abstract class, or trait).\n" .
"Dependency resolution chain: {$dependencyChain}\n" .
"Total available bindings: " . count($availableBindings) . "\n";
if (!empty($similarBindings)) {
$message .= "Similar bindings found: " . implode(', ', $similarBindings) . "\n";
}
$message .= "All bindings: " . implode(', ', $availableBindings);
throw new \RuntimeException($message);
}
private function resolveBinding(string $class, callable|string|object $concrete): object