1414use Tempest \Core \Kernel \LoadDiscoveryLocations ;
1515use Tempest \Core \ShellExecutors \GenericShellExecutor ;
1616use Tempest \EventBus \EventBus ;
17- use Tempest \Router \Exceptions \HttpProductionErrorHandler ;
1817use Whoops \Handler \PrettyPageHandler ;
1918use Whoops \Run ;
2019
@@ -51,8 +50,9 @@ public static function boot(
5150 discoveryLocations: $ discoveryLocations ,
5251 container: $ container ,
5352 )
53+ ->validateRoot ()
5454 ->loadEnv ()
55- ->registerKernelErrorHandler ()
55+ ->registerEmergencyErrorHandler ()
5656 ->registerShutdownFunction ()
5757 ->registerInternalStorage ()
5858 ->registerKernel ()
@@ -64,6 +64,19 @@ public static function boot(
6464 ->event (KernelEvent::BOOTED );
6565 }
6666
67+ public function validateRoot (): self
68+ {
69+ $ root = realpath ($ this ->root );
70+
71+ if (! is_dir ($ root )) {
72+ throw new \RuntimeException ('The specified root directory is not valid. ' );
73+ }
74+
75+ $ this ->root = $ root ;
76+
77+ return $ this ;
78+ }
79+
6780 public function shutdown (int |string $ status = '' ): never
6881 {
6982 $ this ->finishDeferredTasks ()
@@ -150,27 +163,6 @@ public function loadConfig(): self
150163 return $ this ;
151164 }
152165
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-
174166 public function registerInternalStorage (): self
175167 {
176168 $ path = $ this ->root . '/vendor/.tempest ' ;
@@ -200,24 +192,44 @@ public function event(object $event): self
200192 return $ this ;
201193 }
202194
203- public function registerKernelErrorHandler (): self
195+ public function registerEmergencyErrorHandler (): self
204196 {
205197 $ environment = Environment::fromEnv ();
206198
199+ // During tests, PHPUnit registers its own error handling.
207200 if ($ environment ->isTesting ()) {
208201 return $ this ;
209202 }
210203
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 ' ) {
204+ // In development, we want to register a developer-friendly error
205+ // handler as soon as possible to catch any kind of exception.
206+ if (PHP_SAPI !== 'cli ' && ! $ environment ->isProduction ()) {
216207 $ whoops = new Run ();
217208 $ whoops ->pushHandler (new PrettyPageHandler ());
218209 $ whoops ->register ();
219210 }
220211
221212 return $ this ;
222213 }
214+
215+ public function registerErrorHandler (): self
216+ {
217+ $ appConfig = $ this ->container ->get (AppConfig::class);
218+
219+ // During tests, PHPUnit registers its own error handling.
220+ if ($ appConfig ->environment ->isTesting ()) {
221+ return $ this ;
222+ }
223+
224+ // We already have a non-CLI error handler.
225+ if (PHP_SAPI !== 'cli ' ) {
226+ return $ this ;
227+ }
228+
229+ $ handler = $ this ->container ->get (ConsoleErrorHandler::class);
230+ set_exception_handler ($ handler ->handleException (...));
231+ set_error_handler ($ handler ->handleError (...)); // @phpstan-ignore-line
232+
233+ return $ this ;
234+ }
223235}
0 commit comments