Passed
Push — main ( 34042d...ebfbeb )
by Dimitri
14:02
created

Services::cacheServices()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

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

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

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