• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

aplus-framework / mvc / 16357225248

17 Jul 2025 10:10PM UTC coverage: 100.0%. Remained the same
16357225248

push

github

natanfelles
Adds links to service pages

1833 of 1833 relevant lines covered (100.0%)

10.63 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/src/App.php
1
<?php declare(strict_types=1);
2
/*
3
 * This file is part of Aplus Framework MVC Library.
4
 *
5
 * (c) Natan Felles <natanfelles@gmail.com>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace Framework\MVC;
11

12
use Framework\Autoload\Autoloader;
13
use Framework\Autoload\Debug\AutoloadCollection;
14
use Framework\Autoload\Locator;
15
use Framework\Cache\Cache;
16
use Framework\Cache\Debug\CacheCollection;
17
use Framework\Cache\Debug\CacheCollector;
18
use Framework\Cache\Serializer;
19
use Framework\CLI\Command;
20
use Framework\CLI\Console;
21
use Framework\Config\Config;
22
use Framework\Config\Debug\ConfigCollection;
23
use Framework\Config\Debug\ConfigCollector;
24
use Framework\Database\Database;
25
use Framework\Database\Debug\DatabaseCollection;
26
use Framework\Database\Debug\DatabaseCollector;
27
use Framework\Database\Extra\Migrator;
28
use Framework\Debug\Debugger;
29
use Framework\Debug\ExceptionHandler;
30
use Framework\Email\Debug\EmailCollection;
31
use Framework\Email\Debug\EmailCollector;
32
use Framework\Email\Mailer;
33
use Framework\Helpers\Isolation;
34
use Framework\HTTP\AntiCSRF;
35
use Framework\HTTP\CSP;
36
use Framework\HTTP\Debug\HTTPCollection;
37
use Framework\HTTP\Debug\HTTPCollector;
38
use Framework\HTTP\Request;
39
use Framework\HTTP\Response;
40
use Framework\Language\Debug\LanguageCollection;
41
use Framework\Language\Debug\LanguageCollector;
42
use Framework\Language\FallbackLevel;
43
use Framework\Language\Language;
44
use Framework\Log\Debug\LogCollection;
45
use Framework\Log\Debug\LogCollector;
46
use Framework\Log\Logger;
47
use Framework\Log\Loggers\MultiFileLogger;
48
use Framework\Log\LogLevel;
49
use Framework\MVC\Debug\AppCollection;
50
use Framework\MVC\Debug\AppCollector;
51
use Framework\MVC\Debug\ViewsCollection;
52
use Framework\MVC\Debug\ViewsCollector;
53
use Framework\Routing\Debug\RoutingCollection;
54
use Framework\Routing\Debug\RoutingCollector;
55
use Framework\Routing\Router;
56
use Framework\Session\Debug\SessionCollection;
57
use Framework\Session\Debug\SessionCollector;
58
use Framework\Session\SaveHandlers\DatabaseHandler;
59
use Framework\Session\Session;
60
use Framework\Validation\Debug\ValidationCollection;
61
use Framework\Validation\Debug\ValidationCollector;
62
use Framework\Validation\FilesValidator;
63
use Framework\Validation\Validation;
64
use LogicException;
65
use ReflectionClass;
66
use ReflectionException;
67
use SensitiveParameter;
68

69
/**
70
 * Class App.
71
 *
72
 * @package mvc
73
 */
74
class App
75
{
76
    /**
77
     * Array with keys with names of services and their values have arrays where
78
     * the keys are the names of the instances and the values are the objects.
79
     *
80
     * @var array<string,array<string,object>>
81
     */
82
    protected static array $services = [];
83
    /**
84
     * Tells if the App is running.
85
     *
86
     * @var bool
87
     */
88
    protected static bool $isRunning = false;
89
    /**
90
     * The Config instance.
91
     *
92
     * @var Config|null
93
     */
94
    protected static ?Config $config;
95
    /**
96
     * Tells if the request is by command line. Updating directly makes it
97
     * possible to run tests simulating HTTP or CLI.
98
     *
99
     * @var bool|null
100
     */
101
    protected static ?bool $isCli = null;
102
    /**
103
     * The App collector instance that is set when in debug mode.
104
     *
105
     * @var AppCollector
106
     */
107
    protected static AppCollector $debugCollector;
108
    /**
109
     * Variables set in the $_SERVER super-global in command-line requests.
110
     *
111
     * @var array<string,mixed>
112
     */
113
    protected static array $defaultServerVars = [
114
        'REMOTE_ADDR' => '127.0.0.1',
115
        'REQUEST_METHOD' => 'GET',
116
        'REQUEST_URI' => '/',
117
        'SERVER_PROTOCOL' => 'HTTP/1.1',
118
        'HTTP_HOST' => 'localhost',
119
    ];
120

121
    /**
122
     * Initialize the App.
123
     *
124
     * @param Config|array<string,mixed>|string|null $config The config
125
     * @param bool $debug Set true to enable debug mode. False to disable.
126
     */
127
    public function __construct(
128
        #[SensitiveParameter]
129
        Config | array | string | null $config = null,
130
        bool $debug = false
131
    ) {
132
        if ($debug) {
116✔
133
            $this->debugStart();
11✔
134
        }
135
        if (isset(static::$config)) {
116✔
136
            throw new LogicException('App already initialized');
1✔
137
        }
138
        if (!$config instanceof Config) {
116✔
139
            $config = new Config($config);
12✔
140
        }
141
        static::$config = $config;
116✔
142
        if ($debug) {
116✔
143
            $collection = new AppCollection('App');
11✔
144
            $collection->addCollector(static::$debugCollector);
11✔
145
            static::debugger()->addCollection($collection);
11✔
146
            $configCollector = new ConfigCollector();
11✔
147
            static::$config->setDebugCollector($configCollector);
11✔
148
            $configCollection = new ConfigCollection('Config');
11✔
149
            $configCollection->addCollector($configCollector);
11✔
150
            static::debugger()->addCollection($configCollection);
11✔
151
        }
152
    }
153

154
    /**
155
     * Start debugging the App.
156
     */
157
    protected function debugStart() : void
158
    {
159
        static::$debugCollector = new AppCollector();
11✔
160
        static::$debugCollector->setStartTime()->setStartMemory();
11✔
161
        static::$debugCollector->setApp($this);
11✔
162
    }
163

164
    /**
165
     * Load service configs catching exceptions.
166
     *
167
     * @param string $name The service name
168
     *
169
     * @return array<string,array<string,mixed>>|null The service configs or null
170
     */
171
    protected function loadConfigs(string $name) : ?array
172
    {
173
        $config = static::config();
10✔
174
        try {
175
            $config->load($name);
10✔
176
        } catch (LogicException) {
1✔
177
        }
178
        return $config->getInstances($name);
10✔
179
    }
180

181
    /**
182
     * Make sure to load the autoloader service if its default config is set.
183
     */
184
    protected function loadAutoloader() : void
185
    {
186
        $config = static::config();
10✔
187
        $autoloaderConfigs = $config->getInstances('autoloader');
10✔
188
        if ($config->getDir() !== null) {
10✔
189
            $autoloaderConfigs ??= $this->loadConfigs('autoloader');
9✔
190
        }
191
        if (isset($autoloaderConfigs['default'])) {
10✔
192
            static::autoloader();
10✔
193
        }
194
    }
195

196
    /**
197
     * Make sure to load the exceptionHandler service if its default config is set.
198
     */
199
    protected function loadExceptionHandler() : void
200
    {
201
        $config = static::config();
10✔
202
        $exceptionHandlerConfigs = $config->getInstances('exceptionHandler');
10✔
203
        if ($config->getDir() !== null) {
10✔
204
            $exceptionHandlerConfigs ??= $this->loadConfigs('exceptionHandler');
9✔
205
        }
206
        if (!isset($exceptionHandlerConfigs['default'])) {
10✔
207
            $environment = static::isDebugging()
3✔
208
                ? ExceptionHandler::DEVELOPMENT
2✔
209
                : ExceptionHandler::PRODUCTION;
1✔
210
            $config->set('exceptionHandler', [
3✔
211
                'environment' => $environment,
3✔
212
            ]);
3✔
213
            $exceptionHandlerConfigs = $config->getInstances('exceptionHandler');
3✔
214
        }
215
        if (isset($exceptionHandlerConfigs['default'])) {
10✔
216
            static::exceptionHandler();
10✔
217
        }
218
    }
219

220
    /**
221
     * Prepare the App to run via CLI or HTTP.
222
     */
223
    protected function prepareToRun() : void
224
    {
225
        if (static::$isRunning) {
10✔
226
            throw new LogicException('App is already running');
1✔
227
        }
228
        static::$isRunning = true;
10✔
229
        $this->loadAutoloader();
10✔
230
        $this->loadExceptionHandler();
10✔
231
    }
232

233
    /**
234
     * Run the App on HTTP requests.
235
     */
236
    public function runHttp() : void
237
    {
238
        $this->prepareToRun();
8✔
239
        $router = static::router();
8✔
240
        $response = $router->getResponse();
7✔
241
        $router->match()
7✔
242
            ->run($response->getRequest(), $response)
7✔
243
            ->send();
7✔
244
        if (static::isDebugging()) {
7✔
245
            $this->debugEnd();
3✔
246
        }
247
    }
248

249
    /**
250
     * Ends the debugging of the App and prints the debugbar if there is no
251
     * download file, if the request is not via AJAX and the Content-Type is
252
     * text/html.
253
     */
254
    protected function debugEnd() : void
255
    {
256
        static::$debugCollector->setEndTime()->setEndMemory();
3✔
257
        $response = static::router()->getResponse();
3✔
258
        if (!$response->hasDownload()
3✔
259
            && !$response->getRequest()->isAjax()
3✔
260
            && \str_contains(
3✔
261
                (string) $response->getHeader('Content-Type'),
3✔
262
                'text/html'
3✔
263
            )
3✔
264
        ) {
265
            echo static::debugger()->renderDebugbar();
3✔
266
        }
267
    }
268

269
    /**
270
     * Detects if the request is via command-line and runs as a CLI request,
271
     * otherwise runs as HTTP.
272
     */
273
    public function run() : void
274
    {
275
        static::isCli() ? $this->runCli() : $this->runHttp();
2✔
276
    }
277

278
    /**
279
     * Run the App on CLI requests.
280
     */
281
    public function runCli() : void
282
    {
283
        $this->prepareToRun();
3✔
284
        static::console()->run();
2✔
285
    }
286

287
    /**
288
     * Get the Config instance.
289
     *
290
     * @return Config
291
     */
292
    public static function config() : Config
293
    {
294
        return static::$config;
111✔
295
    }
296

297
    /**
298
     * Get a service.
299
     *
300
     * @param string $name Service name
301
     * @param string $instance Service instance name
302
     *
303
     * @return object|null The service object or null
304
     */
305
    public static function getService(string $name, string $instance = 'default') : ?object
306
    {
307
        return static::$services[$name][$instance] ?? null;
110✔
308
    }
309

310
    /**
311
     * Set a service.
312
     *
313
     * @template T of object
314
     *
315
     * @param string $name Service name
316
     * @param T $service Service object
317
     * @param string $instance Service instance name
318
     *
319
     * @return T The service object
320
     */
321
    public static function setService(
322
        string $name,
323
        object $service,
324
        string $instance = 'default'
325
    ) : object {
326
        return static::$services[$name][$instance] = $service;
110✔
327
    }
328

329
    /**
330
     * Remove services.
331
     *
332
     * @param string $name Service name
333
     * @param string|null $instance Service instance name or null to remove all instances
334
     */
335
    public static function removeService(string $name, ?string $instance = 'default') : void
336
    {
337
        if ($instance === null) {
3✔
338
            unset(static::$services[$name]);
1✔
339
            return;
1✔
340
        }
341
        unset(static::$services[$name][$instance]);
3✔
342
    }
343

344
    /**
345
     * Get a autoloader service.
346
     *
347
     * @param string $instance The autoloader instance name
348
     *
349
     * @return Autoloader
350
     */
351
    public static function autoloader(string $instance = 'default') : Autoloader
352
    {
353
        $service = static::getService('autoloader', $instance);
74✔
354
        if ($service) {
74✔
355
            return $service; // @phpstan-ignore-line
11✔
356
        }
357
        if (static::isDebugging()) {
74✔
358
            $start = \microtime(true);
7✔
359
            $service = static::setAutoloader($instance);
7✔
360
            $end = \microtime(true);
7✔
361
            $service->setDebugCollector(name: $instance);
7✔
362
            $collection = static::debugger()->getCollection('Autoload')
7✔
363
                ?? new AutoloadCollection('Autoload');
7✔
364
            $collection->addCollector($service->getDebugCollector());
7✔
365
            static::debugger()->addCollection($collection);
7✔
366
            static::addDebugData('autoloader', $instance, $start, $end);
7✔
367
            return $service;
7✔
368
        }
369
        return static::setAutoloader($instance);
67✔
370
    }
371

372
    /**
373
     * Set a autoloader service.
374
     *
375
     * @param string $instance The autoloader instance name
376
     *
377
     * @return Autoloader
378
     */
379
    protected static function setAutoloader(string $instance) : Autoloader
380
    {
381
        $config = static::config()->get('autoloader', $instance);
74✔
382
        $service = new Autoloader($config['register'] ?? true, $config['extensions'] ?? '.php');
74✔
383
        if (isset($config['namespaces'])) {
74✔
384
            $service->setNamespaces($config['namespaces']);
69✔
385
        }
386
        if (isset($config['classes'])) {
74✔
387
            $service->setClasses($config['classes']);
67✔
388
        }
389
        return static::setService('autoloader', $service, $instance);
74✔
390
    }
391

392
    /**
393
     * Get a cache service.
394
     *
395
     * @param string $instance The cache instance name
396
     *
397
     * @return Cache
398
     */
399
    public static function cache(string $instance = 'default') : Cache
400
    {
401
        $service = static::getService('cache', $instance);
13✔
402
        if ($service) {
13✔
403
            return $service; // @phpstan-ignore-line
12✔
404
        }
405
        if (static::isDebugging()) {
13✔
406
            $start = \microtime(true);
1✔
407
            $service = static::setCache($instance);
1✔
408
            $end = \microtime(true);
1✔
409
            $collector = new CacheCollector($instance);
1✔
410
            $service->setDebugCollector($collector);
1✔
411
            $collection = static::debugger()->getCollection('Cache')
1✔
412
                ?? new CacheCollection('Cache');
1✔
413
            $collection->addCollector($collector);
1✔
414
            static::debugger()->addCollection($collection);
1✔
415
            static::addDebugData('cache', $instance, $start, $end);
1✔
416
            return $service;
1✔
417
        }
418
        return static::setCache($instance);
12✔
419
    }
420

421
    /**
422
     * Set a cache service.
423
     *
424
     * @param string $instance The cache instance name
425
     *
426
     * @return Cache
427
     */
428
    protected static function setCache(string $instance) : Cache
429
    {
430
        $config = static::config()->get('cache', $instance);
13✔
431
        $logger = null;
13✔
432
        if (isset($config['logger_instance'])) {
13✔
433
            $logger = static::logger($config['logger_instance']);
13✔
434
        }
435
        $config['serializer'] ??= Serializer::PHP;
13✔
436
        if (\is_string($config['serializer'])) {
13✔
437
            $config['serializer'] = Serializer::from($config['serializer']);
13✔
438
        }
439
        /**
440
         * @var Cache $service
441
         */
442
        $service = new $config['class'](
13✔
443
            $config['configs'] ?? [],
13✔
444
            $config['prefix'] ?? null,
13✔
445
            $config['serializer'],
13✔
446
            $logger
13✔
447
        );
13✔
448
        if (isset($config['default_ttl'])) {
13✔
449
            $service->setDefaultTtl($config['default_ttl']);
13✔
450
        }
451
        return static::setService('cache', $service, $instance);
13✔
452
    }
453

454
    /**
455
     * Get a console service.
456
     *
457
     * @param string $instance The console instance name
458
     *
459
     * @throws ReflectionException
460
     *
461
     * @return Console
462
     */
463
    public static function console(string $instance = 'default') : Console
464
    {
465
        $service = static::getService('console', $instance);
4✔
466
        if ($service) {
4✔
467
            return $service; // @phpstan-ignore-line
1✔
468
        }
469
        if (static::isDebugging()) {
4✔
470
            $start = \microtime(true);
1✔
471
            $service = static::setConsole($instance);
1✔
472
            $end = \microtime(true);
1✔
473
            static::addDebugData('console', $instance, $start, $end);
1✔
474
            return $service;
1✔
475
        }
476
        return static::setConsole($instance);
3✔
477
    }
478

479
    /**
480
     * Set a console service.
481
     *
482
     * @param string $instance The console instance name
483
     *
484
     * @throws ReflectionException
485
     *
486
     * @return Console
487
     */
488
    protected static function setConsole(string $instance) : Console
489
    {
490
        $config = static::config()->get('console', $instance);
4✔
491
        $language = null;
4✔
492
        if (isset($config['language_instance'])) {
4✔
493
            $language = static::language($config['language_instance']);
4✔
494
        }
495
        $service = new Console($language);
4✔
496
        $locator = static::locator($config['locator_instance'] ?? 'default');
4✔
497
        if (isset($config['find_in_namespaces']) && $config['find_in_namespaces'] === true) {
4✔
498
            foreach ($locator->getFiles('Commands') as $file) {
4✔
499
                static::addCommand($file, $service, $locator);
4✔
500
            }
501
        }
502
        if (isset($config['directories'])) {
4✔
503
            foreach ($config['directories'] as $dir) {
4✔
504
                foreach ((array) $locator->listFiles($dir) as $file) {
4✔
505
                    static::addCommand($file, $service, $locator);
4✔
506
                }
507
            }
508
        }
509
        return static::setService('console', $service, $instance);
4✔
510
    }
511

512
    /**
513
     * Detects if the file has a command and adds it to the console.
514
     *
515
     * @param string $file The file to get the command class
516
     * @param Console $console The console to add the class
517
     * @param Locator $locator The locator to get the class name in the file
518
     *
519
     * @throws ReflectionException
520
     *
521
     * @return bool True if the command was added. If not, it's false.
522
     */
523
    protected static function addCommand(string $file, Console $console, Locator $locator) : bool
524
    {
525
        $className = $locator->getClassName($file);
4✔
526
        if ($className === null) {
4✔
527
            return false;
4✔
528
        }
529
        if (!\class_exists($className)) {
4✔
530
            Isolation::require($file);
4✔
531
        }
532
        $class = new ReflectionClass($className); // @phpstan-ignore-line
4✔
533
        if ($class->isInstantiable() && $class->isSubclassOf(Command::class)) {
4✔
534
            $console->addCommand($className); // @phpstan-ignore-line
4✔
535
            return true;
4✔
536
        }
537
        return false;
4✔
538
    }
539

540
    /**
541
     * Get a debugger service.
542
     *
543
     * @param string $instance The debugger instance name
544
     *
545
     * @return Debugger
546
     */
547
    public static function debugger(string $instance = 'default') : Debugger
548
    {
549
        $service = static::getService('debugger', $instance);
12✔
550
        if ($service) {
12✔
551
            return $service; // @phpstan-ignore-line
12✔
552
        }
553
        if (static::isDebugging()) {
12✔
554
            $start = \microtime(true);
11✔
555
            $service = static::setDebugger($instance);
11✔
556
            $end = \microtime(true);
11✔
557
            static::addDebugData('debugger', $instance, $start, $end);
11✔
558
            return $service;
11✔
559
        }
560
        return static::setDebugger($instance);
1✔
561
    }
562

563
    /**
564
     * Set a debugger service.
565
     *
566
     * @param string $instance The debugger instance name
567
     *
568
     * @return Debugger
569
     */
570
    protected static function setDebugger(string $instance) : Debugger
571
    {
572
        $config = static::config()->get('debugger', $instance);
12✔
573
        $service = new Debugger();
12✔
574
        if (isset($config['debugbar_view'])) {
12✔
575
            $service->setDebugbarView($config['debugbar_view']);
3✔
576
        }
577
        if (isset($config['options'])) {
12✔
578
            $service->setOptions($config['options']);
3✔
579
        }
580
        return static::setService('debugger', $service, $instance);
12✔
581
    }
582

583
    /**
584
     * Get a exceptionHandler service.
585
     *
586
     * @param string $instance The exceptionHandler instance name
587
     *
588
     * @return ExceptionHandler
589
     */
590
    public static function exceptionHandler(string $instance = 'default') : ExceptionHandler
591
    {
592
        $service = static::getService('exceptionHandler', $instance);
11✔
593
        if ($service) {
11✔
594
            return $service; // @phpstan-ignore-line
1✔
595
        }
596
        if (static::isDebugging()) {
11✔
597
            $start = \microtime(true);
3✔
598
            $service = static::setExceptionHandler($instance);
3✔
599
            $end = \microtime(true);
3✔
600
            static::addDebugData('exceptionHandler', $instance, $start, $end);
3✔
601
            return $service;
3✔
602
        }
603
        return static::setExceptionHandler($instance);
8✔
604
    }
605

606
    /**
607
     * Set a exceptionHandler service.
608
     *
609
     * @param string $instance The exceptionHandler instance name
610
     *
611
     * @return ExceptionHandler
612
     */
613
    protected static function setExceptionHandler(string $instance) : ExceptionHandler
614
    {
615
        $config = static::config()->get('exceptionHandler', $instance);
11✔
616
        $environment = $config['environment'] ?? ExceptionHandler::PRODUCTION;
11✔
617
        $logger = null;
11✔
618
        if (isset($config['logger_instance'])) {
11✔
619
            $logger = static::logger($config['logger_instance']);
8✔
620
        }
621
        $language = null;
11✔
622
        if (isset($config['language_instance'])) {
11✔
623
            $language = static::language($config['language_instance']);
8✔
624
        }
625
        $service = new ExceptionHandler($environment, $logger, $language);
11✔
626
        if (isset($config['development_view'])) {
11✔
627
            $service->setDevelopmentView($config['development_view']);
8✔
628
        }
629
        if (isset($config['production_view'])) {
11✔
630
            $service->setProductionView($config['production_view']);
8✔
631
        }
632
        $config['initialize'] ??= true;
11✔
633
        if ($config['initialize'] === true) {
11✔
634
            $service->initialize($config['handle_errors'] ?? true);
11✔
635
        }
636
        if (isset($config['search_engine'])) {
11✔
637
            $service->getSearchEngines()->setCurrent($config['search_engine']);
8✔
638
        }
639
        if (isset($config['show_log_id'])) {
11✔
640
            $service->setShowLogId($config['show_log_id']);
8✔
641
        }
642
        if (isset($config['json_flags'])) {
11✔
643
            $service->setJsonFlags($config['json_flags']);
8✔
644
        }
645
        if (isset($config['hidden_inputs']) && $config['hidden_inputs'] !== []) {
11✔
646
            $service->setHiddenInputs(...$config['hidden_inputs']);
8✔
647
        }
648
        return static::setService('exceptionHandler', $service, $instance);
11✔
649
    }
650

651
    /**
652
     * Get a antiCsrf service.
653
     *
654
     * @param string $instance The antiCsrf instance name
655
     *
656
     * @return AntiCSRF
657
     */
658
    public static function antiCsrf(string $instance = 'default') : AntiCSRF
659
    {
660
        $service = static::getService('antiCsrf', $instance);
2✔
661
        if ($service) {
2✔
662
            return $service; // @phpstan-ignore-line
1✔
663
        }
664
        if (static::isDebugging()) {
2✔
665
            $start = \microtime(true);
1✔
666
            $service = static::setAntiCsrf($instance);
1✔
667
            $end = \microtime(true);
1✔
668
            static::addDebugData('antiCsrf', $instance, $start, $end);
1✔
669
            return $service;
1✔
670
        }
671
        return static::setAntiCsrf($instance);
1✔
672
    }
673

674
    /**
675
     * Set a antiCsrf service.
676
     *
677
     * @param string $instance The antiCsrf instance name
678
     *
679
     * @return AntiCSRF
680
     */
681
    protected static function setAntiCsrf(string $instance) : AntiCSRF
682
    {
683
        $config = static::config()->get('antiCsrf', $instance);
2✔
684
        static::session($config['session_instance'] ?? 'default');
2✔
685
        $service = new AntiCSRF(
2✔
686
            static::request($config['request_instance'] ?? 'default'),
2✔
687
            $config['token_bytes_length'] ?? null,
2✔
688
            $config['generate_token_function'] ?? null,
2✔
689
        );
2✔
690
        if (isset($config['token_name'])) {
2✔
691
            $service->setTokenName($config['token_name']);
2✔
692
        }
693
        if (isset($config['enabled']) && $config['enabled'] === false) {
2✔
694
            $service->disable();
2✔
695
        }
696
        return static::setService('antiCsrf', $service, $instance);
2✔
697
    }
698

699
    /**
700
     * Get a database service.
701
     *
702
     * @param string $instance The database instance name
703
     *
704
     * @return Database
705
     */
706
    public static function database(string $instance = 'default') : Database
707
    {
708
        $service = static::getService('database', $instance);
83✔
709
        if ($service) {
83✔
710
            return $service; // @phpstan-ignore-line
83✔
711
        }
712
        if (static::isDebugging()) {
83✔
713
            $collector = new DatabaseCollector($instance);
1✔
714
            $start = \microtime(true);
1✔
715
            $service = static::setDatabase($instance, $collector);
1✔
716
            $end = \microtime(true);
1✔
717
            $collection = static::debugger()->getCollection('Database')
1✔
718
                ?? new DatabaseCollection('Database');
1✔
719
            $collection->addCollector($collector);
1✔
720
            static::debugger()->addCollection($collection);
1✔
721
            static::addDebugData('database', $instance, $start, $end);
1✔
722
            return $service;
1✔
723
        }
724
        return static::setDatabase($instance);
82✔
725
    }
726

727
    /**
728
     * Set a database service.
729
     *
730
     * @param string $instance The database instance name
731
     *
732
     * @return Database
733
     */
734
    protected static function setDatabase(
735
        string $instance,
736
        ?DatabaseCollector $collector = null
737
    ) : Database {
738
        $config = static::config()->get('database', $instance);
83✔
739
        $logger = null;
83✔
740
        if (isset($config['logger_instance'])) {
83✔
741
            $logger = static::logger($config['logger_instance']);
83✔
742
        }
743
        return static::setService(
83✔
744
            'database',
83✔
745
            new Database(
83✔
746
                $config['config'],
83✔
747
                logger: $logger,
83✔
748
                collector: $collector
83✔
749
            ),
83✔
750
            $instance
83✔
751
        );
83✔
752
    }
753

754
    /**
755
     * Get a mailer service.
756
     *
757
     * @param string $instance The mailer instance name
758
     *
759
     * @return Mailer
760
     */
761
    public static function mailer(string $instance = 'default') : Mailer
762
    {
763
        $service = static::getService('mailer', $instance);
2✔
764
        if ($service) {
2✔
765
            return $service; // @phpstan-ignore-line
1✔
766
        }
767
        if (static::isDebugging()) {
2✔
768
            $start = \microtime(true);
1✔
769
            $service = static::setMailer($instance);
1✔
770
            $end = \microtime(true);
1✔
771
            $collector = new EmailCollector($instance);
1✔
772
            $service->setDebugCollector($collector);
1✔
773
            $collection = static::debugger()->getCollection('Email')
1✔
774
                ?? new EmailCollection('Email');
1✔
775
            $collection->addCollector($collector);
1✔
776
            static::debugger()->addCollection($collection);
1✔
777
            static::addDebugData('mailer', $instance, $start, $end);
1✔
778
            return $service;
1✔
779
        }
780
        return static::setMailer($instance);
1✔
781
    }
782

783
    /**
784
     * Set a mailer service.
785
     *
786
     * @param string $instance The mailer instance name
787
     *
788
     * @return Mailer
789
     */
790
    protected static function setMailer(string $instance) : Mailer
791
    {
792
        $config = static::config()->get('mailer', $instance);
2✔
793
        return static::setService(
2✔
794
            'mailer',
2✔
795
            new Mailer($config),
2✔
796
            $instance
2✔
797
        );
2✔
798
    }
799

800
    /**
801
     * Get a migrator service.
802
     *
803
     * @param string $instance The migrator instance name
804
     *
805
     * @return Migrator
806
     */
807
    public static function migrator(string $instance = 'default') : Migrator
808
    {
809
        $service = static::getService('migrator', $instance);
2✔
810
        if ($service) {
2✔
811
            return $service; // @phpstan-ignore-line
1✔
812
        }
813
        if (static::isDebugging()) {
2✔
814
            $start = \microtime(true);
1✔
815
            $service = static::setMigrator($instance);
1✔
816
            $end = \microtime(true);
1✔
817
            static::addDebugData('migrator', $instance, $start, $end);
1✔
818
            return $service;
1✔
819
        }
820
        return static::setMigrator($instance);
1✔
821
    }
822

823
    /**
824
     * Set a migrator service.
825
     *
826
     * @param string $instance The migrator instance name
827
     *
828
     * @return Migrator
829
     */
830
    protected static function setMigrator(string $instance) : Migrator
831
    {
832
        $config = static::config()->get('migrator', $instance);
2✔
833
        return static::setService(
2✔
834
            'migrator',
2✔
835
            new Migrator(
2✔
836
                static::database($config['database_instance'] ?? 'default'),
2✔
837
                $config['directories'],
2✔
838
                $config['table'] ?? 'Migrations',
2✔
839
            ),
2✔
840
            $instance
2✔
841
        );
2✔
842
    }
843

844
    /**
845
     * Get a language service.
846
     *
847
     * @param string $instance The language instance name
848
     *
849
     * @return Language
850
     */
851
    public static function language(string $instance = 'default') : Language
852
    {
853
        $service = static::getService('language', $instance);
68✔
854
        if ($service) {
68✔
855
            return $service; // @phpstan-ignore-line
15✔
856
        }
857
        if (static::isDebugging()) {
68✔
858
            $start = \microtime(true);
4✔
859
            $service = static::setLanguage($instance);
4✔
860
            $end = \microtime(true);
4✔
861
            $collector = new LanguageCollector($instance);
4✔
862
            $service->setDebugCollector($collector);
4✔
863
            $collection = static::debugger()->getCollection('Language')
4✔
864
                ?? new LanguageCollection('Language');
4✔
865
            $collection->addCollector($collector);
4✔
866
            static::debugger()->addCollection($collection);
4✔
867
            static::addDebugData('language', $instance, $start, $end);
4✔
868
            return $service;
4✔
869
        }
870
        return static::setLanguage($instance);
64✔
871
    }
872

873
    /**
874
     * Set a language service.
875
     *
876
     * @param string $instance The language instance name
877
     *
878
     * @return Language
879
     */
880
    protected static function setLanguage(string $instance) : Language
881
    {
882
        $config = static::config()->get('language', $instance);
68✔
883
        $service = new Language($config['default'] ?? 'en');
68✔
884
        if (isset($config['current'])) {
68✔
885
            $service->setCurrentLocale($config['current']);
66✔
886
        }
887
        if (isset($config['supported'])) {
68✔
888
            $service->setSupportedLocales($config['supported']);
66✔
889
        }
890
        if (isset($config['negotiate']) && $config['negotiate'] === true) {
68✔
891
            $service->setCurrentLocale(
66✔
892
                static::negotiateLanguage($service, $config['request_instance'] ?? 'default')
66✔
893
            );
66✔
894
        }
895
        if (isset($config['fallback_level'])) {
68✔
896
            if (\is_int($config['fallback_level'])) {
66✔
897
                $config['fallback_level'] = FallbackLevel::from($config['fallback_level']);
66✔
898
            }
899
            $service->setFallbackLevel($config['fallback_level']);
66✔
900
        }
901
        $config['directories'] ??= [];
68✔
902
        if (isset($config['find_in_namespaces']) && $config['find_in_namespaces'] === true) {
68✔
903
            foreach (static::autoloader($config['autoloader_instance'] ?? 'default')
66✔
904
                ->getNamespaces() as $directories) {
66✔
905
                foreach ($directories as $directory) {
66✔
906
                    $directory .= 'Languages';
66✔
907
                    if (\is_dir($directory)) {
66✔
908
                        $config['directories'][] = $directory;
66✔
909
                    }
910
                }
911
            }
912
        }
913
        if ($config['directories']) {
68✔
914
            $service->setDirectories($config['directories']);
66✔
915
        }
916
        $service->addDirectory(__DIR__ . '/Languages');
68✔
917
        return static::setService('language', $service, $instance);
68✔
918
    }
919

920
    /**
921
     * Negotiates the language either via the command line or over HTTP.
922
     *
923
     * @param Language $language The current Language instance
924
     * @param string $requestInstance The name of the Request instance to be used
925
     *
926
     * @return string The negotiated language
927
     */
928
    protected static function negotiateLanguage(Language $language, string $requestInstance = 'default') : string
929
    {
930
        if (static::isCli()) {
67✔
931
            $supported = \array_map('\strtolower', $language->getSupportedLocales());
61✔
932
            $lang = \getenv('LANG');
61✔
933
            if ($lang) {
61✔
934
                $lang = \explode('.', $lang, 2);
61✔
935
                $lang = \strtolower($lang[0]);
61✔
936
                $lang = \strtr($lang, ['_' => '-']);
61✔
937
                if (\in_array($lang, $supported, true)) {
61✔
938
                    return $lang;
1✔
939
                }
940
            }
941
            return $language->getDefaultLocale();
61✔
942
        }
943
        return static::request($requestInstance)->negotiateLanguage(
7✔
944
            $language->getSupportedLocales()
7✔
945
        );
7✔
946
    }
947

948
    /**
949
     * Get a locator service.
950
     *
951
     * @param string $instance The locator instance name
952
     *
953
     * @return Locator
954
     */
955
    public static function locator(string $instance = 'default') : Locator
956
    {
957
        $service = static::getService('locator', $instance);
6✔
958
        if ($service) {
6✔
959
            return $service; // @phpstan-ignore-line
4✔
960
        }
961
        if (static::isDebugging()) {
6✔
962
            $start = \microtime(true);
1✔
963
            $service = static::setLocator($instance);
1✔
964
            $end = \microtime(true);
1✔
965
            static::addDebugData('locator', $instance, $start, $end);
1✔
966
            return $service;
1✔
967
        }
968
        return static::setLocator($instance);
5✔
969
    }
970

971
    /**
972
     * Set a locator service.
973
     *
974
     * @param string $instance The locator instance name
975
     *
976
     * @return Locator
977
     */
978
    protected static function setLocator(string $instance) : Locator
979
    {
980
        $config = static::config()->get('locator', $instance);
6✔
981
        return static::setService(
6✔
982
            'locator',
6✔
983
            new Locator(static::autoloader($config['autoloader_instance'] ?? 'default')),
6✔
984
            $instance
6✔
985
        );
6✔
986
    }
987

988
    /**
989
     * Get a logger service.
990
     *
991
     * @param string $instance The logger instance name
992
     *
993
     * @return Logger
994
     */
995
    public static function logger(string $instance = 'default') : Logger
996
    {
997
        $service = static::getService('logger', $instance);
90✔
998
        if ($service) {
90✔
999
            return $service; // @phpstan-ignore-line
14✔
1000
        }
1001
        if (static::isDebugging()) {
90✔
1002
            $start = \microtime(true);
1✔
1003
            $service = static::setLogger($instance);
1✔
1004
            $end = \microtime(true);
1✔
1005
            $collector = new LogCollector($instance);
1✔
1006
            $service->setDebugCollector($collector);
1✔
1007
            $collection = static::debugger()->getCollection('Log')
1✔
1008
                ?? new LogCollection('Log');
1✔
1009
            $collection->addCollector($collector);
1✔
1010
            static::debugger()->addCollection($collection);
1✔
1011
            static::addDebugData('logger', $instance, $start, $end);
1✔
1012
            return $service;
1✔
1013
        }
1014
        return static::setLogger($instance);
89✔
1015
    }
1016

1017
    /**
1018
     * Set a logger service.
1019
     *
1020
     * @param string $instance The logger instance name
1021
     *
1022
     * @return Logger
1023
     */
1024
    protected static function setLogger(string $instance) : Logger
1025
    {
1026
        $config = static::config()->get('logger', $instance);
90✔
1027
        /**
1028
         * @var class-string<Logger> $class
1029
         */
1030
        $class = $config['class'] ?? MultiFileLogger::class;
90✔
1031
        $config['level'] ??= LogLevel::DEBUG;
90✔
1032
        if (\is_int($config['level'])) {
90✔
1033
            $config['level'] = LogLevel::from($config['level']);
90✔
1034
        }
1035
        return static::setService(
90✔
1036
            'logger',
90✔
1037
            new $class(
90✔
1038
                $config['destination'],
90✔
1039
                $config['level'],
90✔
1040
                $config['config'] ?? [],
90✔
1041
            ),
90✔
1042
            $instance
90✔
1043
        );
90✔
1044
    }
1045

1046
    /**
1047
     * Get a router service.
1048
     *
1049
     * @param string $instance The router instance name
1050
     *
1051
     * @return Router
1052
     */
1053
    public static function router(string $instance = 'default') : Router
1054
    {
1055
        $service = static::getService('router', $instance);
9✔
1056
        if ($service) {
9✔
1057
            return $service; // @phpstan-ignore-line
8✔
1058
        }
1059
        if (static::isDebugging()) {
9✔
1060
            $start = \microtime(true);
3✔
1061
            $config = (array) static::config()->get('router', $instance);
3✔
1062
            $service = static::setRouter($instance, $config);
3✔
1063
            $collector = new RoutingCollector($instance);
3✔
1064
            $service->setDebugCollector($collector);
3✔
1065
            if (isset($config['files'])) {
3✔
1066
                static::requireRouterFiles($config['files'], $service);
2✔
1067
            }
1068
            $end = \microtime(true);
3✔
1069
            $collection = static::debugger()->getCollection('Routing')
3✔
1070
                ?? new RoutingCollection('Routing');
3✔
1071
            $collection->addCollector($collector);
3✔
1072
            static::debugger()->addCollection($collection);
3✔
1073
            static::addDebugData('router', $instance, $start, $end);
3✔
1074
            return $service;
3✔
1075
        }
1076
        return static::setRouter($instance);
6✔
1077
    }
1078

1079
    /**
1080
     * Set a router service.
1081
     *
1082
     * @param string $instance The router instance name
1083
     * @param array<mixed>|null $config The router instance configs or null
1084
     *
1085
     * @return Router
1086
     */
1087
    protected static function setRouter(string $instance, ?array $config = null) : Router
1088
    {
1089
        $requireFiles = $config === null;
9✔
1090
        $config ??= static::config()->get('router', $instance);
9✔
1091
        $language = null;
9✔
1092
        if (isset($config['language_instance'])) {
9✔
1093
            $language = static::language($config['language_instance']);
7✔
1094
        }
1095
        $service = static::setService('router', new Router(
9✔
1096
            static::response($config['response_instance'] ?? 'default'),
9✔
1097
            $language
9✔
1098
        ), $instance);
9✔
1099
        if (isset($config['auto_options']) && $config['auto_options'] === true) {
9✔
1100
            $service->setAutoOptions();
7✔
1101
        }
1102
        if (isset($config['auto_methods']) && $config['auto_methods'] === true) {
9✔
1103
            $service->setAutoMethods();
7✔
1104
        }
1105
        if (!empty($config['placeholders'])) {
9✔
1106
            $service->addPlaceholder($config['placeholders']);
7✔
1107
        }
1108
        if ($requireFiles && isset($config['files'])) {
9✔
1109
            static::requireRouterFiles($config['files'], $service);
6✔
1110
        }
1111
        if (isset($config['callback'])) {
8✔
1112
            $config['callback']($service);
7✔
1113
        }
1114
        return $service;
8✔
1115
    }
1116

1117
    /**
1118
     * Load files that set the routes.
1119
     *
1120
     * @param array<string> $files The path of the router files
1121
     * @param Router $router
1122
     */
1123
    protected static function requireRouterFiles(array $files, Router $router) : void
1124
    {
1125
        foreach ($files as $file) {
8✔
1126
            if (!\is_file($file)) {
8✔
1127
                throw new LogicException('Invalid router file: ' . $file);
1✔
1128
            }
1129
            Isolation::require($file, ['router' => $router]);
7✔
1130
        }
1131
    }
1132

1133
    /**
1134
     * Get a request service.
1135
     *
1136
     * @param string $instance The request instance name
1137
     *
1138
     * @return Request
1139
     */
1140
    public static function request(string $instance = 'default') : Request
1141
    {
1142
        $service = static::getService('request', $instance);
14✔
1143
        if ($service) {
14✔
1144
            return $service; // @phpstan-ignore-line
9✔
1145
        }
1146
        if (static::isDebugging()) {
14✔
1147
            $start = \microtime(true);
3✔
1148
            $service = static::setRequest($instance);
3✔
1149
            $end = \microtime(true);
3✔
1150
            $collector = new HTTPCollector($instance);
3✔
1151
            $collector->setRequest($service);
3✔
1152
            $collection = static::debugger()->getCollection('HTTP')
3✔
1153
                ?? new HTTPCollection('HTTP');
3✔
1154
            $collection->addCollector($collector);
3✔
1155
            static::debugger()->addCollection($collection);
3✔
1156
            static::addDebugData('request', $instance, $start, $end);
3✔
1157
            return $service;
3✔
1158
        }
1159
        return static::setRequest($instance);
11✔
1160
    }
1161

1162
    /**
1163
     * Overrides variables to be set in the $_SERVER super-global when the
1164
     * request is made via the command line.
1165
     *
1166
     * @param array<string,mixed> $vars
1167
     */
1168
    protected static function setServerVars(array $vars = []) : void
1169
    {
1170
        $vars = \array_replace(static::$defaultServerVars, $vars);
14✔
1171
        foreach ($vars as $key => $value) {
14✔
1172
            $_SERVER[$key] ??= $value;
14✔
1173
        }
1174
    }
1175

1176
    /**
1177
     * Set a request service.
1178
     *
1179
     * @param string $instance The request instance name
1180
     *
1181
     * @return Request
1182
     */
1183
    protected static function setRequest(string $instance) : Request
1184
    {
1185
        $config = static::config()->get('request', $instance);
14✔
1186
        if (static::isCli()) {
14✔
1187
            static::setServerVars($config['server_vars'] ?? []);
7✔
1188
        }
1189
        $service = new Request($config['allowed_hosts'] ?? []);
14✔
1190
        if (isset($config['force_https']) && $config['force_https'] === true) {
14✔
1191
            $service->forceHttps();
1✔
1192
        }
1193
        if (isset($config['json_flags'])) {
14✔
1194
            $service->setJsonFlags($config['json_flags']);
12✔
1195
        }
1196
        return static::setService('request', $service, $instance);
14✔
1197
    }
1198

1199
    /**
1200
     * Get a response service.
1201
     *
1202
     * @param string $instance The response instance name
1203
     *
1204
     * @return Response
1205
     */
1206
    public static function response(string $instance = 'default') : Response
1207
    {
1208
        $service = static::getService('response', $instance);
11✔
1209
        if ($service) {
11✔
1210
            return $service; // @phpstan-ignore-line
4✔
1211
        }
1212
        if (static::isDebugging()) {
11✔
1213
            $start = \microtime(true);
3✔
1214
            $service = static::setResponse($instance);
3✔
1215
            $end = \microtime(true);
3✔
1216
            $collection = static::debugger()->getCollection('HTTP');
3✔
1217
            foreach ($collection->getCollectors() as $collector) {
3✔
1218
                if ($collector->getName() === $instance) {
3✔
1219
                    $service->setDebugCollector($collector); // @phpstan-ignore-line
3✔
1220
                    break;
3✔
1221
                }
1222
            }
1223
            static::addDebugData('response', $instance, $start, $end);
3✔
1224
            return $service;
3✔
1225
        }
1226
        return static::setResponse($instance);
8✔
1227
    }
1228

1229
    /**
1230
     * Set a response service.
1231
     *
1232
     * @param string $instance The response instance name
1233
     *
1234
     * @return Response
1235
     */
1236
    protected static function setResponse(string $instance) : Response
1237
    {
1238
        $config = static::config()->get('response', $instance);
11✔
1239
        $service = new Response(static::request($config['request_instance'] ?? 'default'));
11✔
1240
        if (!empty($config['headers'])) {
11✔
1241
            $service->setHeaders($config['headers']);
9✔
1242
        }
1243
        if (!empty($config['auto_etag'])) {
11✔
1244
            $service->setAutoEtag(
9✔
1245
                $config['auto_etag']['active'] ?? true,
9✔
1246
                $config['auto_etag']['hash_algo'] ?? null
9✔
1247
            );
9✔
1248
        }
1249
        if (isset($config['auto_language']) && $config['auto_language'] === true) {
11✔
1250
            $service->setContentLanguage(
9✔
1251
                static::language($config['language_instance'] ?? 'default')->getCurrentLocale()
9✔
1252
            );
9✔
1253
        }
1254
        if (isset($config['cache'])) {
11✔
1255
            $config['cache'] === false
10✔
1256
                ? $service->setNoCache()
1✔
1257
                : $service->setCache($config['cache']['seconds'], $config['cache']['public'] ?? false);
9✔
1258
        }
1259
        if (!empty($config['csp'])) {
11✔
1260
            $service->setCsp(new CSP($config['csp']));
9✔
1261
        }
1262
        if (!empty($config['csp_report_only'])) {
11✔
1263
            $service->setCspReportOnly(new CSP($config['csp_report_only']));
9✔
1264
        }
1265
        if (isset($config['json_flags'])) {
11✔
1266
            $service->setJsonFlags($config['json_flags']);
9✔
1267
        }
1268
        if (isset($config['replace_headers'])) {
11✔
1269
            $service->setReplaceHeaders($config['replace_headers']);
9✔
1270
        }
1271
        return static::setService('response', $service, $instance);
11✔
1272
    }
1273

1274
    /**
1275
     * Get a session service.
1276
     *
1277
     * @param string $instance The session instance name
1278
     *
1279
     * @return Session
1280
     */
1281
    public static function session(string $instance = 'default') : Session
1282
    {
1283
        $service = static::getService('session', $instance);
5✔
1284
        if ($service) {
5✔
1285
            return $service; // @phpstan-ignore-line
5✔
1286
        }
1287
        if (static::isDebugging()) {
5✔
1288
            $start = \microtime(true);
1✔
1289
            $service = static::setSession($instance);
1✔
1290
            $end = \microtime(true);
1✔
1291
            $collector = new SessionCollector($instance);
1✔
1292
            $service->setDebugCollector($collector);
1✔
1293
            $collection = static::debugger()->getCollection('Session')
1✔
1294
                ?? new SessionCollection('Session');
1✔
1295
            $collection->addCollector($collector);
1✔
1296
            static::debugger()->addCollection($collection);
1✔
1297
            static::addDebugData('session', $instance, $start, $end);
1✔
1298
            return $service;
1✔
1299
        }
1300
        return static::setSession($instance);
4✔
1301
    }
1302

1303
    /**
1304
     * Set a session service.
1305
     *
1306
     * @param string $instance The session instance name
1307
     *
1308
     * @return Session
1309
     */
1310
    protected static function setSession(string $instance) : Session
1311
    {
1312
        $config = static::config()->get('session', $instance);
5✔
1313
        if (isset($config['save_handler']['class'])) {
5✔
1314
            $logger = null;
3✔
1315
            if (isset($config['logger_instance'])) {
3✔
1316
                $logger = static::logger($config['logger_instance']);
1✔
1317
            }
1318
            $saveHandler = new $config['save_handler']['class'](
3✔
1319
                $config['save_handler']['config'] ?? [],
3✔
1320
                $logger
3✔
1321
            );
3✔
1322
            if ($saveHandler instanceof DatabaseHandler
3✔
1323
                && isset($config['save_handler']['database_instance'])
3✔
1324
            ) {
1325
                $saveHandler->setDatabase(
1✔
1326
                    static::database($config['save_handler']['database_instance'])
1✔
1327
                );
1✔
1328
            }
1329
        }
1330
        // @phpstan-ignore-next-line
1331
        $service = new Session($config['options'] ?? [], $saveHandler ?? null);
5✔
1332
        if (isset($config['auto_start']) && $config['auto_start'] === true) {
5✔
1333
            $service->start();
2✔
1334
        }
1335
        return static::setService('session', $service, $instance);
5✔
1336
    }
1337

1338
    /**
1339
     * Get a validation service.
1340
     *
1341
     * @param string $instance The validation instance name
1342
     *
1343
     * @return Validation
1344
     */
1345
    public static function validation(string $instance = 'default') : Validation
1346
    {
1347
        $service = static::getService('validation', $instance);
21✔
1348
        if ($service) {
21✔
1349
            return $service; // @phpstan-ignore-line
2✔
1350
        }
1351
        if (static::isDebugging()) {
21✔
1352
            $start = \microtime(true);
2✔
1353
            $service = static::setValidation($instance);
2✔
1354
            $end = \microtime(true);
2✔
1355
            $collector = new ValidationCollector($instance);
2✔
1356
            $service->setDebugCollector($collector);
2✔
1357
            $collection = static::debugger()->getCollection('Validation')
2✔
1358
                ?? new ValidationCollection('Validation');
2✔
1359
            $collection->addCollector($collector);
2✔
1360
            static::debugger()->addCollection($collection);
2✔
1361
            static::addDebugData('validation', $instance, $start, $end);
2✔
1362
            return $service;
2✔
1363
        }
1364
        return static::setValidation($instance);
19✔
1365
    }
1366

1367
    /**
1368
     * Set a validation service.
1369
     *
1370
     * @param string $instance The validation instance name
1371
     *
1372
     * @return Validation
1373
     */
1374
    protected static function setValidation(string $instance) : Validation
1375
    {
1376
        $config = static::config()->get('validation', $instance);
21✔
1377
        $language = null;
21✔
1378
        if (isset($config['language_instance'])) {
21✔
1379
            $language = static::language($config['language_instance']);
20✔
1380
        }
1381
        return static::setService(
21✔
1382
            'validation',
21✔
1383
            new Validation(
21✔
1384
                $config['validators'] ?? [
21✔
1385
                Validator::class,
21✔
1386
                FilesValidator::class,
21✔
1387
            ],
1388
                $language
21✔
1389
            ),
21✔
1390
            $instance
21✔
1391
        );
21✔
1392
    }
1393

1394
    /**
1395
     * Get a view service.
1396
     *
1397
     * @param string $instance The view instance name
1398
     *
1399
     * @return View
1400
     */
1401
    public static function view(string $instance = 'default') : View
1402
    {
1403
        $service = static::getService('view', $instance);
3✔
1404
        if ($service) {
3✔
1405
            return $service; // @phpstan-ignore-line
1✔
1406
        }
1407
        if (static::isDebugging()) {
3✔
1408
            $start = \microtime(true);
1✔
1409
            $service = static::setView($instance);
1✔
1410
            $service->setInstanceName($instance);
1✔
1411
            $end = \microtime(true);
1✔
1412
            $collector = new ViewsCollector($instance);
1✔
1413
            $service->setDebugCollector($collector);
1✔
1414
            $collection = static::debugger()->getCollection('Views')
1✔
1415
                ?? new ViewsCollection('Views');
1✔
1416
            $collection->addCollector($collector);
1✔
1417
            static::debugger()->addCollection($collection);
1✔
1418
            static::addDebugData('view', $instance, $start, $end);
1✔
1419
            return $service;
1✔
1420
        }
1421
        return static::setView($instance);
2✔
1422
    }
1423

1424
    /**
1425
     * Set a view service.
1426
     *
1427
     * @param string $instance The view instance name
1428
     *
1429
     * @return View
1430
     */
1431
    protected static function setView(string $instance) : View
1432
    {
1433
        $config = static::config()->get('view', $instance);
3✔
1434
        $service = new View($config['base_dir'] ?? null, $config['extension'] ?? '.php');
3✔
1435
        if (isset($config['layout_prefix'])) {
3✔
1436
            $service->setLayoutPrefix($config['layout_prefix']);
3✔
1437
        }
1438
        if (isset($config['include_prefix'])) {
3✔
1439
            $service->setIncludePrefix($config['include_prefix']);
3✔
1440
        }
1441
        if (isset($config['show_debug_comments']) && $config['show_debug_comments'] === false) {
3✔
1442
            $service->disableDebugComments();
3✔
1443
        }
1444
        if (isset($config['throw_exceptions_in_destructor'])) {
3✔
1445
            $service->setThrowExceptionsInDestructor($config['throw_exceptions_in_destructor']);
3✔
1446
        }
1447
        return static::setService('view', $service, $instance);
3✔
1448
    }
1449

1450
    /**
1451
     * Tell if it is a command-line request.
1452
     *
1453
     * @return bool
1454
     */
1455
    public static function isCli() : bool
1456
    {
1457
        if (static::$isCli === null) {
72✔
1458
            static::$isCli = \PHP_SAPI === 'cli' || \defined('STDIN');
65✔
1459
        }
1460
        return static::$isCli;
72✔
1461
    }
1462

1463
    /**
1464
     * Set if it is a CLI request. Used for testing.
1465
     *
1466
     * @param bool $is
1467
     */
1468
    public static function setIsCli(bool $is) : void
1469
    {
1470
        static::$isCli = $is;
9✔
1471
    }
1472

1473
    /**
1474
     * Tell if the App is in debug mode.
1475
     *
1476
     * @return bool
1477
     */
1478
    public static function isDebugging() : bool
1479
    {
1480
        return isset(static::$debugCollector);
109✔
1481
    }
1482

1483
    /**
1484
     * Add services data to the debug collector.
1485
     *
1486
     * @param string $service Service name
1487
     * @param string $instance Service instance name
1488
     * @param float $start Microtime right before setting up the service
1489
     * @param float $end Microtime right after setting up the service
1490
     */
1491
    public static function addDebugData(
1492
        string $service,
1493
        string $instance,
1494
        float $start,
1495
        float $end
1496
    ) : void {
1497
        static::$debugCollector->addData([
11✔
1498
            'service' => $service,
11✔
1499
            'instance' => $instance,
11✔
1500
            'start' => $start,
11✔
1501
            'end' => $end,
11✔
1502
        ]);
11✔
1503
    }
1504
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc