Test Failed
Pull Request — master (#11)
by Evgeniy
02:47
created

ApplicationRunner.php$0 ➔ handle()   A

Complexity

Conditions 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace App;
6
7
use Psr\Container\ContainerInterface;
8
use Psr\Http\Message\RequestInterface;
9
use Psr\Http\Message\ResponseInterface;
10
use Psr\Http\Message\ServerRequestInterface;
11
use Psr\Http\Server\RequestHandlerInterface;
12
use Psr\Log\NullLogger;
13
use Throwable;
14
use Yiisoft\Composer\Config\Builder;
15
use Yiisoft\Di\Container;
16
use Yiisoft\ErrorHandler\ErrorHandler;
17
use Yiisoft\ErrorHandler\Middleware\ErrorCatcher;
18
use Yiisoft\ErrorHandler\Renderer\JsonRenderer;
19
use Yiisoft\Files\FileHelper;
20
use Yiisoft\Http\Method;
21
use Yiisoft\Yii\Web\Application;
22
use Yiisoft\Yii\Web\SapiEmitter;
23
use Yiisoft\Yii\Web\ServerRequestFactory;
24
25
use function dirname;
26
use function microtime;
27
28
final class ApplicationRunner
29
{
30
    private bool $debug = false;
31
32
    public function debug(bool $enable = true): void
33
    {
34
        $this->debug = $enable;
35
    }
36
37
    public function run(): void
38
    {
39
        $startTime = microtime(true);
40
        // Register temporary error handler to catch error while container is building.
41
        $errorHandler = new ErrorHandler(new NullLogger(), new JsonRenderer());
42
        $this->registerErrorHandler($errorHandler);
43
44
        if ($this->debug && $this->shouldRebuildConfigs()) {
45
            Builder::rebuild();
46
        }
47
48
        $container = new Container(
49
            require Builder::path('web'),
50
            require Builder::path('providers')
51
        );
52
53
        // Register error handler with real container-configured dependencies.
54
        $this->registerErrorHandler($container->get(ErrorHandler::class), $errorHandler);
55
56
        $container = $container->get(ContainerInterface::class);
57
        $application = $container->get(Application::class);
58
59
        $request = $container->get(ServerRequestFactory::class)->createFromGlobals();
60
        $request = $request->withAttribute('applicationStartTime', $startTime);
61
62
        try {
63
            $application->start();
64
            $response = $application->handle($request);
65
            $this->emit($request, $response);
66
        } catch (Throwable $throwable) {
67
            $handler = $this->createThrowableHandler($throwable);
68
            $response = $container->get(ErrorCatcher::class)->process($request, $handler);
69
            $this->emit($request, $response);
70
        } finally {
71
            $application->afterEmit($response ?? null);
72
            $application->shutdown();
73
        }
74
    }
75
76
    private function emit(RequestInterface $request, ResponseInterface $response): void
77
    {
78
        (new SapiEmitter())->emit($response, $request->getMethod() === Method::HEAD);
79
    }
80
81
    private function createThrowableHandler(Throwable $throwable): RequestHandlerInterface
82
    {
83
        return new class($throwable) implements RequestHandlerInterface {
84
            private Throwable $throwable;
85
86
            public function __construct(Throwable $throwable)
87
            {
88
                $this->throwable = $throwable;
89
            }
90
91
            public function handle(ServerRequestInterface $request): ResponseInterface
92
            {
93
                throw $this->throwable;
94
            }
95
        };
96
    }
97
98
    private function registerErrorHandler(ErrorHandler $registered, ErrorHandler $unregistered = null): void
99
    {
100
        if ($unregistered !== null) {
101
            $unregistered->unregister();
102
        }
103
104
        if ($this->debug) {
105
            $registered->debug();
106
        }
107
108
        $registered->register();
109
    }
110
111
    private function shouldRebuildConfigs(): bool
112
    {
113
        $sourceDirectory = dirname(__DIR__) . '/config/';
114
        $buildDirectory = dirname(__DIR__) . '/runtime/build/config/';
115
116
        if (FileHelper::isEmptyDirectory($buildDirectory)) {
117
            return true;
118
        }
119
120
        $sourceTime = FileHelper::lastModifiedTime($sourceDirectory);
121
        $buildTime = FileHelper::lastModifiedTime($buildDirectory);
122
        return $buildTime < $sourceTime;
123
    }
124
}
125