Test Failed
Push — main ( 94eef3...96fa17 )
by Dimitri
14:06
created

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

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

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

373
        Cookie::setDefaults(/** @scrutinizer ignore-type */ $cookies = static::config()->get('cookie'));
Loading history...
374
        $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

374
        $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

374
        $session = new Store($config, /** @scrutinizer ignore-type */ $cookies, Helpers::ipAddress());
Loading history...
375
        $session->setLogger(static::logger());
376
        $session->setDatabase($db);
377
378
        if (session_status() === PHP_SESSION_NONE) {
379
            $session->start();
380
        }
381
382
        return static::$instances[Store::class] = $session;
383
    }
384
385
    /**
386
     * System de gestion de fichier par disque
387
     */
388
    public static function storage(bool $shared = true): FilesystemManager
389
    {
390
        if ($shared && isset(static::$instances[FilesystemManager::class])) {
391
            return static::$instances[FilesystemManager::class];
392
        }
393
394
        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

394
        return static::$instances[FilesystemManager::class] = new FilesystemManager(/** @scrutinizer ignore-type */ static::config()->get('filesystems'));
Loading history...
395
    }
396
397
    /**
398
     * La classe Timer fournit un moyen simple d'évaluer des parties de votre application.
399
     */
400
    public static function timer(bool $shared = true): Timer
401
    {
402
        if (true === $shared && isset(static::$instances[Timer::class])) {
403
            return static::$instances[Timer::class];
404
        }
405
406
        return static::$instances[Timer::class] = new Timer();
407
    }
408
409
    /**
410
     * Renvoie la barre d'outils de débogage.
411
     */
412
    public static function toolbar(?stdClass $config = null, bool $shared = true): Toolbar
413
    {
414
        if ($shared && isset(static::$instances[Toolbar::class])) {
415
            return static::$instances[Toolbar::class];
416
        }
417
418
        $config ??= (object) config('toolbar');
419
420
        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

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