1<?php 2 3/** 4 * webtrees: online genealogy 5 * Copyright (C) 2023 webtrees development team 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 */ 17 18declare(strict_types=1); 19 20namespace Fisharebest\Webtrees; 21 22use Fisharebest\Webtrees\Contracts\ContainerInterface; 23use Fisharebest\Webtrees\Exceptions\NotFoundInContainerException; 24use ReflectionClass; 25use ReflectionNamedType; 26use ReflectionParameter; 27 28use function array_key_exists; 29use function array_map; 30 31/** 32 * @template T of object 33 * 34 * A PSR-11 compatible container and dependency injector. 35 */ 36class Container implements ContainerInterface 37{ 38 /** @var array<class-string<T>,T> */ 39 private array $container = []; 40 41 /** 42 * @param class-string<T> $id 43 * 44 * @return bool 45 */ 46 public function has(string $id): bool 47 { 48 return array_key_exists($id, $this->container); 49 } 50 51 /** 52 * @param class-string<T> $id 53 * 54 * @return T 55 */ 56 public function get(string $id): object 57 { 58 return $this->container[$id] ??= $this->make($id); 59 } 60 61 /** 62 * @param class-string<T> $id 63 * @param T $object 64 * 65 * @return $this 66 */ 67 public function set(string $id, object $object): static 68 { 69 $this->container[$id] = $object; 70 71 return $this; 72 } 73 74 /** 75 * @param class-string<T> $id 76 * 77 * @return T 78 */ 79 private function make(string $id): object 80 { 81 $reflector = new ReflectionClass($id); 82 $constructor = $reflector->getConstructor(); 83 84 if ($constructor === null) { 85 return new $id(); 86 } 87 88 $parameters = array_map(self::makeParameter(...), $constructor->getParameters()); 89 90 return new $id(...$parameters); 91 } 92 93 private function makeParameter(ReflectionParameter $reflection_parameter): mixed 94 { 95 if ($reflection_parameter->isOptional()) { 96 return $reflection_parameter->getDefaultValue(); 97 } 98 99 $type = $reflection_parameter->getType(); 100 101 if ($type instanceof ReflectionNamedType) { 102 return $this->get($type->getName()); 103 } 104 105 throw new NotFoundInContainerException('Cannot make ' . $reflection_parameter->getName()); 106 } 107} 108