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

aplus-framework / mvc / 16927505441

22 Jul 2025 07:08PM UTC coverage: 100.0%. Remained the same
16927505441

push

github

natanfelles
Add info about order in which commands are added to the console

1836 of 1836 relevant lines covered (100.0%)

10.61 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
        if (isset($config['commands'])) {
4✔
510
            foreach ($config['commands'] as $command) {
4✔
511
                $service->addCommand($command);
4✔
512
            }
513
        }
514
        return static::setService('console', $service, $instance);
4✔
515
    }
516

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1468
    /**
1469
     * Set if it is a CLI request. Used for testing.
1470
     *
1471
     * @param bool $is
1472
     */
1473
    public static function setIsCli(bool $is) : void
1474
    {
1475
        static::$isCli = $is;
9✔
1476
    }
1477

1478
    /**
1479
     * Tell if the App is in debug mode.
1480
     *
1481
     * @return bool
1482
     */
1483
    public static function isDebugging() : bool
1484
    {
1485
        return isset(static::$debugCollector);
109✔
1486
    }
1487

1488
    /**
1489
     * Add services data to the debug collector.
1490
     *
1491
     * @param string $service Service name
1492
     * @param string $instance Service instance name
1493
     * @param float $start Microtime right before setting up the service
1494
     * @param float $end Microtime right after setting up the service
1495
     */
1496
    public static function addDebugData(
1497
        string $service,
1498
        string $instance,
1499
        float $start,
1500
        float $end
1501
    ) : void {
1502
        static::$debugCollector->addData([
11✔
1503
            'service' => $service,
11✔
1504
            'instance' => $instance,
11✔
1505
            'start' => $start,
11✔
1506
            'end' => $end,
11✔
1507
        ]);
11✔
1508
    }
1509
}
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