bindings[$abstract] = [ 'concrete' => $concrete, 'shared' => $shared, ]; } public function singleton(string $abstract, callable|Closure|string $concrete): void { $this->bind($abstract, $concrete, true); } public function instance(string $abstract, object $instance): void { $this->instances[$abstract] = $instance; } public function alias(string $abstract, string $alias): void { $this->aliases[$alias] = $abstract; } public function has(string $abstract): bool { $abstract = $this->getAlias($abstract); return isset($this->instances[$abstract]) || isset($this->bindings[$abstract]); } public function make(string $abstract, array $parameters = []): object { $abstract = $this->getAlias($abstract); if (isset($this->instances[$abstract])) { return $this->instances[$abstract]; } if (! isset($this->bindings[$abstract])) { if (class_exists($abstract)) { return $this->resolve($abstract, $parameters); } throw new ProviderException(sprintf('Service [%s] is not bound in the container.', $abstract)); } $binding = $this->bindings[$abstract]; $concrete = $binding['concrete']; if (is_string($concrete) && $concrete !== $abstract) { $object = $this->resolve($concrete, $parameters); } elseif ($concrete instanceof Closure || is_callable($concrete)) { $object = $concrete($this, $parameters); } else { $object = $this->resolve($abstract, $parameters); } if ($binding['shared']) { $this->instances[$abstract] = $object; } return $object; } public function resolve(string $class, array $parameters = []): object { if (! class_exists($class)) { throw new ProviderException(sprintf('Cannot resolve class [%s] because it does not exist.', $class)); } $reflector = new \ReflectionClass($class); if (! $reflector->isInstantiable()) { throw new ProviderException(sprintf('Class [%s] is not instantiable.', $class)); } $constructor = $reflector->getConstructor(); if ($constructor === null || $constructor->getNumberOfParameters() === 0) { return $reflector->newInstance(); } $dependencies = []; foreach ($constructor->getParameters() as $parameter) { $name = $parameter->getName(); if (array_key_exists($name, $parameters)) { $dependencies[] = $parameters[$name]; continue; } $type = $parameter->getType(); if ($type && ! $type->isBuiltin()) { $dependencies[] = $this->make($type->getName()); continue; } if ($parameter->isDefaultValueAvailable()) { $dependencies[] = $parameter->getDefaultValue(); continue; } throw new ProviderException(sprintf('Unable to resolve dependency [%s] for [%s].', $parameter->getName(), $class)); } return $reflector->newInstanceArgs($dependencies); } public function forget(string $abstract): void { $abstract = $this->getAlias($abstract); unset($this->bindings[$abstract], $this->instances[$abstract]); } private function getAlias(string $abstract): string { return $this->aliases[$abstract] ?? $abstract; } }