xref: /webtrees/app/Container.php (revision d35568b467207589ea9059739da0bf1f7e785a0d)
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