1 | <?php |
||
2 | |||
3 | /** |
||
4 | * This file is part of Blitz PHP framework. |
||
5 | * |
||
6 | * (c) 2022 Dimitri Sitchet Tomkeu <[email protected]> |
||
7 | * |
||
8 | * For the full copyright and license information, please view |
||
9 | * the LICENSE file that was distributed with this source code. |
||
10 | */ |
||
11 | |||
12 | namespace BlitzPHP\Container; |
||
13 | |||
14 | use BadMethodCallException; |
||
15 | use BlitzPHP\Contracts\Container\ContainerInterface; |
||
16 | use Closure; |
||
17 | use DI\Container as DIContainer; |
||
18 | use DI\ContainerBuilder; |
||
19 | |||
20 | /** |
||
21 | * Conteneur d’injection de dépendances. |
||
22 | * |
||
23 | * @method string debugEntry(string $name) Obtenir les informations de débogage de l'entrée. |
||
24 | * @method array getKnownEntryNames() Obtenez des entrées de conteneur définies. |
||
25 | * @method object injectOn(object $instance) Injectez toutes les dépendances sur une instance existante. |
||
26 | * @method void set(string $name, mixed $value) Définissez un objet ou une valeur dans le conteneur. |
||
27 | */ |
||
28 | class Container implements ContainerInterface |
||
29 | { |
||
30 | protected DIContainer $container; |
||
31 | |||
32 | /** |
||
33 | * Providers deja charges (cache) |
||
34 | * |
||
35 | * @var list<AbstractProvider> |
||
0 ignored issues
–
show
|
|||
36 | */ |
||
37 | private static array $providers = []; |
||
38 | |||
39 | /** |
||
40 | * Noms des providers deja charges (cache) |
||
41 | * |
||
42 | * @var list<class-string<AbstractProvider>> |
||
43 | */ |
||
44 | private static array $providerNames = []; |
||
45 | |||
46 | /** |
||
47 | * Avons-nous déjà découvert les fournisseurs ? |
||
48 | */ |
||
49 | private static bool $discovered = false; |
||
50 | |||
51 | /** |
||
52 | * Drapeau pour determiner si le conteneur est deja initialiser |
||
53 | */ |
||
54 | private bool $initialized = false; |
||
55 | |||
56 | /** |
||
57 | * Renvoie une entrée du conteneur par son nom. |
||
58 | * |
||
59 | * @param string $name Nom de l’entrée ou nom de classe. |
||
60 | * |
||
61 | * @return mixed |
||
62 | */ |
||
63 | public function get(string $name) |
||
64 | { |
||
65 | 8 | return $this->container->get($name); |
|
66 | } |
||
67 | |||
68 | /** |
||
69 | * Testez si le conteneur peut fournir quelque chose pour le nom donné. |
||
70 | * |
||
71 | * @param string $name Nom d'entrée ou nom de classe |
||
72 | */ |
||
73 | public function has(string $name): bool |
||
74 | { |
||
75 | 10 | return $this->container->has($name); |
|
76 | } |
||
77 | |||
78 | /** |
||
79 | * Construire une entrée du conteneur par son nom. |
||
80 | * |
||
81 | * Cette méthode se comporte comme get() sauf qu'elle résout l'entrée à chaque fois. |
||
82 | * Par exemple, si l'entrée est une classe, une nouvelle instance sera créée à chaque fois. |
||
83 | * |
||
84 | * Cette méthode permet au conteneur de se comporter comme une usine. |
||
85 | * |
||
86 | * @template T |
||
87 | * |
||
88 | * @param class-string<T>|string $name Nom de l'entrée ou nom de la classe. |
||
0 ignored issues
–
show
|
|||
89 | * @param array $parameters Paramètres optionnels à utiliser pour construire l'entrée. |
||
90 | * Utilisez ceci pour forcer des paramètres spécifiques à des valeurs spécifiques. |
||
91 | * Les paramètres non définis dans ce tableau seront résolus à l'aide du conteneur. |
||
92 | * |
||
93 | * @return mixed|T |
||
94 | */ |
||
95 | public function make(string $name, array $parameters = []): mixed |
||
96 | { |
||
97 | 28 | return $this->container->make($name, $parameters); |
|
98 | } |
||
99 | |||
100 | /** |
||
101 | * Appelle la fonction donnée en utilisant les paramètres donnés. |
||
102 | * Les paramètres manquants seront résolus à partir du conteneur. |
||
103 | * |
||
104 | * @param array|callable|string $callback Fonction à appeler. |
||
105 | * @param array $parameters Paramètres facultatifs à utiliser pour construire l'entrée. |
||
106 | * Utilisez ceci pour forcer des paramètres spécifiques à des valeurs spécifiques. |
||
107 | * Les paramètres non définis dans ce tableau seront résolus en utilisant le conteneur. |
||
108 | * Peut être indexé par les noms de paramètre ou non indexé (même ordre que les paramètres). |
||
109 | * Le tableau peut également contenir des définitions DI, par ex. DI\get(). |
||
110 | */ |
||
111 | public function call(array|callable|string $callback, array $parameters = []): mixed |
||
112 | { |
||
113 | 4 | return $this->container->call($callback, $parameters); |
|
114 | } |
||
115 | |||
116 | /** |
||
117 | * Defini un element au conteneur sous forme de factory |
||
118 | * Si l'element existe déjà, il sera remplacé |
||
119 | */ |
||
120 | public function add(string $key, Closure $callback): void |
||
121 | { |
||
122 | $this->container->set($key, $callback); |
||
123 | |||
124 | $this->container->set(self::class, $this); |
||
125 | } |
||
126 | |||
127 | /** |
||
128 | * Defini un element au conteneur sous forme de factory |
||
129 | * Si l'element existe déjà, il sera ignoré |
||
130 | */ |
||
131 | public function addIf(string $key, Closure $callback): void |
||
132 | { |
||
133 | if (! $this->has($key)) { |
||
134 | $this->add($key, $callback); |
||
135 | } |
||
136 | } |
||
137 | |||
138 | /** |
||
139 | * Defini plusieurs elements au conteneur sous forme de factory |
||
140 | * L'element qui existera déjà sera remplacé par la correspondance du tableau |
||
141 | * |
||
142 | * @param array<string, Closure> $keys |
||
143 | */ |
||
144 | public function merge(array $keys): void |
||
145 | { |
||
146 | foreach ($keys as $key => $callback) { |
||
147 | if ($callback instanceof Closure) { |
||
148 | $this->add($key, $callback); |
||
149 | } |
||
150 | } |
||
151 | } |
||
152 | |||
153 | /** |
||
154 | * Defini plusieurs elements au conteneur sous forme de factory |
||
155 | * L'element qui existera déjà sera ignoré |
||
156 | * |
||
157 | * @param array<string, Closure> $keys |
||
158 | */ |
||
159 | public function mergeIf(array $keys): void |
||
160 | { |
||
161 | foreach ($keys as $key => $callback) { |
||
162 | if ($callback instanceof Closure) { |
||
163 | $this->addIf($key, $callback); |
||
164 | } |
||
165 | } |
||
166 | } |
||
167 | |||
168 | /** |
||
169 | * Verifie qu'une entree a été explicitement définie dans le conteneur |
||
170 | */ |
||
171 | public function bound(string $key): bool |
||
172 | { |
||
173 | return in_array($key, $this->getKnownEntryNames(), true); |
||
174 | } |
||
175 | |||
176 | /** |
||
177 | * Methode magique pour acceder aux methodes de php-di |
||
178 | * |
||
179 | * @param mixed $name |
||
180 | * @param mixed $arguments |
||
181 | */ |
||
182 | public function __call($name, $arguments) |
||
183 | { |
||
184 | if (method_exists($this->container, $name)) { |
||
185 | 52 | return call_user_func_array([$this->container, $name], $arguments); |
|
186 | } |
||
187 | |||
188 | throw new BadMethodCallException('Methode "' . $name . '" non definie'); |
||
189 | } |
||
190 | |||
191 | /** |
||
192 | * Initialise le conteneur et injecte les services providers. |
||
193 | * |
||
194 | * @internal |
||
195 | */ |
||
196 | public function initialize() |
||
197 | { |
||
198 | if ($this->initialized) { |
||
199 | return; |
||
200 | } |
||
201 | |||
202 | $builder = new ContainerBuilder(); |
||
203 | $builder->useAutowiring(true); |
||
204 | $builder->useAttributes(true); |
||
205 | |||
206 | if (on_prod(true)) { |
||
207 | if (extension_loaded('apcu')) { |
||
208 | $builder->enableDefinitionCache(str_replace([' ', '/', '\\', '.'], '', APP_PATH)); |
||
209 | } |
||
210 | |||
211 | $builder->enableCompilation(FRAMEWORK_STORAGE_PATH . 'cache'); |
||
212 | } |
||
213 | |||
214 | $this->discoveProviders(); |
||
215 | |||
216 | foreach (self::$providerNames as $provider) { |
||
217 | $builder->addDefinitions($provider::definitions()); |
||
218 | } |
||
219 | |||
220 | $this->container = $builder->build(); |
||
221 | |||
222 | $this->registryProviders(); |
||
223 | |||
224 | $this->initialized = true; |
||
225 | } |
||
226 | |||
227 | /** |
||
228 | * Listes des providers chargés par le framework |
||
229 | * |
||
230 | * @return array<class-string<AbstractProvider>, AbstractProvider> |
||
0 ignored issues
–
show
|
|||
231 | */ |
||
232 | public static function providers(): array |
||
233 | { |
||
234 | return array_combine(self::$providerNames, self::$providers); |
||
235 | } |
||
236 | |||
237 | /** |
||
238 | * Enregistre les provider dans le conteneur |
||
239 | */ |
||
240 | private function registryProviders(): void |
||
241 | { |
||
242 | foreach (self::$providerNames as $classname) { |
||
243 | $provider = $this->container->make($classname, [ |
||
244 | 'container' => $this, |
||
245 | ]); |
||
246 | $this->container->call([$provider, 'register']); |
||
247 | self::$providers[] = $provider; |
||
248 | } |
||
249 | |||
250 | $this->set(self::class, $this); |
||
251 | $this->set(ContainerInterface::class, $this); |
||
252 | } |
||
253 | |||
254 | /** |
||
255 | * Recherche tous les fournisseurs disponibles et les charge en cache |
||
256 | */ |
||
257 | private function discoveProviders(): void |
||
258 | { |
||
259 | if (! self::$discovered) { |
||
260 | $locator = service('locator'); |
||
261 | $files = array_merge( |
||
262 | $locator->search('Config/Providers'), |
||
263 | $locator->listFiles('Providers/'), |
||
264 | ); |
||
265 | |||
266 | $appProviders = array_filter($files, static fn ($name) => str_starts_with($name, APP_PATH)); |
||
267 | $systProviders = array_filter($files, static fn ($name) => str_starts_with($name, SYST_PATH)); |
||
268 | $files = array_diff($files, $appProviders, $systProviders); |
||
269 | |||
270 | $files = [ |
||
271 | ...$files, // Les founisseurs des vendors sont les premier a etre remplacer si besoin |
||
272 | ...$systProviders, // Les founisseurs du systeme viennent ensuite pour eventuelement remplacer pour les vendors sont les |
||
273 | ...$appProviders, // Ceux de l'application ont peu de chance de modifier quelque chose mais peuvent le faire |
||
274 | ]; |
||
275 | |||
276 | // Obtenez des instances de toutes les classes de providers et mettez-les en cache localement. |
||
277 | foreach ($files as $file) { |
||
278 | if (is_a($classname = $locator->getClassname($file), AbstractProvider::class, true)) { |
||
279 | self::$providerNames[] = $classname; |
||
280 | } |
||
281 | } |
||
282 | |||
283 | self::$discovered = true; |
||
284 | } |
||
285 | } |
||
286 | } |
||
287 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths