Passed
Push — main ( 1e579d...c0f50c )
by
unknown
08:04 queued 03:57
created

Services::response()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 3
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 7
ccs 0
cts 2
cp 0
crap 12
rs 10
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 BlitzPHP\Autoloader\Autoloader;
15
use BlitzPHP\Autoloader\Locator;
16
use BlitzPHP\Autoloader\LocatorInterface;
17
use BlitzPHP\Cache\Cache;
18
use BlitzPHP\Cache\ResponseCache;
19
use BlitzPHP\Config\Config;
20
use BlitzPHP\Contracts\Database\ConnectionResolverInterface;
21
use BlitzPHP\Contracts\Security\EncrypterInterface;
22
use BlitzPHP\Contracts\Session\CookieManagerInterface;
23
use BlitzPHP\Debug\Logger;
24
use BlitzPHP\Debug\Timer;
25
use BlitzPHP\Debug\Toolbar;
26
use BlitzPHP\Event\EventManager;
27
use BlitzPHP\Filesystem\Filesystem;
28
use BlitzPHP\Filesystem\FilesystemManager;
29
use BlitzPHP\Http\Negotiator;
30
use BlitzPHP\Http\Redirection;
31
use BlitzPHP\Http\Request;
32
use BlitzPHP\Http\Response;
33
use BlitzPHP\Http\ResponseEmitter;
34
use BlitzPHP\Http\ServerRequest;
35
use BlitzPHP\Http\Uri;
36
use BlitzPHP\Http\UrlGenerator;
37
use BlitzPHP\Mail\Mail;
38
use BlitzPHP\Router\RouteCollection;
39
use BlitzPHP\Router\Router;
40
use BlitzPHP\Security\Encryption\Encryption;
41
use BlitzPHP\Session\Cookie\Cookie;
42
use BlitzPHP\Session\Cookie\CookieManager;
43
use BlitzPHP\Session\Handlers\Database as DatabaseSessionHandler;
44
use BlitzPHP\Session\Handlers\Database\MySQL as MySQLSessionHandler;
45
use BlitzPHP\Session\Handlers\Database\Postgre as PostgreSessionHandler;
46
use BlitzPHP\Session\Store;
47
use BlitzPHP\Translator\Translate;
48
use BlitzPHP\Utilities\Helpers;
49
use BlitzPHP\Utilities\String\Text;
50
use BlitzPHP\View\View;
51
use Psr\Log\LoggerInterface;
52
use stdClass;
53
54
/**
55
 * Service
56
 *
57
 * Les services sont simplement d'autres classes/bibliothèques que le système utilise
58
 * pour faire son travail. Ceci est utilisé par BlitzPHP pour permettre au coeur du
59
 * framework à échanger facilement sans affecter l'utilisation à l'intérieur
60
 * le reste de votre application.
61
 *
62
 * Ceci est utilisé à la place d'un conteneur d'injection de dépendance principalement
63
 * en raison de sa simplicité, qui permet un meilleur entretien à long terme
64
 * des applications construites sur BlitzPHP. Un effet secondaire bonus
65
 * est que les IDE sont capables de déterminer quelle classe vous appelez
66
 * alors qu'avec les conteneurs DI, il n'y a généralement aucun moyen pour eux de le faire.
67
 */
68
class Services
69
{
70
    /**
71
     * Cache des instances des services demander comme instance "partagee".
72
     * La cle est le FQCN du service.
73
     */
74
    protected static array $instances = [];
75
76
    /**
77
     * Cache d'autres classe de que nous avons trouver via la methode cacheService.
78
     */
79
    protected static array $services = [];
80
81
    /**
82
     * Avons-nous déjà découvert d'autres Services ?
83
     */
84
    protected static bool $discovered = false;
85
86
    /**
87
     * Un cache des noms de classes de services trouvés.
88
     *
89
     * @var array<string>
90
     */
91
    private static array $serviceNames = [];
92
93
    /**
94
     * La classe Autoloader permet de charger les fichiers simplement.
95
     */
96
    public static function autoloader(bool $shared = true): Autoloader
97
    {
98
        if (true === $shared && isset(static::$instances[Autoloader::class])) {
99 50
            return static::$instances[Autoloader::class];
100
        }
101
102 2
        $config  = static::config()->get('autoload');
103 2
        $helpers = array_merge(['url'], ($config['helpers'] ?? []));
104
105 2
        return static::$instances[Autoloader::class] = new Autoloader($config, $helpers);
0 ignored issues
show
Bug introduced by
It seems like $config can also be of type null; however, parameter $config of BlitzPHP\Autoloader\Autoloader::__construct() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

105
        return static::$instances[Autoloader::class] = new Autoloader(/** @scrutinizer ignore-type */ $config, $helpers);
Loading history...
106
    }
107
108
    /**
109
     * La classe de cache fournit un moyen simple de stocker et de récupérer
110
     * données complexes pour plus tard
111
     */
112
    public static function cache(?array $config = null, bool $shared = true): Cache
113
    {
114
        if (empty($config)) {
115
            $config = static::config()->get('cache');
116
        }
117
118
        if (true === $shared && isset(static::$instances[Cache::class])) {
119
            $instance = static::$instances[Cache::class];
120
            if (empty(func_get_args()[0])) {
121
                return $instance;
122
            }
123
124
            return $instance->setConfig($config);
125
        }
126
127
        return static::$instances[Cache::class] = new Cache($config);
128
    }
129
130
    /**
131
     * La clase Config offre une api fluide por gerer les configurations de l'application
132
     */
133
    public static function config(bool $shared = true): Config
134
    {
135
        if (true === $shared && isset(static::$instances[Config::class])) {
136 80
            return static::$instances[Config::class];
137
        }
138
139
        return static::$instances[Config::class] = new Config();
140
    }
141
142
    /**
143
     * Conteneur d'injection de dependances
144
     */
145
    public static function container(bool $shared = true): Container
146
    {
147
        if (true === $shared && isset(static::$instances[Container::class])) {
148 32
            return static::$instances[Container::class];
149
        }
150
151
        return static::$instances[Container::class] = new Container();
152
    }
153
154
    /**
155
     * Gestionnaire de cookies
156
     */
157
    public static function cookie(bool $shared = true): CookieManagerInterface
158
    {
159
        if (true === $shared && isset(static::$instances[CookieManager::class])) {
160
            return static::$instances[CookieManager::class];
161
        }
162
163
        $config = (object) static::config()->get('cookie');
164
165
        return static::$instances[CookieManager::class] = (new CookieManager())->setDefaultPathAndDomain(
166
            $config->path ?: '/',
167
            $config->domain ?: '',
168
            $config->secure ?: false,
169
            $config->httponly ?: true,
170
            $config->samesite ?: 'Lax'
171
        );
172
    }
173
174
    /**
175
     * Émetteur de réponse au client
176
     */
177
    public static function emitter(bool $shared = true): ResponseEmitter
178
    {
179
        if (true === $shared && isset(static::$instances[ResponseEmitter::class])) {
180
            return static::$instances[ResponseEmitter::class];
181
        }
182
183
        return static::$instances[ResponseEmitter::class] = new ResponseEmitter();
184
    }
185
186
    /**
187
     * La classe Encryption fournit un cryptage bidirectionnel.
188
     */
189
    public static function encrypter(?array $config = null, bool $shared = false): EncrypterInterface
190
    {
191
        if (true === $shared && isset(static::$instances[Encryption::class])) {
192 2
            return static::$instances[Encryption::class];
193
        }
194
195 4
        $config ??= config('encryption');
196 4
        $config     = (object) $config;
197 4
        $encryption = new Encryption($config);
198
199 4
        return static::$instances[Encryption::class] = $encryption->initialize($config);
200
    }
201
202
    /**
203
     * Gestionnaire d'evenement
204
     */
205
    public static function event(bool $shared = true): EventManager
206
    {
207
        if (true === $shared && isset(static::$instances[EventManager::class])) {
208
            return static::$instances[EventManager::class];
209
        }
210
211
        return static::$instances[EventManager::class] = new EventManager();
212
    }
213
214
    /**
215
     * System de gestion de fichier
216
     */
217
    public static function fs(bool $shared = true): Filesystem
218
    {
219
        if (true === $shared && isset(static::$instances[Filesystem::class])) {
220 2
            return static::$instances[Filesystem::class];
221
        }
222
223 2
        return static::$instances[Filesystem::class] = new Filesystem();
224
    }
225
226
    /**
227
     * Responsable du chargement des traductions des chaînes de langue.
228
     *
229
     * @deprecated 0.9 use translators instead
230
     */
231
    public static function language(?string $locale = null, bool $shared = true): Translate
232
    {
233
        return static::translator($locale, $shared);
234
    }
235
236
    /**
237
     * Le file locator fournit des methodes utilitaire pour chercher les fichiers non-classes
238
     * dans les dossiers de namespace. C'est une excelente methode pour charger les 'vues', 'helpers', et 'libraries'.
239
     */
240
    public static function locator(bool $shared = true): LocatorInterface
241
    {
242
        if ($shared && isset(static::$instances[Locator::class])) {
243 62
            return static::$instances[Locator::class];
244
        }
245
246
        return static::$instances[Locator::class] = new Locator(static::autoloader());
247
    }
248
249
    /**
250
     * La classe Logger est une classe Logging compatible PSR-3 qui prend en charge
251
     * plusieurs gestionnaires qui traitent la journalisation réelle.
252
     *
253
     * @return Logger
254
     */
255
    public static function logger(bool $shared = true): LoggerInterface
256
    {
257
        if ($shared && isset(static::$instances[Logger::class])) {
258 2
            return static::$instances[Logger::class];
259
        }
260
261
        return static::$instances[Logger::class] = new Logger();
262
    }
263
264
    /**
265
     * La classe de mail vous permet d'envoyer par courrier électronique via mail, sendmail, SMTP.
266
     */
267
    public static function mail(?array $config = null, bool $shared = true): Mail
268
    {
269
        if (empty($config)) {
270
            $config = static::config()->get('mail');
271
        }
272
273
        if (true === $shared && isset(static::$instances[Mail::class])) {
274
            /** @var Mail $instance */
275
            $instance = static::$instances[Mail::class];
276
            if (empty(func_get_args()[0])) {
277
                return $instance;
278
            }
279
280
            return $instance->merge($config);
281
        }
282
283
        return static::$instances[Mail::class] = new Mail($config);
284
    }
285
286
    /**
287
     * La classe Input générale modélise une requête HTTP.
288
     */
289
    public static function negotiator(?ServerRequest $request = null, bool $shared = true): Negotiator
290
    {
291
        if (empty($request)) {
292 2
            $request = static::request(true);
293
        }
294
295
        if (true === $shared && isset(static::$instances[Negotiator::class])) {
296 2
            $instance = static::$instances[Negotiator::class];
297
            if (empty(func_get_args()[0])) {
298
                return $instance;
299
            }
300
301
            return $instance->setRequest($request);
302
        }
303
304 2
        return static::$instances[Negotiator::class] = new Negotiator($request);
305
    }
306
307
    /**
308
     * La classe des redirections HTTP
309
     */
310
    public static function redirection(bool $shared = true): Redirection
311
    {
312
        if (true === $shared && isset(static::$instances[Redirection::class])) {
313
            return static::$instances[Redirection::class];
314
        }
315
316
        return static::$instances[Redirection::class] = new Redirection(static::factory(UrlGenerator::class));
317
    }
318
319
    /**
320
     * La classe Resquest modélise une reqûete HTTP.
321
     */
322
    public static function request(bool $shared = true): Request
323
    {
324
        if (true === $shared && isset(static::$instances[Request::class])) {
325 28
            return static::$instances[Request::class];
326
        }
327
328
        return static::$instances[Request::class] = new Request();
329
    }
330
331
    /**
332
     * La classe Response modélise une réponse HTTP.
333
     */
334
    public static function response(bool $shared = true): Response
335
    {
336
        if (true === $shared && isset(static::$instances[Response::class])) {
337
            return static::$instances[Response::class];
338
        }
339
340
        return static::$instances[Response::class] = new Response();
341
    }
342
343
    /**
344
     * CacheResponse
345
     */
346
    public static function responsecache(bool $shared = true): ResponseCache
347
    {
348
        if (true === $shared && isset(static::$instances[ResponseCache::class])) {
349
            return static::$instances[ResponseCache::class];
350
        }
351
352
        return static::$instances[ResponseCache::class] = new ResponseCache(static::cache(), static::config()->get('cache.cache_query_string'));
0 ignored issues
show
Bug introduced by
It seems like static::config()->get('cache.cache_query_string') can also be of type null; however, parameter $cacheQueryString of BlitzPHP\Cache\ResponseCache::__construct() does only seem to accept array|boolean, maybe add an additional type check? ( Ignorable by Annotation )

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

352
        return static::$instances[ResponseCache::class] = new ResponseCache(static::cache(), /** @scrutinizer ignore-type */ static::config()->get('cache.cache_query_string'));
Loading history...
353
    }
354
355
    /**
356
     * Le service Routes est une classe qui permet de construire facilement
357
     * une collection d'itinéraires.
358
     */
359
    public static function routes(bool $shared = true): RouteCollection
360
    {
361
        if (true === $shared && isset(static::$instances[RouteCollection::class])) {
362 2
            return static::$instances[RouteCollection::class];
363
        }
364
365 8
        return static::$instances[RouteCollection::class] = new RouteCollection(static::locator(), (object) static::config()->get('routing'));
366
    }
367
368
    /**
369
     * La classe Router utilise le tableau de routes d'une RouteCollection et détermine
370
     * le contrôleur et la méthode corrects à exécuter.
371
     */
372
    public static function router(?RouteCollection $routes = null, ?ServerRequest $request = null, bool $shared = true): Router
373
    {
374
        if (true === $shared && isset(static::$instances[Router::class])) {
375 8
            return static::$instances[Router::class];
376
        }
377
378
        if (empty($routes)) {
379 8
            $routes = static::routes(true);
380
        }
381
        if (empty($request)) {
382 8
            $request = static::request(true);
383
        }
384
385 8
        return static::$instances[Router::class] = new Router($routes, $request);
386
    }
387
388
    /**
389
     * Retourne le gestionnaire de session.
390
     */
391
    public static function session(bool $shared = true): Store
392
    {
393
        if (true === $shared && isset(static::$instances[Store::class])) {
394
            return static::$instances[Store::class];
395
        }
396
397
        $config = static::config()->get('session');
398
        $db     = null;
399
400
        if (Text::contains($config['handler'], [DatabaseSessionHandler::class, 'database'])) {
401
            $group = $config['group'] ?? static::config()->get('database.connection');
402
            $db    = static::singleton(ConnectionResolverInterface::class)->connection($group);
403
404
            $driver = $db->getPlatform();
405
406
            if (Text::contains($driver, ['mysql', MySQLSessionHandler::class])) {
407
                $config['handler'] = MySQLSessionHandler::class;
408
            } elseif (Text::contains($driver, ['postgre', PostgreSessionHandler::class])) {
409
                $config['handler'] = PostgreSessionHandler::class;
410
            }
411
        }
412
413
        Cookie::setDefaults($cookies = static::config()->get('cookie'));
0 ignored issues
show
Bug introduced by
It seems like $cookies = static::config()->get('cookie') can also be of type null; however, parameter $options of BlitzPHP\Session\Cookie\Cookie::setDefaults() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

413
        Cookie::setDefaults(/** @scrutinizer ignore-type */ $cookies = static::config()->get('cookie'));
Loading history...
414
        $session = new Store($config, $cookies, Helpers::ipAddress());
0 ignored issues
show
Bug introduced by
It seems like $cookies can also be of type null; however, parameter $cookie of BlitzPHP\Session\Store::__construct() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

414
        $session = new Store($config, /** @scrutinizer ignore-type */ $cookies, Helpers::ipAddress());
Loading history...
Bug introduced by
It seems like $config can also be of type null; however, parameter $config of BlitzPHP\Session\Store::__construct() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

414
        $session = new Store(/** @scrutinizer ignore-type */ $config, $cookies, Helpers::ipAddress());
Loading history...
415
        $session->setLogger(static::logger());
416
        $session->setDatabase($db);
417
418
        if (session_status() === PHP_SESSION_NONE) {
419
            $session->start();
420
        }
421
422
        return static::$instances[Store::class] = $session;
423
    }
424
425
    /**
426
     * System de gestion de fichier par disque
427
     */
428
    public static function storage(bool $shared = true): FilesystemManager
429
    {
430
        if ($shared && isset(static::$instances[FilesystemManager::class])) {
431 2
            return static::$instances[FilesystemManager::class];
432
        }
433
434 2
        return static::$instances[FilesystemManager::class] = new FilesystemManager(static::config()->get('filesystems'));
0 ignored issues
show
Bug introduced by
It seems like static::config()->get('filesystems') can also be of type null; however, parameter $config of BlitzPHP\Filesystem\File...mManager::__construct() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

434
        return static::$instances[FilesystemManager::class] = new FilesystemManager(/** @scrutinizer ignore-type */ static::config()->get('filesystems'));
Loading history...
435
    }
436
437
    /**
438
     * La classe Timer fournit un moyen simple d'évaluer des parties de votre application.
439
     */
440
    public static function timer(bool $shared = true): Timer
441
    {
442
        if (true === $shared && isset(static::$instances[Timer::class])) {
443
            return static::$instances[Timer::class];
444
        }
445
446
        return static::$instances[Timer::class] = new Timer();
447
    }
448
449
    /**
450
     * Renvoie la barre d'outils de débogage.
451
     */
452
    public static function toolbar(?stdClass $config = null, bool $shared = true): Toolbar
453
    {
454
        if ($shared && isset(static::$instances[Toolbar::class])) {
455
            return static::$instances[Toolbar::class];
456
        }
457
458
        $config ??= (object) config('toolbar');
459
460
        return static::$instances[Toolbar::class] = new Toolbar($config);
0 ignored issues
show
Bug introduced by
It seems like $config can also be of type BlitzPHP\Config\Config; however, parameter $config of BlitzPHP\Debug\Toolbar::__construct() does only seem to accept null|stdClass, maybe add an additional type check? ( Ignorable by Annotation )

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

460
        return static::$instances[Toolbar::class] = new Toolbar(/** @scrutinizer ignore-type */ $config);
Loading history...
461
    }
462
463
    /**
464
     * Responsable du chargement des traductions des chaînes de langue.
465
     */
466
    public static function translator(?string $locale = null, bool $shared = true): Translate
467
    {
468
        if (empty($locale)) {
469
            if (empty($locale = static::$instances[Translate::class . 'locale'] ?? null)) {
470 2
                $config = static::config()->get('app');
471
472
                if (empty($locale = static::negotiator()->language($config['supported_locales']))) {
473 2
                    $locale = $config['language'];
474
                }
475
476 2
                static::$instances[Translate::class . 'locale'] = $locale;
477
            }
478
        }
479
480
        if (true === $shared && isset(static::$instances[Translate::class])) {
481 18
            return static::$instances[Translate::class]->setLocale($locale);
482
        }
483
484 2
        return static::$instances[Translate::class] = new Translate($locale, static::locator());
485
    }
486
487
    /**
488
     * La classe URI fournit un moyen de modéliser et de manipuler les URI.
489
     */
490
    public static function uri(?string $uri = null, bool $shared = true): Uri
491
    {
492
        if (true === $shared && isset(static::$instances[Uri::class])) {
493
            return static::$instances[Uri::class]->setURI($uri);
494
        }
495
496
        return static::$instances[Uri::class] = new Uri($uri);
497
    }
498
499
    /**
500
     * La classe Renderer est la classe qui affiche réellement un fichier à l'utilisateur.
501
     * La classe View par défaut dans BlitzPHP est intentionnellement simple, mais
502
     * le service peut facilement être remplacé par un moteur de modèle si l'utilisateur en a besoin.
503
     */
504
    public static function viewer(bool $shared = true): View
505
    {
506
        if (true === $shared && isset(static::$instances[View::class])) {
507
            return static::$instances[View::class];
508
        }
509
510
        return static::$instances[View::class] = new View();
511
    }
512
513
    /**
514
     * Offre la possibilité d'effectuer des appels insensibles à la casse des noms de service.
515
     *
516
     * @return mixed
517
     */
518
    public static function __callStatic(string $name, array $arguments)
519
    {
520
        if (null === $service = static::serviceExists($name)) {
521
            return static::discoverServices($name, $arguments);
522
        }
523
524
        return $service::$name(...$arguments);
525
    }
526
527
    /**
528
     * Vérifiez si le service demandé est défini et renvoyez la classe déclarante.
529
     * Renvoie null s'il n'est pas trouvé.
530
     */
531
    public static function serviceExists(string $name): ?string
532
    {
533
        static::cacheServices();
534
        $services = array_merge(self::$serviceNames, [self::class]);
535
        $name     = strtolower($name);
536
537
        foreach ($services as $service) {
538
            if (method_exists($service, $name)) {
539
                return $service;
540
            }
541
        }
542
543
        return null;
544
    }
545
546
    /**
547
     * Essaie d'obtenir un service à partir du conteneur
548
     *
549
     * @return mixed
550
     */
551
    protected static function discoverServices(string $name, array $arguments)
552
    {
553
        if (true !== array_pop($arguments)) {
554
            return static::factory($name, $arguments);
555
        }
556
557
        return static::singleton($name, ...$arguments);
558
    }
559
560
    protected static function cacheServices(): void
561
    {
562
        if (! static::$discovered) {
563
            $locator = static::locator();
564
            $files   = $locator->search('Config/Services');
565
566
            // Obtenez des instances de toutes les classes de service et mettez-les en cache localement.
567
            foreach ($files as $file) {
568
                if (false === $classname = $locator->findQualifiedNameFromPath($file)) {
569
                    continue;
570
                }
571
                if (self::class !== $classname) {
572
                    self::$serviceNames[] = $classname;
573
                    static::$services[]   = new $classname();
574
                }
575
            }
576
577
            static::$discovered = true;
578
        }
579
    }
580
581
    /**
582
     * Injecter une seule instance de la classe donnée
583
     *
584
     * @return mixed
585
     */
586
    public static function singleton(string $name)
587
    {
588
        $arguments = func_get_args();
589
        $name      = array_shift($arguments);
590
591
        if (empty(static::$instances[$name])) {
592
            if (! empty($arguments)) {
593
                static::$instances[$name] = static::factory($name, $arguments);
594
            } else {
595
                static::$instances[$name] = static::container()->get($name);
596
            }
597
        }
598
599
        return static::$instances[$name];
600
    }
601
602
    /**
603
     * Injecter une nouvelle instance de la classe donnée
604
     *
605
     * @return mixed
606
     */
607
    public static function factory(string $name, array $arguments = [])
608
    {
609
        return static::container()->make($name, $arguments);
610
    }
611
612
    /**
613
     * Définissez un objet ou une valeur dans le conteneur.
614
     *
615
     * @param string $name  Nom de l'entrée
616
     * @param mixed  $value utilisez les aides à la définition pour définir les objets
617
     */
618
    public static function set(string $name, $value)
619
    {
620 28
        static::$instances[$name] = $value;
621 28
        static::container()->set($name, $value);
622
    }
623
624
    /**
625
     * Réinitialisez les instances partagées et les simulations pour les tests.
626
     */
627
    public static function reset(bool $initAutoloader = true): void
628
    {
629
        // static::$mocks     = [];
630
        static::$instances = [];
631
632
        if ($initAutoloader) {
633
            static::autoloader()->initialize();
634
        }
635
    }
636
}
637