. */ declare(strict_types=1); namespace Fisharebest\Webtrees; use Fisharebest\Webtrees\Contracts\ContainerInterface; use Fisharebest\Webtrees\Exceptions\NotFoundInContainerException; use ReflectionClass; use ReflectionNamedType; use ReflectionParameter; use function array_key_exists; use function array_map; /** * @template T of object * * A PSR-11 compatible container and dependency injector. */ class Container implements ContainerInterface { /** @var array,T> */ private array $container = []; /** * @param class-string $id * * @return bool */ public function has(string $id): bool { return array_key_exists($id, $this->container); } /** * @param class-string $id * * @return T */ public function get(string $id): object { return $this->container[$id] ??= $this->make($id); } /** * @param class-string $id * @param T $object * * @return $this */ public function set(string $id, object $object): static { $this->container[$id] = $object; return $this; } /** * @param class-string $id * * @return T */ private function make(string $id): object { $reflector = new ReflectionClass($id); $constructor = $reflector->getConstructor(); if ($constructor === null) { return new $id(); } $parameters = array_map(self::makeParameter(...), $constructor->getParameters()); return new $id(...$parameters); } private function makeParameter(ReflectionParameter $reflection_parameter): mixed { if ($reflection_parameter->isOptional()) { return $reflection_parameter->getDefaultValue(); } $type = $reflection_parameter->getType(); if ($type instanceof ReflectionNamedType) { return $this->get($type->getName()); } throw new NotFoundInContainerException('Cannot make ' . $reflection_parameter->getName()); } }