Passed
Pull Request — master (#383)
by Wilmer
03:15
created

createTemporaryErrorHandler()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace App\Runner;
6
7
use App\Handler\ThrowableHandler;
8
use ErrorException;
9
use Psr\Container\ContainerInterface;
10
use Psr\Http\Message\RequestInterface;
11
use Psr\Http\Message\ResponseInterface;
12
use Psr\Http\Message\ServerRequestInterface;
13
use Throwable;
14
use Yiisoft\Di\Container;
15
use Yiisoft\ErrorHandler\ErrorHandler;
16
use Yiisoft\ErrorHandler\Middleware\ErrorCatcher;
17
use Yiisoft\ErrorHandler\Renderer\HtmlRenderer;
18
use Yiisoft\Definitions\Exception\CircularReferenceException;
19
use Yiisoft\Definitions\Exception\InvalidConfigException;
20
use Yiisoft\Definitions\Exception\NotFoundException;
21
use Yiisoft\Definitions\Exception\NotInstantiableException;
22
use Yiisoft\Http\Method;
23
use Yiisoft\Log\Logger;
24
use Yiisoft\Log\Target\File\FileTarget;
25
use Yiisoft\Yii\Event\ListenerConfigurationChecker;
26
use Yiisoft\Yii\Web\Application;
27
use Yiisoft\Yii\Web\Exception\HeadersHaveBeenSentException;
28
use Yiisoft\Yii\Web\SapiEmitter;
29
use Yiisoft\Yii\Web\ServerRequestFactory;
30
31
use function dirname;
32
use function microtime;
33
34
final class WebApplicationRunner
35
{
36
    private bool $debug;
37
    private ?string $environment;
38
39 10
    public function __construct(bool $debug, ?string $environment)
40
    {
41 10
        $this->debug = $debug;
42 10
        $this->environment = $environment;
43 10
    }
44
45
    /**
46
     * @throws CircularReferenceException|ErrorException|HeadersHaveBeenSentException|InvalidConfigException
47
     * @throws NotFoundException|NotInstantiableException|
48
     */
49 10
    public function run(): void
50
    {
51 10
        $startTime = microtime(true);
52
53
        // Register temporary error handler to catch error while container is building.
54 10
        $errorHandler = $this->createTemporaryErrorHandler();
55 10
        $this->registerErrorHandler($errorHandler);
56
57 10
        $config = ConfigFactory::create($this->environment);
58
59 10
        $container = new Container(
60 10
            $config->get('web'),
61 10
            $config->get('providers-web'),
62 10
            [],
63 10
            $this->debug,
64 10
            $config->get('delegates-web')
65
        );
66
67
        // Register error handler with real container-configured dependencies.
68 10
        $this->registerErrorHandler($container->get(ErrorHandler::class), $errorHandler);
69
70
        // Run bootstrap
71 10
        $this->runBootstrap($container, $config->get('bootstrap-web'));
72
73 10
        $container = $container->get(ContainerInterface::class);
74
75 10
        if ($this->debug) {
76
            /** @psalm-suppress MixedMethodCall */
77 10
            $container->get(ListenerConfigurationChecker::class)->check($config->get('events-web'));
78
        }
79
80
        /** @var Application */
81 10
        $application = $container->get(Application::class);
82
83
        /**
84
         * @var ServerRequestInterface
85
         * @psalm-suppress MixedMethodCall
86
         */
87 10
        $serverRequest = $container->get(ServerRequestFactory::class)->createFromGlobals();
88 10
        $request = $serverRequest->withAttribute('applicationStartTime', $startTime);
89
90
        try {
91 10
            $application->start();
92 10
            $response = $application->handle($request);
93 10
            $this->emit($request, $response);
94
        } catch (Throwable $throwable) {
95
            $handler = new ThrowableHandler($throwable);
96
            /**
97
             * @var ResponseInterface
98
             * @psalm-suppress MixedMethodCall
99
             */
100
            $response = $container->get(ErrorCatcher::class)->process($request, $handler);
101
            $this->emit($request, $response);
102 10
        } finally {
103 10
            $application->afterEmit($response ?? null);
104 10
            $application->shutdown();
105
        }
106 10
    }
107
108 10
    private function createTemporaryErrorHandler(): ErrorHandler
109
    {
110 10
        $logger = new Logger([new FileTarget(dirname(__DIR__) . '/runtime/logs/app.log')]);
111 10
        return new ErrorHandler($logger, new HtmlRenderer());
112
    }
113
114
    /**
115
     * @throws HeadersHaveBeenSentException
116
     */
117 10
    private function emit(RequestInterface $request, ResponseInterface $response): void
118
    {
119 10
        (new SapiEmitter())->emit($response, $request->getMethod() === Method::HEAD);
120 10
    }
121
122
    /**
123
     * @throws ErrorException
124
     */
125 10
    private function registerErrorHandler(ErrorHandler $registered, ErrorHandler $unregistered = null): void
126
    {
127 10
        if ($unregistered !== null) {
128 10
            $unregistered->unregister();
129
        }
130
131 10
        if ($this->debug) {
132 10
            $registered->debug();
133
        }
134
135 10
        $registered->register();
136 10
    }
137
138 10
    private function runBootstrap(Container $container, array $bootstrapList): void
139
    {
140 10
        (new BootstrapRunner($container, $bootstrapList))->run();
141 10
    }
142
}
143