Passed
Pull Request — master (#19)
by Evgeniy
11:02
created

RoadRunnerApplicationRunner::runBootstrap()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 2
b 0
f 0
nc 1
nop 2
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Runner\RoadRunner;
6
7
use ErrorException;
8
use JsonException;
9
use Psr\Container\ContainerExceptionInterface;
10
use Psr\Container\ContainerInterface;
11
use Psr\Container\NotFoundExceptionInterface;
12
use Psr\Http\Message\ResponseInterface;
13
use Psr\Http\Message\ServerRequestFactoryInterface;
14
use Psr\Http\Message\StreamFactoryInterface;
15
use Psr\Http\Message\UploadedFileFactoryInterface;
16
use Spiral\RoadRunner;
17
use Throwable;
18
use Yiisoft\Definitions\Exception\CircularReferenceException;
19
use Yiisoft\Definitions\Exception\InvalidConfigException;
20
use Yiisoft\Definitions\Exception\NotInstantiableException;
21
use Yiisoft\Di\Container;
22
use Yiisoft\Di\NotFoundException;
23
use Yiisoft\Di\StateResetter;
24
use Yiisoft\ErrorHandler\ErrorHandler;
25
use Yiisoft\ErrorHandler\Middleware\ErrorCatcher;
26
use Yiisoft\ErrorHandler\Renderer\PlainTextRenderer;
27
use Yiisoft\Log\Logger;
28
use Yiisoft\Log\Target\File\FileTarget;
29
use Yiisoft\Yii\Http\Application;
30
use Yiisoft\Yii\Http\Handler\ThrowableHandler;
31
use Yiisoft\Yii\Runner\ApplicationRunner;
0 ignored issues
show
Bug introduced by
The type Yiisoft\Yii\Runner\ApplicationRunner was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
32
33
use function gc_collect_cycles;
34
use function microtime;
35
36
/**
37
 * `RoadRunnerApplicationRunner` runs the Yii HTTP application for RoadRunner.
38
 */
39
final class RoadRunnerApplicationRunner extends ApplicationRunner
40
{
41
    private ?ErrorHandler $temporaryErrorHandler = null;
42
43
    /**
44
     * @param string $rootPath The absolute path to the project root.
45
     * @param bool $debug Whether the debug mode is enabled.
46
     * @param string|null $environment The environment name.
47
     */
48
    public function __construct(string $rootPath, bool $debug, ?string $environment)
49
    {
50
        parent::__construct($rootPath, $debug, $environment);
51
        $this->bootstrapGroup = 'bootstrap-web';
0 ignored issues
show
Bug Best Practice introduced by
The property bootstrapGroup does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
52
        $this->eventsGroup = 'events-web';
0 ignored issues
show
Bug Best Practice introduced by
The property eventsGroup does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
53
    }
54
55
    /**
56
     * Returns a new instance with the specified temporary error handler instance {@see ErrorHandler}.
57
     *
58
     * A temporary error handler is needed to handle the creation of configuration and container instances,
59
     * then the error handler configured in your application configuration will be used.
60
     *
61
     * @param ErrorHandler $temporaryErrorHandler The temporary error handler instance.
62
     *
63
     * @return self
64
     */
65
    public function withTemporaryErrorHandler(ErrorHandler $temporaryErrorHandler): self
66
    {
67
        $new = clone $this;
68
        $new->temporaryErrorHandler = $temporaryErrorHandler;
69
        return $new;
70
    }
71
72
    /**
73
     * {@inheritDoc}
74
     *
75
     * @throws CircularReferenceException|ErrorException|InvalidConfigException|JsonException
76
     * @throws ContainerExceptionInterface|NotFoundException|NotFoundExceptionInterface|NotInstantiableException
77
     */
78
    public function run(): void
79
    {
80
        // Register temporary error handler to catch error while container is building.
81
        $temporaryErrorHandler = $this->createTemporaryErrorHandler();
82
        $this->registerErrorHandler($temporaryErrorHandler);
83
84
        $config = $this->config ?? $this->createConfig();
85
        $container = $this->container ?? $this->createContainer($config, 'web');
86
87
        // Register error handler with real container-configured dependencies.
88
        /** @var ErrorHandler $actualErrorHandler */
89
        $actualErrorHandler = $container->get(ErrorHandler::class);
90
        $this->registerErrorHandler($actualErrorHandler, $temporaryErrorHandler);
91
92
        if ($container instanceof Container) {
93
            $container = $container->get(ContainerInterface::class);
94
        }
95
96
        $this->runBootstrap($config, $container);
97
        $this->checkEvents($config, $container);
98
99
        $worker = RoadRunner\Worker::create();
100
        /** @var ServerRequestFactoryInterface $serverRequestFactory */
101
        $serverRequestFactory = $container->get(ServerRequestFactoryInterface::class);
102
        /** @var StreamFactoryInterface $streamFactory */
103
        $streamFactory = $container->get(StreamFactoryInterface::class);
104
        /** @var UploadedFileFactoryInterface $uploadsFactory */
105
        $uploadsFactory = $container->get(UploadedFileFactoryInterface::class);
106
        $worker = new RoadRunner\Http\PSR7Worker($worker, $serverRequestFactory, $streamFactory, $uploadsFactory);
107
108
        /** @var Application $application */
109
        $application = $container->get(Application::class);
110
        $application->start();
111
112
        while ($request = $worker->waitRequest()) {
113
            $request = $request->withAttribute('applicationStartTime', microtime(true));
114
            $response = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $response is dead and can be removed.
Loading history...
115
            try {
116
                $response = $application->handle($request);
117
                $worker->respond($response);
118
            } catch (Throwable $t) {
119
                $handler = new ThrowableHandler($t);
120
                /**
121
                 * @var ResponseInterface
122
                 * @psalm-suppress MixedMethodCall
123
                 */
124
                $response = $container->get(ErrorCatcher::class)->process($request, $handler);
125
                $worker->respond($response);
126
            } finally {
127
                $application->afterEmit($response ?? null);
128
                /** @psalm-suppress MixedMethodCall */
129
                $container->get(StateResetter::class)->reset(); // We should reset the state of such services every request.
130
                gc_collect_cycles();
131
            }
132
        }
133
134
        $application->shutdown();
135
    }
136
137
    private function createTemporaryErrorHandler(): ErrorHandler
138
    {
139
        if ($this->temporaryErrorHandler !== null) {
140
            return $this->temporaryErrorHandler;
141
        }
142
143
        $logger = new Logger([new FileTarget("$this->rootPath/runtime/logs/app.log")]);
144
        return new ErrorHandler($logger, new PlainTextRenderer());
145
    }
146
147
    /**
148
     * @throws ErrorException
149
     */
150
    private function registerErrorHandler(ErrorHandler $registered, ErrorHandler $unregistered = null): void
151
    {
152
        $unregistered?->unregister();
153
154
        if ($this->debug) {
155
            $registered->debug();
156
        }
157
158
        $registered->register();
159
    }
160
}
161