diff --git a/src/Framework/DI/DefaultContainer.php b/src/Framework/DI/DefaultContainer.php index 483558fe..7ad6b424 100644 --- a/src/Framework/DI/DefaultContainer.php +++ b/src/Framework/DI/DefaultContainer.php @@ -127,10 +127,63 @@ final class DefaultContainer implements Container return $this->resolveBinding($class, $this->bindings->getBinding($class)); } - $reflection = $this->reflectionProvider->getClass($className); - $dependencies = $this->dependencyResolver->resolveDependencies($className); + // Enhanced diagnostics for missing bindings + try { + $reflection = $this->reflectionProvider->getClass($className); + + // Check if class is instantiable using framework's method + if (!$reflection->isInstantiable()) { + $this->throwDetailedBindingException($class, $reflection); + } - return $reflection->newInstance(...$dependencies->toArray()); + $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