Issues (2884)

src/Di/Container.php (1 issue)

1
<?php
2
3
/**
4
 * Container.php
5
 *
6
 * Jaxon DI container. Provide container service for Jaxon classes.
7
 *
8
 * @package jaxon-core
9
 * @author Thierry Feuzeu <[email protected]>
10
 * @copyright 2016 Thierry Feuzeu <[email protected]>
11
 * @license https://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
12
 * @link https://github.com/jaxon-php/jaxon-core
13
 */
14
15
namespace Jaxon\Di;
16
17
use Jaxon\App\Ajax;
18
use Jaxon\App\I18n\Translator;
19
use Jaxon\App\Session\SessionInterface;
20
use Jaxon\Exception\SetupException;
21
use Pimple\Container as PimpleContainer;
22
use Psr\Container\ContainerInterface;
23
use Psr\Log\LoggerInterface;
24
use Psr\Log\NullLogger;
25
26
use Closure;
27
use Exception;
28
use ReflectionClass;
29
use ReflectionException;
30
use ReflectionNamedType;
31
use ReflectionParameter;
32
use Throwable;
33
34
use function array_map;
35
use function realpath;
36
37
class Container
38
{
39
    use Traits\AppTrait;
40
    use Traits\PsrTrait;
41
    use Traits\RequestTrait;
42
    use Traits\ResponseTrait;
43
    use Traits\PluginTrait;
44
    use Traits\CallableTrait;
45
    use Traits\RegisterTrait;
46
    use Traits\ViewTrait;
47
    use Traits\UtilTrait;
48
49
    /**
50
     * The library Dependency Injection Container
51
     *
52
     * @var PimpleContainer
53
     */
54
    private $xLibContainer;
55
56
    /**
57
     * The application or framework Dependency Injection Container
58
     *
59
     * @var ContainerInterface
60
     */
61
    private $xAppContainer = null;
62
63
    /**
64
     * The class constructor
65
     */
66
    public function __construct(Ajax $jaxon)
67
    {
68
        $this->xLibContainer = new PimpleContainer();
69
70
        // Save the Ajax and Container instances
71
        $this->val(Ajax::class, $jaxon);
72
        $this->val(Container::class, $this);
73
74
        // Register the null logger by default
75
        $this->setLogger(new NullLogger());
76
77
        // Template directory
78
        $sTemplateDir = realpath(__DIR__ . '/../../templates');
79
        $this->val('jaxon.core.dir.template', $sTemplateDir);
80
81
        // Translation directory
82
        $sTranslationDir = realpath(__DIR__ . '/../../translations');
83
        $this->val('jaxon.core.dir.translation', $sTranslationDir);
84
85
        $this->registerAll();
86
    }
87
88
    /**
89
     * Register the values into the container
90
     *
91
     * @return void
92
     */
93
    private function registerAll()
94
    {
95
        $this->registerApp();
96
        $this->registerPsr();
97
        $this->registerRequests();
98
        $this->registerResponses();
99
        $this->registerPlugins();
100
        $this->registerCallables();
101
        $this->registerViews();
102
        $this->registerUtils();
103
    }
104
105
    /**
106
     * Set the logger
107
     *
108
     * @param LoggerInterface $xLogger
109
     *
110
     * @return void
111
     */
112
    public function setLogger(LoggerInterface $xLogger)
113
    {
114
        $this->val(LoggerInterface::class, $xLogger);
115
    }
116
117
    /**
118
     * Get the logger
119
     *
120
     * @return LoggerInterface
121
     */
122
    public function getLogger(): LoggerInterface
123
    {
124
        return $this->get(LoggerInterface::class);
125
    }
126
127
    /**
128
     * Set the container provided by the integrated framework
129
     *
130
     * @param ContainerInterface $xContainer    The container implementation
131
     *
132
     * @return void
133
     */
134
    public function setContainer(ContainerInterface $xContainer)
135
    {
136
        $this->xAppContainer = $xContainer;
137
    }
138
139
    /**
140
     * Check if a class is defined in the container
141
     *
142
     * @param string $sClass    The full class name
143
     *
144
     * @return bool
145
     */
146
    public function h(string $sClass): bool
147
    {
148
        return $this->xLibContainer->offsetExists($sClass);
149
    }
150
151
    /**
152
     * Check if a class is defined in the container
153
     *
154
     * @param string $sClass    The full class name
155
     *
156
     * @return bool
157
     */
158
    public function has(string $sClass): bool
159
    {
160
        if($this->xAppContainer != null && $this->xAppContainer->has($sClass))
161
        {
162
            return true;
163
        }
164
        return $this->xLibContainer->offsetExists($sClass);
165
    }
166
167
    /**
168
     * Get a class instance
169
     *
170
     * @param string $sClass    The full class name
171
     *
172
     * @return mixed
173
     */
174
    public function g(string $sClass)
175
    {
176
        return $this->xLibContainer->offsetGet($sClass);
177
    }
178
179
    /**
180
     * Get a class instance
181
     *
182
     * @param string $sClass The full class name
183
     *
184
     * @return mixed
185
     * @throws SetupException
186
     */
187
    public function get(string $sClass)
188
    {
189
        try
190
        {
191
            if($this->xAppContainer != null && $this->xAppContainer->has($sClass))
192
            {
193
                return $this->xAppContainer->get($sClass);
194
            }
195
            return $this->xLibContainer->offsetGet($sClass);
196
        }
197
        catch(Exception|Throwable $e)
198
        {
199
            $xLogger = $this->g(LoggerInterface::class);
200
            $xTranslator = $this->g(Translator::class);
201
            $sMessage = $e->getMessage() . ': ' . $xTranslator->trans('errors.class.container', ['name' => $sClass]);
202
            $xLogger->error($e->getMessage(), ['message' => $sMessage]);
203
            throw new SetupException($sMessage);
204
        }
205
    }
206
207
    /**
208
     * Save a closure in the container
209
     *
210
     * @param string $sClass    The full class name
211
     * @param Closure $xClosure    The closure
212
     *
213
     * @return void
214
     */
215
    public function set(string $sClass, Closure $xClosure)
216
    {
217
       $this->xLibContainer->offsetSet($sClass, function() use($xClosure) {
218
            return $xClosure($this);
219
        });
220
    }
221
222
    /**
223
     * Save a value in the container
224
     *
225
     * @param string $sKey    The key
226
     * @param mixed $xValue    The value
227
     *
228
     * @return void
229
     */
230
    public function val(string $sKey, $xValue)
231
    {
232
       $this->xLibContainer->offsetSet($sKey, $xValue);
233
    }
234
235
    /**
236
     * Set an alias in the container
237
     *
238
     * @param string $sAlias    The alias name
239
     * @param string $sClass    The class name
240
     *
241
     * @return void
242
     */
243
    public function alias(string $sAlias, string $sClass)
244
    {
245
        $this->set($sAlias, function($di) use ($sClass) {
246
            return $di->get($sClass);
247
        });
248
    }
249
250
    /**
251
     * @param ReflectionClass $xClass
252
     * @param ReflectionParameter $xParameter
253
     *
254
     * @return mixed
255
     * @throws SetupException
256
     */
257
    protected function getParameter(ReflectionClass $xClass, ReflectionParameter $xParameter)
0 ignored issues
show
The parameter $xClass is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

257
    protected function getParameter(/** @scrutinizer ignore-unused */ ReflectionClass $xClass, ReflectionParameter $xParameter)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
258
    {
259
        $xType = $xParameter->getType();
260
        // Check the parameter class first.
261
        if($xType instanceof ReflectionNamedType)
262
        {
263
            // Check the class + the name
264
            if($this->has($xType->getName() . ' $' . $xParameter->getName()))
265
            {
266
                return $this->get($xType->getName() . ' $' . $xParameter->getName());
267
            }
268
            // Check the class only
269
            if($this->get($xType->getName()))
270
            {
271
                return $this->get($xType->getName());
272
            }
273
        }
274
        // Check the name only
275
        return $this->get('$' . $xParameter->getName());
276
    }
277
278
    /**
279
     * Create an instance of a class, getting the constructor parameters from the DI container
280
     *
281
     * @param string|ReflectionClass $xClass The class name or the reflection class
282
     *
283
     * @return object|null
284
     * @throws ReflectionException
285
     * @throws SetupException
286
     */
287
    public function make($xClass)
288
    {
289
        if(is_string($xClass))
290
        {
291
            $xClass = new ReflectionClass($xClass); // Create the reflection class instance
292
        }
293
        if(!($xClass instanceof ReflectionClass))
294
        {
295
            return null;
296
        }
297
        // Use the Reflection class to get the parameters of the constructor
298
        if(($constructor = $xClass->getConstructor()) === null)
299
        {
300
            return $xClass->newInstance();
301
        }
302
        $aParameterInstances = array_map(function($xParameter) use($xClass) {
303
            return $this->getParameter($xClass, $xParameter);
304
        }, $constructor->getParameters());
305
306
        return $xClass->newInstanceArgs($aParameterInstances);
307
    }
308
309
    /**
310
     * Create an instance of a class by automatically fetching the dependencies in the constructor.
311
     *
312
     * @param string $sClass    The class name
313
     *
314
     * @return void
315
     */
316
    public function auto(string $sClass)
317
    {
318
        $this->set($sClass, function() use ($sClass) {
319
            return $this->make($sClass);
320
        });
321
    }
322
323
    /**
324
     * Get the session manager
325
     *
326
     * @return SessionInterface|null
327
     */
328
    public function getSessionManager(): ?SessionInterface
329
    {
330
        return $this->h(SessionInterface::class) ? $this->g(SessionInterface::class) : null;
331
    }
332
333
    /**
334
     * Set the session manager
335
     *
336
     * @param Closure $xClosure    A closure to create the session manager instance
337
     *
338
     * @return void
339
     */
340
    public function setSessionManager(Closure $xClosure)
341
    {
342
        $this->set(SessionInterface::class, $xClosure);
343
    }
344
}
345