Test Failed
Pull Request — master (#18)
by Evgeniy
01:56
created

HttpApplicationRunner::runBootstrap()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Runner\Http;
6
7
use ErrorException;
8
use Psr\Container\ContainerExceptionInterface;
9
use Psr\Container\NotFoundExceptionInterface;
10
use Psr\Http\Message\ResponseInterface;
11
use Psr\Http\Message\ServerRequestInterface;
12
use Throwable;
13
use Yiisoft\Definitions\Exception\CircularReferenceException;
14
use Yiisoft\Definitions\Exception\InvalidConfigException;
15
use Yiisoft\Definitions\Exception\NotInstantiableException;
16
use Yiisoft\Di\NotFoundException;
17
use Yiisoft\ErrorHandler\ErrorHandler;
18
use Yiisoft\ErrorHandler\Middleware\ErrorCatcher;
19
use Yiisoft\ErrorHandler\Renderer\HtmlRenderer;
20
use Yiisoft\Http\Method;
21
use Yiisoft\Log\Logger;
22
use Yiisoft\Log\Target\File\FileTarget;
23
use Yiisoft\Yii\Http\Application;
24
use Yiisoft\Yii\Http\Handler\ThrowableHandler;
25
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...
26
use Yiisoft\Yii\Runner\Http\Exception\HeadersHaveBeenSentException;
27
28
use function microtime;
29
30
/**
31
 * `HttpApplicationRunner` runs the Yii HTTP application.
32
 */
33
final class HttpApplicationRunner extends ApplicationRunner
34
{
35
    private ?ErrorHandler $temporaryErrorHandler = null;
36
37
    /**
38
     * @param string $rootPath The absolute path to the project root.
39
     * @param bool $debug Whether the debug mode is enabled.
40
     * @param string|null $environment The environment name.
41
     */
42
    public function __construct(string $rootPath, bool $debug, ?string $environment)
43
    {
44
        parent::__construct($rootPath, $debug, $environment);
45
        $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...
46
        $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...
47
    }
48
49
    /**
50
     * Returns a new instance with the specified temporary error handler instance {@see ErrorHandler}.
51
     *
52
     * A temporary error handler is needed to handle the creation of configuration and container instances,
53
     * then the error handler configured in your application configuration will be used.
54
     *
55
     * @param ErrorHandler $temporaryErrorHandler The temporary error handler instance.
56
     *
57 5
     * @return self
58
     */
59 5
    public function withTemporaryErrorHandler(ErrorHandler $temporaryErrorHandler): self
60 5
    {
61 5
        $new = clone $this;
62 5
        $new->temporaryErrorHandler = $temporaryErrorHandler;
63
        return $new;
64
    }
65
66
    /**
67
     * {@inheritDoc}
68
     *
69
     * @throws CircularReferenceException|ErrorException|HeadersHaveBeenSentException|InvalidConfigException
70
     * @throws ContainerExceptionInterface|NotFoundException|NotFoundExceptionInterface|NotInstantiableException
71 1
     */
72
    public function run(): void
73 1
    {
74 1
        $startTime = microtime(true);
75 1
76
        // Register temporary error handler to catch error while container is building.
77
        $temporaryErrorHandler = $this->createTemporaryErrorHandler();
78
        $this->registerErrorHandler($temporaryErrorHandler);
79
80
        $config = $this->getConfig();
81
        $container = $this->getContainer($config, 'web');
82
83 2
        // Register error handler with real container-configured dependencies.
84
        /** @var ErrorHandler $actualErrorHandler */
85 2
        $actualErrorHandler = $container->get(ErrorHandler::class);
86 2
        $this->registerErrorHandler($actualErrorHandler, $temporaryErrorHandler);
87 2
88
        $this->runBootstrap($config, $container);
89
        $this->checkEvents($config, $container);
90
91
        /** @var Application $application */
92
        $application = $container->get(Application::class);
93
94
        /**
95
         * @var ServerRequestInterface
96
         * @psalm-suppress MixedMethodCall
97 1
         */
98
        $serverRequest = $container->get(ServerRequestFactory::class)->createFromGlobals();
99 1
        $request = $serverRequest->withAttribute('applicationStartTime', $startTime);
100 1
101 1
        try {
102
            $application->start();
103
            $response = $application->handle($request);
104
            $this->emit($request, $response);
105
        } catch (Throwable $throwable) {
106
            $handler = new ThrowableHandler($throwable);
107
            /**
108
             * @var ResponseInterface
109 2
             * @psalm-suppress MixedMethodCall
110
             */
111 2
            $response = $container->get(ErrorCatcher::class)->process($request, $handler);
112 2
            $this->emit($request, $response);
113 2
        } finally {
114
            $application->afterEmit($response ?? null);
115
            $application->shutdown();
116
        }
117
    }
118
119
    private function createTemporaryErrorHandler(): ErrorHandler
120
    {
121
        if ($this->temporaryErrorHandler !== null) {
122
            return $this->temporaryErrorHandler;
123 2
        }
124
125 2
        $logger = new Logger([new FileTarget("$this->rootPath/runtime/logs/app.log")]);
126 2
        return new ErrorHandler($logger, new HtmlRenderer());
127 2
    }
128
129
    /**
130
     * @throws HeadersHaveBeenSentException
131
     */
132
    private function emit(ServerRequestInterface $request, ResponseInterface $response): void
133
    {
134
        (new SapiEmitter())->emit($response, $request->getMethod() === Method::HEAD);
135
    }
136
137 3
    /**
138
     * @throws ErrorException
139 3
     */
140 3
    private function registerErrorHandler(ErrorHandler $registered, ErrorHandler $unregistered = null): void
141 3
    {
142
        $unregistered?->unregister();
143
144
        if ($this->debug) {
145
            $registered->debug();
146
        }
147
148
        $registered->register();
149
    }
150
}
151