Skip to content

Commit fb8977b

Browse files
innocenzibrendt
andauthored
feat(router): introduce response processors (#1084)
Co-authored-by: Brent Roose <[email protected]>
1 parent ed6f85c commit fb8977b

23 files changed

+480
-310
lines changed

phpstan.neon.dist

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@ parameters:
2020
ignoreErrors:
2121
-
2222
identifier: argument.named
23-
-
24-
message: '#.*#'
25-
path: src/Tempest/Router/src/Exceptions/exception.php
2623
-
2724
message: '#.*exec*#'
2825
path: src/Tempest/Console/src/Terminal/Terminal.php

rector.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,6 @@
5858
EncapsedStringsToSprintfRector::class,
5959
AddArrowFunctionReturnTypeRector::class,
6060
])
61-
->withSkipPath(__DIR__ . '/src/Tempest/Router/src/Exceptions/HttpProductionErrorHandler.php')
62-
->withSkipPath(__DIR__ . '/src/Tempest/Router/src/Exceptions/exception.php')
6361
->withParallel(300, 10, 10)
6462
->withPreparedSets(
6563
codeQuality: false,

src/Tempest/Core/src/FrameworkKernel.php

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
use Tempest\Core\Kernel\LoadDiscoveryLocations;
1515
use Tempest\Core\ShellExecutors\GenericShellExecutor;
1616
use Tempest\EventBus\EventBus;
17-
use Tempest\Router\Exceptions\HttpProductionErrorHandler;
1817
use Whoops\Handler\PrettyPageHandler;
1918
use Whoops\Run;
2019

@@ -52,7 +51,7 @@ public static function boot(
5251
container: $container,
5352
)
5453
->loadEnv()
55-
->registerKernelErrorHandler()
54+
->registerEmergencyErrorHandler()
5655
->registerShutdownFunction()
5756
->registerInternalStorage()
5857
->registerKernel()
@@ -150,27 +149,6 @@ public function loadConfig(): self
150149
return $this;
151150
}
152151

153-
public function registerErrorHandler(): self
154-
{
155-
$appConfig = $this->container->get(AppConfig::class);
156-
157-
if ($appConfig->environment->isTesting()) {
158-
return $this;
159-
}
160-
161-
if (PHP_SAPI === 'cli') {
162-
$handler = $this->container->get(ConsoleErrorHandler::class);
163-
set_exception_handler($handler->handleException(...));
164-
set_error_handler($handler->handleError(...)); // @phpstan-ignore-line
165-
} elseif ($appConfig->environment->isProduction()) {
166-
$handler = $this->container->get(HttpProductionErrorHandler::class);
167-
set_exception_handler($handler->handleException(...));
168-
set_error_handler($handler->handleError(...)); // @phpstan-ignore-line
169-
}
170-
171-
return $this;
172-
}
173-
174152
public function registerInternalStorage(): self
175153
{
176154
$path = $this->root . '/vendor/.tempest';
@@ -200,24 +178,44 @@ public function event(object $event): self
200178
return $this;
201179
}
202180

203-
public function registerKernelErrorHandler(): self
181+
public function registerEmergencyErrorHandler(): self
204182
{
205183
$environment = Environment::fromEnv();
206184

185+
// During tests, PHPUnit registers its own error handling.
207186
if ($environment->isTesting()) {
208187
return $this;
209188
}
210189

211-
if (PHP_SAPI !== 'cli' && $environment->isProduction()) {
212-
$handler = new HttpProductionErrorHandler();
213-
set_exception_handler($handler->handleException(...));
214-
set_error_handler($handler->handleError(...)); // @phpstan-ignore-line
215-
} elseif (PHP_SAPI !== 'cli') {
190+
// In development, we want to register a developer-friendly error
191+
// handler as soon as possible to catch any kind of exception.
192+
if (PHP_SAPI !== 'cli' && ! $environment->isProduction()) {
216193
$whoops = new Run();
217194
$whoops->pushHandler(new PrettyPageHandler());
218195
$whoops->register();
219196
}
220197

221198
return $this;
222199
}
200+
201+
public function registerErrorHandler(): self
202+
{
203+
$appConfig = $this->container->get(AppConfig::class);
204+
205+
// During tests, PHPUnit registers its own error handling.
206+
if ($appConfig->environment->isTesting()) {
207+
return $this;
208+
}
209+
210+
// We already have a non-CLI error handler.
211+
if (PHP_SAPI !== 'cli') {
212+
return $this;
213+
}
214+
215+
$handler = $this->container->get(ConsoleErrorHandler::class);
216+
set_exception_handler($handler->handleException(...));
217+
set_error_handler($handler->handleError(...)); // @phpstan-ignore-line
218+
219+
return $this;
220+
}
223221
}

src/Tempest/Core/src/Kernel.php

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,7 @@
44

55
namespace Tempest\Core;
66

7-
use Dotenv\Dotenv;
8-
use Tempest\Console\Exceptions\ConsoleErrorHandler;
97
use Tempest\Container\Container;
10-
use Tempest\Container\GenericContainer;
11-
use Tempest\Core\Kernel\FinishDeferredTasks;
12-
use Tempest\Core\Kernel\LoadConfig;
13-
use Tempest\Core\Kernel\LoadDiscoveryClasses;
14-
use Tempest\Core\Kernel\LoadDiscoveryLocations;
15-
use Tempest\Core\ShellExecutors\GenericShellExecutor;
16-
use Tempest\EventBus\EventBus;
17-
use Tempest\Router\Exceptions\HttpProductionErrorHandler;
18-
use Whoops\Handler\PrettyPageHandler;
19-
use Whoops\Run;
208

219
interface Kernel
2210
{
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
namespace Tempest\Router\ErrorResponses;
4+
5+
use Tempest\Http\Status;
6+
use Tempest\Router\Response;
7+
use Tempest\Router\ResponseProcessor;
8+
use Tempest\View\GenericView;
9+
use Tempest\View\View;
10+
11+
final class ErrorResponseProcessor implements ResponseProcessor
12+
{
13+
public function process(Response $response): Response
14+
{
15+
if ($response->status->isClientError() || $response->status->isServerError()) {
16+
$response->setBody($this->renderErrorView($response->status));
17+
}
18+
19+
return $response;
20+
}
21+
22+
private function renderErrorView(Status $status): View
23+
{
24+
return new GenericView(__DIR__ . '/error.view.php', [
25+
'css' => $this->getCss(),
26+
'status' => $status->value,
27+
'title' => $status->description(),
28+
'message' => match ($status) {
29+
Status::INTERNAL_SERVER_ERROR => 'An unexpected server error occurred',
30+
Status::NOT_FOUND => 'This page could not be found on the server',
31+
Status::FORBIDDEN => 'You do not have permission to access this page',
32+
Status::UNAUTHORIZED => 'You must be authenticated in to access this page',
33+
default => null,
34+
},
35+
]);
36+
}
37+
38+
private function getCss(): string
39+
{
40+
return file_get_contents(__DIR__ . '/style.css');
41+
}
42+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<html lang="en">
2+
<head>
3+
<title>{{ $title }}</title>
4+
<style>{!! $css !!}</style>
5+
</head>
6+
<body>
7+
<div class="flex flex-col justify-center items-center bg-[#061324] min-w-screen min-h-screen text-[#a8caf7] antialiased">
8+
<div class="px-8 container">
9+
<h1 class="font-thin text-8xl flex gap-x-1">
10+
<span class="text-[#4c6586]">HTTP</span>
11+
<span>{{ $status }}</span>
12+
</h1>
13+
<p :if="$message" class="text-xl uppercase text-[#4c6586]">{{ \Tempest\Support\Str\ensure_ends_with($message, '.') }}</p>
14+
</div>
15+
</div>
16+
</body>
17+
</html>

0 commit comments

Comments
 (0)