Passed
Push — main ( 34042d...ebfbeb )
by Dimitri
14:02
created

Container::providers()   B

Complexity

Conditions 7
Paths 17

Size

Total Lines 44
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 23
c 1
b 0
f 0
nc 17
nop 0
dl 0
loc 44
rs 8.6186
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 DI\Container as DIContainer;
16
use DI\ContainerBuilder;
17
use Psr\Container\ContainerInterface;
18
19
/**
20
 * Conteneur d’injection de dépendances.
21
 * 
22
 * @method mixed make(string $name, array $parameters = []) Construire une entrée du conteneur par son nom.
23
 *                                                          Cette méthode se comporte comme singleton() sauf qu'elle résout à nouveau l'entrée à chaque fois.
24
 *                                                          Par exemple, si l'entrée est une classe, une nouvelle instance sera créée à chaque fois.
25
 *                                                          Cette méthode fait que le conteneur se comporte comme une usine.
26
 *
27
 *                                                          @param array  $parameters Paramètres facultatifs à utiliser pour construire l'entrée. 
28
 *                                                                                    Utilisez ceci pour forcer des paramètres spécifiques à des valeurs spécifiques. 
29
 *                                                                                    Les paramètres non définis dans ce tableau seront résolus en utilisant le conteneur.
30
 * @method mixed call(array|callable|string $callable, array $parameters = []) Appelez la fonction donnée en utilisant les paramètres donnés.
31
 *                                                                             Les paramètres manquants seront résolus à partir du conteneur.
32
 *                                                        
33
 *                                                          @param array  $parameters Paramètres à utiliser. 
34
 *                                                                                    Peut être indexé par les noms de paramètre ou non indexé (même ordre que les paramètres).
35
 *                                                                                    Le tableau peut également contenir des définitions DI, par ex. DI\get().
36
 * @method string debugEntry(string $name) Obtenir les informations de débogage de l'entrée.
37
 * @method object injectOn(object $instance) Injectez toutes les dépendances sur une instance existante.
38
 * @method void set(string $name, mixed $value) Définissez un objet ou une valeur dans le conteneur.
39
 * @method void add(string $name, mixed $value) Définissez un objet ou une valeur dans le conteneur.
40
 * @method array getKnownEntryNames() Obtenez des entrées de conteneur définies.
41
 */
42
class Container implements ContainerInterface
43
{
44
    protected DIContainer $container;
45
46
    /**
47
     * Providers deja charges (cache)
48
     */
49
    private static array $providers = [];
50
51
    /**
52
     * methodes aliases 
53
     */
54
    private static array $alias = [
55
        'add' => 'set'
56
    ];
57
58
    /**
59
     * Drapeau pour determiner si le conteneur est deja initialiser
60
     */
61
    private bool $initialized = false;
62
63
    /**
64
     * Renvoie une entrée du conteneur par son nom.
65
     *
66
     * @param string $name Nom de l’entrée ou nom de classe.
67
     *
68
     * @return mixed
69
     */
70
    public function get(string $name)
71
    {
72
        return $this->container->get($name);
73
    }
74
75
    /**
76
     * Testez si le conteneur peut fournir quelque chose pour le nom donné.
77
     *
78
     * @param string $name Nom d'entrée ou nom de classe
79
     */
80
    public function has(string $name): bool
81
    {
82
        return $this->container->has($name);
83
    }
84
85
    public function __call($name, $arguments)
86
    {
87
        if (isset(self::$alias[$name])) {
88
            $name = self::$alias[$name];
89
        }
90
91
        if (method_exists($this->container, $name)) {
92
            return call_user_func_array([$this->container, $name], $arguments);
93
        }
94
95
        throw new BadMethodCallException('Methode "' . $name . '" non definie');
96
    }
97
    
98
    public function initialize()
99
    {
100
        if ($this->initialized) {
101
            return;
102
        }
103
104
        $builder = new ContainerBuilder();
105
        $builder->useAutowiring(true);
106
        $builder->useAttributes(true);
107
108
        if (on_prod(true)) {
109
            if (extension_loaded('apcu')) {
110
                $builder->enableDefinitionCache(str_replace([' ', '/', '\\', '.'], '', APP_PATH));
111
            }
112
            
113
            $builder->enableCompilation(FRAMEWORK_STORAGE_PATH . 'cache');
114
        }
115
116
        $builder->addDefinitions(...self::providers());
117
118
        $this->container = $builder->build();
119
120
        $this->set(self::class, $this);
121
        $this->set(ContainerInterface::class, $this);
122
123
        $this->initialized = true;
124
    }
125
126
    /**
127
     * Recupere toutes les definitions des services à injecter dans le container
128
     */
129
    public static function providers(): array
130
    {
131
        if (! empty(static::$providers)) {
0 ignored issues
show
Bug introduced by
Since $providers is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $providers to at least protected.
Loading history...
132
            return static::$providers;
133
        }
134
135
        $providers = [];
136
137
        $loader = Services::locator();
138
139
        // Stockez nos versions de providers système et d'application afin que nous puissions contrôler l'ordre de chargement.
140
        $systemProvider = null;
141
        $appProvider    = null;
142
        $localIncludes  = [];
143
144
        $paths = array_merge(
145
            $loader->search('Constants/providers'), // providers system
146
            $loader->search('Config/providers') // providers de l'application ou des fournisseurs
147
        );
148
149
        foreach ($paths as $path) {
150
            if (strpos($path, APP_PATH . 'Config' . DS) === 0) {
151
                $appProvider = $path;
152
            } elseif (strpos($path, SYST_PATH . 'Constants' . DS) === 0) {
153
                $systemProvider = $path;
154
            } else {
155
                $localIncludes[] = $path;
156
            }
157
        }
158
159
        // Les providers par défaut du système doivent être ajouté en premier pour que les autres puisse les surcharger
160
        if (! empty($systemProvider)) {
161
            $providers[] = $systemProvider;
162
        }
163
164
        // Tous les providers avec espace de noms sont ajoutés ensuite
165
        $providers = [...$providers, ...$localIncludes];
166
167
        // Enfin ceux de l'application doivent remplacer tous les autres
168
        if (! empty($appProvider)) {
169
            $providers[] = $appProvider;
170
        }
171
172
        return static::$providers = $providers;
173
    }
174
}
175