Issues (7)

src/HttpApplicationRunner.php (2 issues)

Labels
Severity
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;
0 ignored issues
show
The type Yiisoft\ErrorHandler\ErrorHandler 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...
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;
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 bool $checkEvents Whether to check events' configuration.
41
     * @param string|null $environment The environment name.
42
     * @param string $bootstrapGroup The bootstrap configuration group name.
43
     * @param string $eventsGroup The events' configuration group name.
44
     * @param string $diGroup The container definitions' configuration group name.
45
     * @param string $diProvidersGroup The container providers' configuration group name.
46
     * @param string $diDelegatesGroup The container delegates' configuration group name.
47
     * @param string $diTagsGroup The container tags' configuration group name.
48
     * @param string $paramsGroup The configuration parameters group name.
49
     * @param array $nestedParamsGroups Configuration group names that are included into configuration parameters group.
50
     * This is needed for recursive merging of parameters.
51
     * @param array $nestedEventsGroups Configuration group names that are included into events' configuration group.
52
     * This is needed for reverse and recursive merge of events' configurations.
53
     * @param object[] $configModifiers Modifiers for {@see Config}.
54
     *
55
     * @psalm-param list<string> $nestedParamsGroups
56
     * @psalm-param list<string> $nestedEventsGroups
57
     * @psalm-param list<object> $configModifiers
58
     */
59 5
    public function __construct(
60
        string $rootPath,
61
        bool $debug = false,
62
        bool $checkEvents = false,
63
        ?string $environment = null,
64
        string $bootstrapGroup = 'bootstrap-web',
65
        string $eventsGroup = 'events-web',
66
        string $diGroup = 'di-web',
67
        string $diProvidersGroup = 'di-providers-web',
68
        string $diDelegatesGroup = 'di-delegates-web',
69
        string $diTagsGroup = 'di-tags-web',
70
        string $paramsGroup = 'params-web',
71
        array $nestedParamsGroups = ['params'],
72
        array $nestedEventsGroups = ['events'],
73
        array $configModifiers = [],
74
    ) {
75 5
        parent::__construct(
76 5
            $rootPath,
77 5
            $debug,
78 5
            $checkEvents,
79 5
            $environment,
80 5
            $bootstrapGroup,
81 5
            $eventsGroup,
82 5
            $diGroup,
83 5
            $diProvidersGroup,
84 5
            $diDelegatesGroup,
85 5
            $diTagsGroup,
86 5
            $paramsGroup,
87 5
            $nestedParamsGroups,
88 5
            $nestedEventsGroups,
89 5
            $configModifiers,
90 5
        );
91
    }
92
93
    /**
94
     * Returns a new instance with the specified temporary error handler instance {@see ErrorHandler}.
95
     *
96
     * A temporary error handler is needed to handle the creation of configuration and container instances,
97
     * then the error handler configured in your application configuration will be used.
98
     *
99
     * @param ErrorHandler $temporaryErrorHandler The temporary error handler instance.
100
     */
101 2
    public function withTemporaryErrorHandler(ErrorHandler $temporaryErrorHandler): self
102
    {
103 2
        $new = clone $this;
104 2
        $new->temporaryErrorHandler = $temporaryErrorHandler;
105 2
        return $new;
106
    }
107
108
    /**
109
     * {@inheritDoc}
110
     *
111
     * @throws CircularReferenceException|ErrorException|HeadersHaveBeenSentException|InvalidConfigException
112
     * @throws ContainerExceptionInterface|NotFoundException|NotFoundExceptionInterface|NotInstantiableException
113
     */
114 4
    public function run(): void
115
    {
116 4
        $startTime = microtime(true);
117
118
        // Register temporary error handler to catch error while container is building.
119 4
        $temporaryErrorHandler = $this->createTemporaryErrorHandler();
120 4
        $this->registerErrorHandler($temporaryErrorHandler);
121
122 4
        $container = $this->getContainer();
123
124
        // Register error handler with real container-configured dependencies.
125
        /** @var ErrorHandler $actualErrorHandler */
126 4
        $actualErrorHandler = $container->get(ErrorHandler::class);
127 4
        $this->registerErrorHandler($actualErrorHandler, $temporaryErrorHandler);
128
129 4
        $this->runBootstrap();
130 4
        $this->checkEvents();
131
132
        /** @var Application $application */
133 4
        $application = $container->get(Application::class);
134
135
        /**
136
         * @var RequestFactory $requestFactory
137
         */
138 4
        $requestFactory = $container->get(RequestFactory::class);
139 4
        $request = $requestFactory->create();
140
141 4
        $request = $request->withAttribute('applicationStartTime', $startTime);
142
        try {
143 4
            $application->start();
144 4
            $response = $application->handle($request);
145 3
            $this->emit($request, $response);
146 1
        } catch (Throwable $throwable) {
147 1
            $handler = new ThrowableHandler($throwable);
148
            /**
149
             * @var ResponseInterface
150
             * @psalm-suppress MixedMethodCall
151
             */
152 1
            $response = $container
153 1
                ->get(ErrorCatcher::class)
154 1
                ->process($request, $handler);
155 1
            $this->emit($request, $response);
156
        } finally {
157 4
            $application->afterEmit($response ?? null);
158 4
            $application->shutdown();
159
        }
160
    }
161
162 4
    private function createTemporaryErrorHandler(): ErrorHandler
163
    {
164 4
        if ($this->temporaryErrorHandler !== null) {
165 1
            return $this->temporaryErrorHandler;
166
        }
167
168 3
        $logger = new Logger([new FileTarget("$this->rootPath/runtime/logs/app.log")]);
169 3
        return new ErrorHandler($logger, new HtmlRenderer());
170
    }
171
172
    /**
173
     * @throws HeadersHaveBeenSentException
174
     */
175 4
    private function emit(ServerRequestInterface $request, ResponseInterface $response): void
176
    {
177 4
        (new SapiEmitter())->emit($response, $request->getMethod() === Method::HEAD);
0 ignored issues
show
The type Yiisoft\Yii\Runner\Http\SapiEmitter 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...
178
    }
179
180
    /**
181
     * @throws ErrorException
182
     */
183 4
    private function registerErrorHandler(ErrorHandler $registered, ErrorHandler $unregistered = null): void
184
    {
185 4
        $unregistered?->unregister();
186
187 4
        if ($this->debug) {
188 4
            $registered->debug();
189
        }
190
191 4
        $registered->register();
192
    }
193
}
194