19 private $dependencies = [];
20 private $entities = [];
22 private $lock =
false;
26 $this->dependencies[static::class] = static::class;
27 $this->dependencies[ContainerContract::class] = static::class;
30 private function __clone()
34 public function has(
string $id): bool
36 return isset($this->dependencies[$id]);
39 public function get(
string $id, array $args = [])
46 $definition = $this->getDefinition($id, $args);
48 if (!isset($this->entities[$definition]))
50 $this->entities[$definition] = $this->instantiate($id, $args);
53 return $this->entities[$definition];
57 public function make(
string $id, array $args = [])
64 return $this->instantiate($id, $args);
69 if ($this->isLocked())
72 "Dependency {{$dependency}} cannot be injected after resolving container dependencies.
73 Try this before resolving."
77 $this->dependencies[$id] = $dependency;
79 if (!isset($this->dependencies[$dependency]) && class_exists($dependency))
81 $this->dependencies[$dependency] = $dependency;
84 unset($this->entities[$id]);
91 return new \ArrayIterator($this->dependencies);
94 private function instantiate($id, array $args = [])
98 $dependency = $this->dependencies[$id];
100 if ($dependency instanceof \Closure)
102 $entity = $this->instantiateClosure($dependency, $args);
104 elseif (class_exists($dependency))
106 $entity = $this->instantiateClass($dependency, $args);
110 $entity = $dependency;
116 private function instantiateClosure($dependency, array $args = [])
118 $function = new \ReflectionFunction($dependency);
120 $invokeArguments = [];
122 foreach ($function->getParameters() as $parameter)
124 $invokeArguments[] = $this->resolveParameter($parameter, $args);
127 return $function->invokeArgs($invokeArguments);
130 private function instantiateClass($dependency, array $args = [])
132 $class = new \ReflectionClass($dependency);
133 $constructor = $class->getConstructor();
137 $invokeArguments = [];
139 foreach ($constructor->getParameters() as $parameter)
141 $invokeArguments[] = $this->resolveParameter($parameter, $args);
144 return $class->newInstance(...$invokeArguments);
147 return $class->newInstanceWithoutConstructor();
150 private function resolveParameter(\ReflectionParameter $parameter, array $args = [])
152 $type = $parameter->getType();
153 if (($type instanceof ReflectionNamedType) && !$type->isBuiltin())
155 return $this->resolveClassParameter($parameter, $args);
158 return $this->resolveVariableParameter($parameter, $args);
161 private function resolveClassParameter(\ReflectionParameter $parameter, array $args = [])
165 $type = $parameter->getType();
166 if (($type instanceof ReflectionNamedType) && !$type->isBuiltin())
168 $className = $type->getName();
172 throw new ObjectNotFoundException(
'Not class type');
175 if ($className === static::class || is_subclass_of(static::class, $className))
180 $dependency = $args[$className] ?? $this->
get($className, $args);
182 catch (ObjectNotFoundException $exception)
184 if ($parameter->isDefaultValueAvailable())
186 $dependency = $parameter->getDefaultValue();
190 $name = $parameter->getName();
191 throw new ObjectNotFoundException(
"Dependency {{$name}} not found.");
198 private function resolveVariableParameter(\ReflectionParameter $parameter, array $args = [])
200 $parameterName = $parameter->getName();
202 if (isset($args[$parameterName]))
204 return $args[$parameterName];
207 if ($parameter->isDefaultValueAvailable())
209 return $parameter->getDefaultValue();
215 private function lock(): self
222 private function isLocked(): bool
227 private function getDefinition(
string $id, array $args = []): string
237 foreach ($args as $key => $argument)
239 if (is_object($argument))
241 $argument = spl_object_hash($argument);
244 $argString .=
"|$key=$argument";
247 return $id . $argString;