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