resolve($id); } public function add(string $id, callable $factory): DIContainer { if(isset($this->definitions[$id])) { return $this; } $this->definitions[$id] = $factory; return $this; } public function resolve(string $id) { if (key_exists($id, $this->definitions)) { $factory = $this->definitions[$id]; return $factory($this); } if ($this->provides($id)) { $this->initialize($id); if (!key_exists($id, $this->definitions)) { throw new \Exception(sprintf('Service does not properly provide (%s)', $id)); } $factory = $this->definitions[$id]; return $factory($this); } throw new \Exception(sprintf('Definition (%s) has not been added to the container', $id)); } public function has(string $id): bool { if (key_exists($id, $this->definitions) || $this->provides($id)) { return true; } if ($this->provides($id)) { return true; } return false; } private function make(string $class) { try { $reflector = new \ReflectionClass($class); } catch (\ReflectionException $e) { throw new \Exception("Target class [$class] does not exist.", 0, $e); } // If the type is not instantiable, such as an Interface or Abstract Class if (! $reflector->isInstantiable()) { throw new \Exception("Target [$class] is not instantiable."); } $constructor = $reflector->getConstructor(); // If there are no constructor, that means there are no dependencies if ($constructor === null) { return new $class; } $parameters = $constructor->getParameters(); $dependencies = []; foreach ($parameters as $parameter) { $type = $parameter->getType(); if (! $type instanceof \ReflectionNamedType || $type->isBuiltin()) { // Resolve a non-class hinted primitive dependency. if ($parameter->isDefaultValueAvailable()) { $dependencies[] = $parameter->getDefaultValue(); } else if ($parameter->isVariadic()) { $dependencies[] = []; } else { throw new \Exception("Unresolvable dependency [$parameter] in class {$parameter->getDeclaringClass()->getName()}"); } } $name = $type->getName(); // Resolve a class based dependency from the container. try { $dependency = $this->get($name); $dependencies[] = $dependency; } catch (\Exception $e) { if ($parameter->isOptional()) { $dependencies[] = $parameter->getDefaultValue(); } else { $dependency = $this->make($parameter->getType()->getName()); //$this->register($name, $dependency); $dependencies[] = $dependency; } } } return $reflector->newInstanceArgs($dependencies); } protected function provides(string $id): bool { foreach($this->services as $service) { if($service->provides($id)) { return true; } } return false; } protected function initialize(string $id): void { $initialized = false; foreach ($this->services as $service) { if (in_array($service->getId(), $this->initializedServices, true)) { $initialized = true; continue; } if ($service->provides($id)) { $service->initialize(); $this->initializedServices[] = $service->getId(); $initialized = true; } } if (!$initialized) { throw new \Exception( sprintf('(%s) is not provided by a service provider', $id) ); } } }