Test Failed
Push — main ( 96fa17...2c2b65 )
by Dimitri
03:36
created

Services::locator()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

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
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\Debug\Logger;
23
use BlitzPHP\Debug\Timer;
24
use BlitzPHP\Debug\Toolbar;
25
use BlitzPHP\Event\EventManager;
26
use BlitzPHP\Filesystem\Filesystem;
27
use BlitzPHP\Filesystem\FilesystemManager;
28
use BlitzPHP\Http\Negotiator;
29
use BlitzPHP\Http\Redirection;
30
use BlitzPHP\Http\Request;
31
use BlitzPHP\Http\Response;
32
use BlitzPHP\Http\ResponseEmitter;
33
use BlitzPHP\Http\ServerRequest;
34
use BlitzPHP\Http\Uri;
35
use BlitzPHP\Http\UrlGenerator;
36
use BlitzPHP\Mail\Mail;
37
use BlitzPHP\Router\RouteCollection;
38
use BlitzPHP\Router\Router;
39
use BlitzPHP\Security\Encryption\Encryption;
40
use BlitzPHP\Session\Cookie\Cookie;
41
use BlitzPHP\Session\Handlers\Database as DatabaseSessionHandler;
42
use BlitzPHP\Session\Handlers\Database\MySQL as MySQLSessionHandler;
43
use BlitzPHP\Session\Handlers\Database\Postgre as PostgreSessionHandler;
44
use BlitzPHP\Session\Store;
45
use BlitzPHP\Translator\Translate;
46
use BlitzPHP\Utilities\Helpers;
47
use BlitzPHP\Utilities\String\Text;
48
use BlitzPHP\View\View;
49
use Psr\Log\LoggerInterface;
50
use stdClass;
51
52
/**
53
 * Service
54
 *
55
 * Les services sont simplement d'autres classes/bibliothèques que le système utilise
56
 * pour faire son travail. Ceci est utilisé par BlitzPHP pour permettre au coeur du
57
 * framework à échanger facilement sans affecter l'utilisation à l'intérieur
58
 * le reste de votre application.
59
 *
60
 * Ceci est utilisé à la place d'un conteneur d'injection de dépendance principalement
61
 * en raison de sa simplicité, qui permet un meilleur entretien à long terme
62
 * des applications construites sur BlitzPHP. Un effet secondaire bonus
63
 * est que les IDE sont capables de déterminer quelle classe vous appelez
64
 * alors qu'avec les conteneurs DI, il n'y a généralement aucun moyen pour eux de le faire.
65
 */
66
class Services
67
{
68
    /**
69
     * Cache des instances des services demander comme instance "partagee".
70
     * La cle est le FQCN du service.
71
     */
72
    protected static array $instances = [];
73
74
    /**
75
     * Cache d'autres classe de que nous avons trouver via la methode cacheService.
76
     */
77
    protected static array $services = [];
78
79
    /**
80
     * Avons-nous déjà découvert d'autres Services ?
81
     */
82
    protected static bool $discovered = false;
83
84
    /**
85
     * Un cache des noms de classes de services trouvés.
86
     *
87
     * @var array<string>
88
     */
89
    private static array $serviceNames = [];
90
91
    /**
92
     * La classe Autoloader permet de charger les fichiers simplement.
93
     */
94
    public static function autoloader(bool $shared = true): Autoloader
95
    {
96
        if (true === $shared && isset(static::$instances[Autoloader::class])) {
97
            return static::$instances[Autoloader::class];
98
        }
99
100
        $config  = static::config()->get('autoload');
101
        $helpers = array_merge(['url'], ($config['helpers'] ?? []));
102
103
        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

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

330
        return static::$instances[ResponseCache::class] = new ResponseCache(static::cache(), /** @scrutinizer ignore-type */ static::config()->get('cache.cache_query_string'));
Loading history...
331
    }
332
333
    /**
334
     * Le service Routes est une classe qui permet de construire facilement
335
     * une collection d'itinéraires.
336
     */
337
    public static function routes(bool $shared = true): RouteCollection
338
    {
339
        if (true === $shared && isset(static::$instances[RouteCollection::class])) {
340
            return static::$instances[RouteCollection::class];
341
        }
342
343
        return static::$instances[RouteCollection::class] = new RouteCollection(static::locator(), (object) static::config()->get('routing'));
344
    }
345
346
    /**
347
     * La classe Router utilise le tableau de routes d'une RouteCollection et détermine
348
     * le contrôleur et la méthode corrects à exécuter.
349
     */
350
    public static function router(?RouteCollection $routes = null, ?ServerRequest $request = null, bool $shared = true): Router
351
    {
352
        if (true === $shared && isset(static::$instances[Router::class])) {
353
            return static::$instances[Router::class];
354
        }
355
356
        if (empty($routes)) {
357
            $routes = static::routes(true);
358
        }
359
        if (empty($request)) {
360
            $request = static::request(true);
361
        }
362
363
        return static::$instances[Router::class] = new Router($routes, $request);
364
    }
365
366
    /**
367
     * Retourne le gestionnaire de session.
368
     */
369
    public static function session(bool $shared = true): Store
370
    {
371
        if (true === $shared && isset(static::$instances[Store::class])) {
372
            return static::$instances[Store::class];
373
        }
374
375
        $config = static::config()->get('session');
376
        $db     = null;
377
378
        if (Text::contains($config['handler'], [DatabaseSessionHandler::class, 'database'])) {
379
            $group = $config['group'] ?? static::config()->get('database.connection');
380
            $db    = static::singleton(ConnectionResolverInterface::class)->connection($group);
381
382
            $driver = $db->getPlatform();
383
384
            if (Text::contains($driver, ['mysql', MySQLSessionHandler::class])) {
385
                $config['handler'] = MySQLSessionHandler::class;
386
            } elseif (Text::contains($driver, ['postgre', PostgreSessionHandler::class])) {
387
                $config['handler'] = PostgreSessionHandler::class;
388
            }
389
        }
390
391
        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

391
        Cookie::setDefaults(/** @scrutinizer ignore-type */ $cookies = static::config()->get('cookie'));
Loading history...
392
        $session = new Store($config, $cookies, Helpers::ipAddress());
0 ignored issues
show
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

392
        $session = new Store(/** @scrutinizer ignore-type */ $config, $cookies, Helpers::ipAddress());
Loading history...
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

392
        $session = new Store($config, /** @scrutinizer ignore-type */ $cookies, Helpers::ipAddress());
Loading history...
393
        $session->setLogger(static::logger());
394
        $session->setDatabase($db);
395
396
        if (session_status() === PHP_SESSION_NONE) {
397
            $session->start();
398
        }
399
400
        return static::$instances[Store::class] = $session;
401
    }
402
403
    /**
404
     * System de gestion de fichier par disque
405
     */
406
    public static function storage(bool $shared = true): FilesystemManager
407
    {
408
        if ($shared && isset(static::$instances[FilesystemManager::class])) {
409
            return static::$instances[FilesystemManager::class];
410
        }
411
412
        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

412
        return static::$instances[FilesystemManager::class] = new FilesystemManager(/** @scrutinizer ignore-type */ static::config()->get('filesystems'));
Loading history...
413
    }
414
415
    /**
416
     * La classe Timer fournit un moyen simple d'évaluer des parties de votre application.
417
     */
418
    public static function timer(bool $shared = true): Timer
419
    {
420
        if (true === $shared && isset(static::$instances[Timer::class])) {
421
            return static::$instances[Timer::class];
422
        }
423
424
        return static::$instances[Timer::class] = new Timer();
425
    }
426
427
    /**
428
     * Renvoie la barre d'outils de débogage.
429
     */
430
    public static function toolbar(?stdClass $config = null, bool $shared = true): Toolbar
431
    {
432
        if ($shared && isset(static::$instances[Toolbar::class])) {
433
            return static::$instances[Toolbar::class];
434
        }
435
436
        $config ??= (object) config('toolbar');
437
438
        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

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