22namespace GT \WebEngine ;
33
44use Closure ;
5+ use Gt \Http \ResponseStatusException \ClientError \ClientErrorException ;
6+ use Gt \Http \ResponseStatusException \ResponseStatusException ;
7+ use Gt \Logger \Log ;
58use Throwable ;
69use ErrorException ;
710use GT \WebEngine \Debug \OutputBuffer ;
@@ -32,6 +35,7 @@ class Application {
3235 private Timer $ timer ;
3336 private OutputBuffer $ outputBuffer ;
3437 private RequestFactory $ requestFactory ;
38+ private ServerRequest $ request ;
3539 /** @var array<string, array<string, string|array<string, string>>> */
3640 private array $ globals ;
3741 private Protection $ globalProtection ;
@@ -62,7 +66,9 @@ public function __construct(
6266 $ this ->config ->getFloat ("app.slow_delta " ),
6367 $ this ->config ->getFloat ("app.very_slow_delta " ),
6468 );
65- $ this ->outputBuffer = $ outputBuffer ?? new OutputBuffer ();
69+ $ this ->outputBuffer = $ outputBuffer ?? new OutputBuffer (
70+ $ this ->config ->getBool ("logger.debug_to_javascript " )
71+ );
6672 $ this ->requestFactory = $ requestFactory ?? new RequestFactory ();
6773 $ this ->dispatcherFactory = $ dispatcherFactory ?? new DispatcherFactory ();
6874 $ this ->globals = array_merge ([
@@ -103,8 +109,7 @@ public function start():void {
103109// $_GET contains query parameters from the URL, and $_POST contains form data.
104110// These arrays are optional and will default to empty arrays if not provided,
105111// ensuring the ServerRequest can always be constructed safely.
106- /** @var ServerRequest $request */
107- $ request = $ this ->requestFactory ->createServerRequestFromGlobalState (
112+ $ this ->request = $ this ->requestFactory ->createServerRequestFromGlobalState (
108113 $ this ->globals ["_SERVER " ] ?? [],
109114 $ this ->globals ["_FILES " ] ?? [],
110115 $ this ->globals ["_GET " ] ?? [],
@@ -125,7 +130,7 @@ public function start():void {
125130// the application's error templates and logging mechanisms.
126131 $ this ->dispatcher = $ this ->dispatcherFactory ->create (
127132 $ this ->config ,
128- $ request ,
133+ $ this -> request ,
129134 $ this ->globals ,
130135 $ this ->finish (...),
131136 );
@@ -134,10 +139,27 @@ public function start():void {
134139 $ response = $ this ->dispatcher ->generateResponse ();
135140 }
136141 catch (Throwable $ throwable ) {
137- var_dump ($ throwable );
138- die ("ERRRRRRRRRRRRRRRRRRR " );
139142 $ this ->logError ($ throwable );
140- $ response = $ this ->dispatcher ->generateErrorResponse ($ throwable );
143+ $ errorStatus = 500 ;
144+
145+ if ($ throwable instanceof ResponseStatusException) {
146+ $ errorStatus = $ throwable ->getHttpCode ();
147+ }
148+
149+ $ this ->dispatcher = $ this ->dispatcherFactory ->create (
150+ $ this ->config ,
151+ $ this ->request ,
152+ $ this ->globals ,
153+ $ this ->finish (...),
154+ $ errorStatus ,
155+ );
156+
157+ try {
158+ $ response = $ this ->dispatcher ->generateErrorResponse ($ throwable );
159+ }
160+ catch (Throwable $ innerThrowable ) {
161+ $ response = $ this ->dispatcher ->generateBasicErrorResponse ($ throwable , $ innerThrowable );
162+ }
141163 }
142164
143165 $ this ->finish ($ response );
@@ -156,12 +178,19 @@ private function finish(
156178 $ response ->getHeaders (),
157179 );
158180
159- // If there's any content in the output buffer, render it to the developer console/log instead of the page.
160- $ this ->outputBuffer ->debugOutput ();
181+ if ($ this ->config ->getBool ("logger.log_all_requests " )) {
182+ Log::info (
183+ "HTTP " . $ response ->getStatusCode (),
184+ $ this ->getLogContext (),
185+ );
186+ }
161187
162188 /** @var Stream $responseBody */
163189 $ responseBody = $ response ->getBody ();
164- $ this ->outputResponseBody ($ responseBody );
190+ $ this ->outputResponseBody (
191+ $ responseBody ,
192+ $ this ->outputBuffer ->debugOutput (),
193+ );
165194
166195 $ this ->timer ->stop ();
167196 $ this ->timer ->logDelta ();
@@ -274,7 +303,32 @@ private function handleShutdown():void {
274303 }
275304
276305 private function logError (Throwable $ throwable ):void {
277- // TODO: implement
306+ if ($ throwable instanceof ClientErrorException) {
307+ return ;
308+ }
309+
310+ Log::error ($ throwable );
311+ }
312+
313+ /** @return array<string, string> */
314+ private function getLogContext ():array {
315+ $ uri = $ this ->request ->getUri ();
316+ $ uriPath = $ uri ->getPath ();
317+ $ uriQuery = $ this ->request ->getQueryParams ();
318+ $ postBody = $ this ->request ->getParsedBody ();
319+
320+ $ context = [
321+ "id " => $ this ->request ->getServerParams ()["REMOTE_ADDR " ] . ": " . substr (session_id (), 0 , 4 ),
322+ "uri " => $ uriPath ,
323+ ];
324+ if ($ uriQuery ) {
325+ $ context ["query " ] = $ uriQuery ;
326+ }
327+ if ($ postBody ) {
328+ $ context ["post " ] = $ postBody ;
329+ }
330+
331+ return $ context ;
278332 }
279333
280334 /** @param array<string, array<string>> $headers */
@@ -301,13 +355,21 @@ private function outputHeaders(int $statusCode, array $headers):void {
301355 * `ob_*` functions are used here to ensure that the response body is
302356 * flushed and doesn't get rendered into another open buffer.
303357 */
304- private function outputResponseBody (Stream $ responseBody ):void {
358+ private function outputResponseBody (Stream $ responseBody, ? string $ debugScript = null ):void {
305359 $ length = $ this ->config ->getInt ("app.render_buffer_size " );
306360
307361 $ responseBody ->rewind ();
308362 ob_start ();
309363 while (!$ responseBody ->eof ()) {
310- echo $ responseBody ->read ($ length );
364+ $ content = $ responseBody ->read ($ length );
365+ if ($ debugScript ) {
366+ $ closingBody = strpos ($ content , "</body> " );
367+ if (false !== $ closingBody ) {
368+ $ content = substr_replace ($ content , $ debugScript , $ closingBody , 0 );
369+ }
370+ }
371+ echo $ content ;
372+
311373 ob_flush ();
312374 flush ();
313375 }
0 commit comments