blitz-php /
framework
| 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