Passed
Push — main ( d79e5f...42d171 )
by Dimitri
12:00 queued 06:40
created

Services::viewer()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 3
nc 2
nop 1
dl 0
loc 7
ccs 2
cts 2
cp 1
crap 3
rs 10
c 1
b 0
f 0
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\Cache\Cache;
17
use BlitzPHP\Cache\ResponseCache;
18
use BlitzPHP\Config\Config;
19
use BlitzPHP\Contracts\Autoloader\LocatorInterface;
20
use BlitzPHP\Contracts\Cache\CacheInterface;
21
use BlitzPHP\Contracts\Container\ContainerInterface;
22
use BlitzPHP\Contracts\Database\ConnectionResolverInterface;
23
use BlitzPHP\Contracts\Event\EventManagerInterface;
24
use BlitzPHP\Contracts\Mail\MailerInterface;
25
use BlitzPHP\Contracts\Router\RouteCollectionInterface;
26
use BlitzPHP\Contracts\Router\RouterInterface;
27
use BlitzPHP\Contracts\Security\EncrypterInterface;
28
use BlitzPHP\Contracts\Session\CookieManagerInterface;
29
use BlitzPHP\Contracts\Session\SessionInterface;
30
use BlitzPHP\Debug\Logger;
31
use BlitzPHP\Debug\Timer;
32
use BlitzPHP\Debug\Toolbar;
33
use BlitzPHP\Event\EventManager;
34
use BlitzPHP\Filesystem\Filesystem;
35
use BlitzPHP\Filesystem\FilesystemManager;
36
use BlitzPHP\Http\Negotiator;
37
use BlitzPHP\Http\Redirection;
38
use BlitzPHP\Http\Request;
39
use BlitzPHP\Http\Response;
40
use BlitzPHP\Http\ResponseEmitter;
41
use BlitzPHP\Http\ServerRequest;
42
use BlitzPHP\Http\ServerRequestFactory;
43
use BlitzPHP\Http\Uri;
44
use BlitzPHP\Http\UrlGenerator;
45
use BlitzPHP\Mail\Mail;
46
use BlitzPHP\Router\RouteCollection;
47
use BlitzPHP\Router\Router;
48
use BlitzPHP\Security\Encryption\Encryption;
49
use BlitzPHP\Session\Cookie\Cookie;
50
use BlitzPHP\Session\Cookie\CookieManager;
51
use BlitzPHP\Session\Handlers\Database as DatabaseSessionHandler;
52
use BlitzPHP\Session\Handlers\Database\MySQL as MySQLSessionHandler;
53
use BlitzPHP\Session\Handlers\Database\Postgre as PostgreSessionHandler;
54
use BlitzPHP\Session\Store;
55
use BlitzPHP\Translator\Translate;
56
use BlitzPHP\Utilities\Helpers;
57
use BlitzPHP\Utilities\String\Text;
58
use BlitzPHP\View\Components\ComponentLoader;
59
use BlitzPHP\View\View;
60
use Psr\Http\Message\UriInterface;
61
use Psr\Log\LoggerInterface;
62
use stdClass;
63
64
/**
65
 * Service
66
 *
67
 * Les services sont simplement d'autres classes/bibliothèques que le système utilise
68
 * pour faire son travail. Ceci est utilisé par BlitzPHP pour permettre au coeur du
69
 * framework à échanger facilement sans affecter l'utilisation à l'intérieur
70
 * le reste de votre application.
71
 *
72
 * Ceci est utilisé à la place d'un conteneur d'injection de dépendance principalement
73
 * en raison de sa simplicité, qui permet un meilleur entretien à long terme
74
 * des applications construites sur BlitzPHP. Un effet secondaire bonus
75
 * est que les IDE sont capables de déterminer quelle classe vous appelez
76
 * alors qu'avec les conteneurs DI, il n'y a généralement aucun moyen pour eux de le faire.
77
 */
78
class Services
79
{
80
    /**
81
     * Cache des instances des services demander comme instance "partagee".
82
     * La cle est le FQCN du service.
83
     */
84
    protected static array $instances = [];
85
86
    /**
87
     * Objets simulés à tester qui sont renvoyés s'ils existent.
88
     */
89
    protected static array $mocks = [];
90
91
    /**
92
     * Cache d'autres classe de que nous avons trouver via la methode cacheService.
93
     */
94
    protected static array $services = [];
95
96
    /**
97
     * Avons-nous déjà découvert d'autres Services ?
98
     */
99
    protected static bool $discovered = false;
100
101
    /**
102
     * Un cache des noms de classes de services trouvés.
103
     *
104
     * @var list<string>
0 ignored issues
show
Bug introduced by
The type BlitzPHP\Container\list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
105
     */
106
    private static array $serviceNames = [];
107
108
    /**
109
     * La classe Autoloader permet de charger les fichiers simplement.
110
     */
111
    public static function autoloader(bool $shared = true): Autoloader
112
    {
113
        if (true === $shared && isset(static::$instances[Autoloader::class])) {
114 68
            return static::$instances[Autoloader::class];
115
        }
116
117 2
        $config  = static::config()->get('autoload');
118 2
        $helpers = array_merge(['url'], ($config['helpers'] ?? []));
119
120 2
        return static::$instances[Autoloader::class] = new Autoloader(/** @scrutinizer ignore-type */ $config, $helpers);
121
    }
122
123
    /**
124
     * La classe de cache fournit un moyen simple de stocker et de récupérer
125
     * données complexes pour plus tard
126
     *
127
     * @return Cache
128
     */
129
    public static function cache(?array $config = null, bool $shared = true): CacheInterface
130
    {
131
        if ($config === null || $config === []) {
132 10
            $config = static::config()->get('cache');
133
        }
134
135
        if (true === $shared && isset(static::$instances[Cache::class])) {
136 10
            $instance = static::$instances[Cache::class];
137
            if (empty(func_get_args()[0])) {
138 10
                return $instance;
139
            }
140
141 2
            return $instance->setConfig($config);
142
        }
143
144 2
        return static::$instances[Cache::class] = new Cache($config);
145
    }
146
147
    /**
148
     * Les composants sont destinées à vous permettre d'insérer du HTML dans la vue
149
     * qui a été généré par n'importe quel appel dans le système.
150
     */
151
    public static function componentLoader(bool $shared = true): ComponentLoader
152
    {
153
        if (true === $shared && isset(static::$instances[ComponentLoader::class])) {
154 2
            return static::$instances[ComponentLoader::class];
155
        }
156
157 2
        return static::$instances[ComponentLoader::class] = new ComponentLoader(static::cache());
158
    }
159
160
    /**
161
     * La clase Config offre une api fluide por gerer les configurations de l'application
162
     */
163
    public static function config(bool $shared = true): Config
164
    {
165
        if (true === $shared && isset(static::$instances[Config::class])) {
166 221
            return static::$instances[Config::class];
167
        }
168
169
        return static::$instances[Config::class] = new Config();
170
    }
171
172
    /**
173
     * Conteneur d'injection de dependances
174
     *
175
     * @return Container
176
     */
177
    public static function container(bool $shared = true): ContainerInterface
178
    {
179
        if (true === $shared && isset(static::$instances[Container::class])) {
180 101
            return static::$instances[Container::class];
181
        }
182
183
        return static::$instances[Container::class] = new Container();
184
    }
185
186
    /**
187
     * Gestionnaire de cookies
188
     *
189
     * @return CookieManager
190
     */
191
    public static function cookie(bool $shared = true): CookieManagerInterface
192
    {
193
        if (true === $shared && isset(static::$instances[CookieManager::class])) {
194 2
            return static::$instances[CookieManager::class];
195
        }
196
197 2
        $config = (object) static::config()->get('cookie');
198
199
        return static::$instances[CookieManager::class] = (new CookieManager())->setDefaultPathAndDomain(
200
            $config->path ?: '/',
201
            $config->domain ?: '',
202
            $config->secure ?: false,
203
            $config->httponly ?: true,
204
            $config->samesite ?: 'Lax'
205 2
        );
206
    }
207
208
    /**
209
     * Émetteur de réponse au client
210
     */
211
    public static function emitter(bool $shared = true): ResponseEmitter
212
    {
213
        if (true === $shared && isset(static::$instances[ResponseEmitter::class])) {
214 2
            return static::$instances[ResponseEmitter::class];
215
        }
216
217 2
        return static::$instances[ResponseEmitter::class] = new ResponseEmitter();
218
    }
219
220
    /**
221
     * La classe Encryption fournit un cryptage bidirectionnel.
222
     *
223
     * @return Encryption
224
     */
225
    public static function encrypter(?array $config = null, bool $shared = false): EncrypterInterface
226
    {
227
        if (true === $shared && isset(static::$instances[Encryption::class])) {
228
            return static::$instances[Encryption::class];
229
        }
230
231
        $config ??= config('encryption');
232
        $config     = (object) $config;
233
        $encryption = new Encryption($config);
234
        $encryption->initialize($config);
235
236
        return static::$instances[Encryption::class] = $encryption;
237
    }
238
239
    /**
240
     * Gestionnaire d'evenement
241
     *
242
     * @return EventManager
243
     */
244
    public static function event(bool $shared = true): EventManagerInterface
245
    {
246
        if (true === $shared && isset(static::$instances[EventManager::class])) {
247 3
            return static::$instances[EventManager::class];
248
        }
249
250
        return static::$instances[EventManager::class] = new EventManager();
251
    }
252
253
    /**
254
     * System de gestion de fichier
255
     */
256
    public static function fs(bool $shared = true): Filesystem
257
    {
258
        if (true === $shared && isset(static::$instances[Filesystem::class])) {
259 22
            return static::$instances[Filesystem::class];
260
        }
261
262 2
        return static::$instances[Filesystem::class] = new Filesystem();
263
    }
264
265
    /**
266
     * Responsable du chargement des traductions des chaînes de langue.
267
     *
268
     * @deprecated 0.9 use translators instead
269
     */
270
    public static function language(?string $locale = null, bool $shared = true): Translate
271
    {
272 2
        return static::translator($locale, $shared);
273
    }
274
275
    /**
276
     * Le file locator fournit des methodes utilitaire pour chercher les fichiers non-classes dans les dossiers de namespace.
277
     * C'est une excelente methode pour charger les 'vues', 'helpers', et 'libraries'.
278
     *
279
     * @return Locator
280
     */
281
    public static function locator(bool $shared = true): LocatorInterface
282
    {
283
        if ($shared && isset(static::$instances[Locator::class])) {
284 153
            return static::$instances[Locator::class];
285
        }
286
287
        return static::$instances[Locator::class] = new Locator(static::autoloader());
288
    }
289
290
    /**
291
     * La classe Logger est une classe Logging compatible PSR-3 qui prend en charge
292
     * plusieurs gestionnaires qui traitent la journalisation réelle.
293
     *
294
     * @return Logger
295
     */
296
    public static function logger(bool $shared = true): LoggerInterface
297
    {
298
        if ($shared && isset(static::$instances[Logger::class])) {
299 43
            return static::$instances[Logger::class];
300
        }
301
302
        return static::$instances[Logger::class] = new Logger();
303
    }
304
305
    /**
306
     * La classe de mail vous permet d'envoyer par courrier électronique via mail, sendmail, SMTP.
307
     *
308
     * @return Mail
309
     */
310
    public static function mail(?array $config = null, bool $shared = true): MailerInterface
311
    {
312
        if ($config === null || $config === []) {
313
            $config = static::config()->get('mail');
314
        }
315
316
        if (true === $shared && isset(static::$instances[Mail::class])) {
317
            /** @var Mail $instance */
318
            $instance = static::$instances[Mail::class];
319
            if (empty(func_get_args()[0])) {
320
                return $instance;
321
            }
322
323
            return $instance->merge($config);
324
        }
325
326
        return static::$instances[Mail::class] = new Mail($config);
327
    }
328
329
    /**
330
     * La classe Input générale modélise une requête HTTP.
331
     */
332
    public static function negotiator(?ServerRequest $request = null, bool $shared = true): Negotiator
333
    {
334
        if ($request === null) {
335
            $request = static::request(true);
336
        }
337
338
        if (true === $shared && isset(static::$instances[Negotiator::class])) {
339
            $instance = static::$instances[Negotiator::class];
340
            if (empty(func_get_args()[0])) {
341
                return $instance;
342
            }
343
344
            return $instance->setRequest($request);
345
        }
346
347
        return static::$instances[Negotiator::class] = new Negotiator($request);
348
    }
349
350
    /**
351
     * La classe des redirections HTTP
352
     */
353
    public static function redirection(bool $shared = true): Redirection
354
    {
355
        if (true === $shared && isset(static::$instances[Redirection::class])) {
356
            return static::$instances[Redirection::class];
357
        }
358
359
        return static::$instances[Redirection::class] = new Redirection(static::factory(UrlGenerator::class));
360
    }
361
362
    /**
363
     * La classe Resquest modélise une reqûete HTTP.
364
     */
365
    public static function request(bool $shared = true): Request
366
    {
367
        if (true === $shared && isset(static::$instances[Request::class])) {
368 105
            return static::$instances[Request::class];
369
        }
370
371 2
        return static::$instances[Request::class] = ServerRequestFactory::fromGlobals();
372
    }
373
374
    /**
375
     * La classe Response modélise une réponse HTTP.
376
     */
377
    public static function response(bool $shared = true): Response
378
    {
379
        if (true === $shared && isset(static::$instances[Response::class])) {
380 8
            return static::$instances[Response::class];
381
        }
382
383 1
        return static::$instances[Response::class] = new Response();
384
    }
385
386
    /**
387
     * CacheResponse
388
     */
389
    public static function responsecache(bool $shared = true): ResponseCache
390
    {
391
        if (true === $shared && isset(static::$instances[ResponseCache::class])) {
392
            return static::$instances[ResponseCache::class];
393
        }
394
395
        return static::$instances[ResponseCache::class] = new ResponseCache(static::cache(), /** @scrutinizer ignore-type */ static::config()->get('cache.cache_query_string'));
396
    }
397
398
    /**
399
     * Le service Routes est une classe qui permet de construire facilement une collection de routes.
400
     *
401
     * @return RouteCollection
402
     */
403
    public static function routes(bool $shared = true): RouteCollectionInterface
404
    {
405
        if (true === $shared && isset(static::$instances[RouteCollection::class])) {
406 4
            return static::$instances[RouteCollection::class];
407
        }
408
409 10
        return static::$instances[RouteCollection::class] = new RouteCollection(static::locator(), (object) static::config()->get('routing'));
410
    }
411
412
    /**
413
     * La classe Router utilise le tableau de routes d'une RouteCollection et détermine
414
     * le contrôleur et la méthode corrects à exécuter.
415
     *
416
     * @return Router
417
     */
418
    public static function router(?RouteCollection $routes = null, ?ServerRequest $request = null, bool $shared = true): RouterInterface
419
    {
420
        if (true === $shared && isset(static::$instances[Router::class])) {
421 12
            return static::$instances[Router::class];
422
        }
423
424
        if ($routes === null) {
425 12
            $routes = static::routes(true);
426
        }
427
        if ($request === null) {
428 2
            $request = static::request(true);
429
        }
430
431 12
        return static::$instances[Router::class] = new Router($routes, $request);
432
    }
433
434
    /**
435
     * Retourne le gestionnaire de session.
436
     *
437
     * @return Store
438
     */
439
    public static function session(bool $shared = true): SessionInterface
440
    {
441
        if (true === $shared && isset(static::$instances[Store::class])) {
442 10
            return static::$instances[Store::class];
443
        }
444
445 19
        $config = static::config()->get('session');
446 19
        $db     = null;
447
448
        if (Text::contains($config['handler'], [DatabaseSessionHandler::class, 'database'])) {
449 19
            $group = $config['group'] ?? static::config()->get('database.connection');
450
            $db    = static::singleton(ConnectionResolverInterface::class)->connection($group);
451
452
            $driver = $db->getPlatform();
453
454
            if (Text::contains($driver, ['mysql', MySQLSessionHandler::class])) {
455
                $config['handler'] = MySQLSessionHandler::class;
456
            } elseif (Text::contains($driver, ['postgre', PostgreSessionHandler::class])) {
457
                $config['handler'] = PostgreSessionHandler::class;
458
            }
459
        }
460
461 19
        Cookie::setDefaults($cookies = /** @scrutinizer ignore-type */ 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

461
        Cookie::setDefaults(/** @scrutinizer ignore-type */ $cookies = /** @scrutinizer ignore-type */ static::config()->get('cookie'));
Loading history...
462 19
        $session = new Store((array) $config, (array) $cookies, Helpers::ipAddress());
463 19
        $session->setLogger(static::logger());
464 19
        $session->setDatabase($db);
465
466
        if (session_status() === PHP_SESSION_NONE) {
467 19
            $session->start();
468
        }
469
470 19
        return static::$instances[Store::class] = $session;
471
    }
472
473
    /**
474
     * System de gestion de fichier par disque
475
     */
476
    public static function storage(bool $shared = true): FilesystemManager
477
    {
478
        if ($shared && isset(static::$instances[FilesystemManager::class])) {
479 2
            return static::$instances[FilesystemManager::class];
480
        }
481
482 2
        return static::$instances[FilesystemManager::class] = new FilesystemManager(/** @scrutinizer ignore-type */ static::config()->get('filesystems'));
483
    }
484
485
    /**
486
     * La classe Timer fournit un moyen simple d'évaluer des parties de votre application.
487
     */
488
    public static function timer(bool $shared = true): Timer
489
    {
490
        if (true === $shared && isset(static::$instances[Timer::class])) {
491
            return static::$instances[Timer::class];
492
        }
493
494
        return static::$instances[Timer::class] = new Timer();
495
    }
496
497
    /**
498
     * Renvoie la barre d'outils de débogage.
499
     */
500
    public static function toolbar(?stdClass $config = null, bool $shared = true): Toolbar
501
    {
502
        if ($shared && isset(static::$instances[Toolbar::class])) {
503
            return static::$instances[Toolbar::class];
504
        }
505
506
        $config ??= (object) static::config()->get('toolbar');
507
508
        return static::$instances[Toolbar::class] = new Toolbar($config);
509
    }
510
511
    /**
512
     * Responsable du chargement des traductions des chaînes de langue.
513
     */
514
    public static function translator(?string $locale = null, bool $shared = true): Translate
515
    {
516
        if (null === $locale || $locale === '' || $locale === '0') {
517 66
            $locale = static::request()->getLocale();
518
        }
519
520
        if (true === $shared && isset(static::$instances[Translate::class])) {
521 64
            return static::$instances[Translate::class]->setLocale($locale);
522
        }
523
524 4
        return static::$instances[Translate::class] = new Translate($locale, static::locator());
525
    }
526
527
    /**
528
     * La classe URI fournit un moyen de modéliser et de manipuler les URI.
529
     *
530
     * @return Uri
531
     */
532
    public static function uri(?string $uri = null, bool $shared = true): UriInterface
533
    {
534
        if (true === $shared && isset(static::$instances[Uri::class])) {
535
            return static::$instances[Uri::class]->setURI($uri);
536
        }
537
538
        return static::$instances[Uri::class] = new Uri($uri);
539
    }
540
541
    /**
542
     * La classe Renderer est la classe qui affiche réellement un fichier à l'utilisateur.
543
     * La classe View par défaut dans BlitzPHP est intentionnellement simple, mais
544
     * le service peut facilement être remplacé par un moteur de modèle si l'utilisateur en a besoin.
545
     */
546
    public static function viewer(bool $shared = true): View
547
    {
548
        if (true === $shared && isset(static::$instances[View::class])) {
549 2
            return static::$instances[View::class];
550
        }
551
552 2
        return static::$instances[View::class] = new View();
553
    }
554
555
    /**
556
     * Offre la possibilité d'effectuer des appels insensibles à la casse des noms de service.
557
     *
558
     * @return mixed
559
     */
560
    public static function __callStatic(string $name, array $arguments)
561
    {
562
        if (null === $service = static::serviceExists($name)) {
563 22
            return static::discoverServices($name, $arguments);
564
        }
565
566 2
        return $service::$name(...$arguments);
567
    }
568
569
    /**
570
     * Vérifiez si le service demandé est défini et renvoyez la classe déclarante.
571
     * Renvoie null s'il n'est pas trouvé.
572
     */
573
    public static function serviceExists(string $name): ?string
574
    {
575 24
        static::cacheServices();
576 24
        $services = array_merge(self::$serviceNames, [self::class]);
577 24
        $name     = strtolower($name);
578
579
        foreach ($services as $service) {
580
            if (method_exists($service, $name)) {
581 2
                return $service;
582
            }
583
        }
584
585 22
        return null;
586
    }
587
588
    /**
589
     * Injectez un objet fictif pour les tests.
590
     */
591
    public static function injectMock(string $name, object $mock): void
592
    {
593 3
        static::$mocks[strtolower($name)] = $mock;
594
    }
595
596
    /**
597
     * Essaie d'obtenir un service à partir du conteneur
598
     *
599
     * @return mixed
600
     */
601
    protected static function discoverServices(string $name, array $arguments)
602
    {
603
        if (true !== array_pop($arguments)) {
604 22
            return static::factory($name, $arguments);
605
        }
606
607
        return static::singleton($name, ...$arguments);
608
    }
609
610
    protected static function cacheServices(): void
611
    {
612
        if (! static::$discovered) {
613 2
            $locator = static::locator();
614 2
            $files   = $locator->search('Config/Services');
615
616
            // Obtenez des instances de toutes les classes de service et mettez-les en cache localement.
617
            foreach ($files as $file) {
618
                if (false === $classname = $locator->findQualifiedNameFromPath($file)) {
619
                    continue;
620
                }
621
                if (self::class !== $classname) {
622 2
                    self::$serviceNames[] = $classname;
623 2
                    static::$services[]   = new $classname();
624
                }
625
            }
626
627 2
            static::$discovered = true;
628
        }
629
    }
630
631
    /**
632
     * Injecter une seule instance de la classe donnée
633
     *
634
     * @return mixed
635
     */
636
    public static function singleton(string $name)
637
    {
638
        $arguments = func_get_args();
639
        $name      = array_shift($arguments);
640
641
        if (empty(static::$instances[$name])) {
642
            static::$instances[$name] = $arguments !== [] ? static::factory($name, $arguments) : static::container()->get($name);
643
        }
644
645
        return static::$instances[$name];
646
    }
647
648
    /**
649
     * Injecter une nouvelle instance de la classe donnée
650
     *
651
     * @return mixed
652
     */
653
    public static function factory(string $name, array $arguments = [])
654
    {
655 26
        return static::container()->make($name, $arguments);
656
    }
657
658
    /**
659
     * Définissez un objet ou une valeur dans le conteneur.
660
     *
661
     * @param string $name  Nom de l'entrée
662
     * @param mixed  $value utilisez les aides à la définition pour définir les objets
663
     */
664
    public static function set(string $name, $value)
665
    {
666 46
        static::$instances[$name] = $value;
667 46
        static::container()->set($name, $value);
668
    }
669
670
    /**
671
     * Réinitialisez les instances partagées et les simulations pour les tests.
672
     */
673
    public static function reset(bool $initAutoloader = true): void
674
    {
675
        // static::$mocks     = [];
676
        static::$instances = [];
677
678
        if ($initAutoloader) {
679
            static::autoloader()->initialize();
680
        }
681
    }
682
}
683