From 80106e39e9e7525c4228e9e3fbab89de7e58c362 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Tue, 10 Dec 2024 10:43:34 +0000 Subject: [PATCH 1/6] fix: catch error handler --- src/App.php | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/App.php b/src/App.php index b895128..2d188ac 100755 --- a/src/App.php +++ b/src/App.php @@ -800,7 +800,13 @@ private function runInternal(Request $request, Response $response): static self::setResource('error', function () use ($e) { return $e; }); - \call_user_func_array($error->getAction(), $this->getArguments($error, [], $request->getParams())); + + try { + $arguments = $this->getArguments($error, [], $request->getParams()); + \call_user_func_array($error->getAction(), $arguments); + } catch (\Throwable $e) { + throw new Exception('Error handler had an error: ' . $e->getMessage(), 500, $e); + } } } } @@ -838,7 +844,12 @@ private function runInternal(Request $request, Response $response): static self::setResource('error', function () use ($e) { return $e; }); - \call_user_func_array($error->getAction(), $this->getArguments($error, [], $request->getParams())); + try { + $arguments = $this->getArguments($error, [], $request->getParams()); + \call_user_func_array($error->getAction(), $arguments); + } catch (\Throwable $e) { + throw new Exception('Error handler had an error: ' . $e->getMessage(), 500, $e); + } } } } @@ -848,7 +859,12 @@ private function runInternal(Request $request, Response $response): static self::setResource('error', function () { return new Exception('Not Found', 404); }); - \call_user_func_array($error->getAction(), $this->getArguments($error, [], $request->getParams())); + try { + $arguments = $this->getArguments($error, [], $request->getParams()); + \call_user_func_array($error->getAction(), $arguments); + } catch (\Throwable $e) { + throw new Exception('Error handler had an error: ' . $e->getMessage(), 500, $e); + } } } } From 4d36d9a83d28361df8e2bd7baca07de71b5024c5 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Tue, 10 Dec 2024 10:44:47 +0000 Subject: [PATCH 2/6] fix: spacing --- src/App.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/App.php b/src/App.php index 2d188ac..63965ec 100755 --- a/src/App.php +++ b/src/App.php @@ -800,7 +800,6 @@ private function runInternal(Request $request, Response $response): static self::setResource('error', function () use ($e) { return $e; }); - try { $arguments = $this->getArguments($error, [], $request->getParams()); \call_user_func_array($error->getAction(), $arguments); From b887c605ec58d58c20a4cd94c8c41966c377cec8 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:39:14 +0000 Subject: [PATCH 3/6] test: error handler scenerios --- tests/AppTest.php | 141 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/tests/AppTest.php b/tests/AppTest.php index 38f720d..3add518 100755 --- a/tests/AppTest.php +++ b/tests/AppTest.php @@ -659,4 +659,145 @@ public function testWildcardRoute(): void $_SERVER['REQUEST_METHOD'] = $method; $_SERVER['REQUEST_URI'] = $uri; } + + public function testErrorHandlerFailure(): void + { + $this->app + ->error() + ->inject('error') + ->action(function($error) { + throw new \Exception('Error handler failed'); + }); + + $route = new Route('GET', '/path'); + $route + ->action(function() { + throw new \Exception('Route action failed'); + }); + + try { + $this->app->execute($route, new Request(), new Response()); + $this->fail('Should have thrown an exception'); + } catch (\Exception $e) { + $this->assertEquals('Error handler had an error: Error handler failed', $e->getMessage()); + $this->assertEquals(500, $e->getCode()); + $this->assertInstanceOf(\Exception::class, $e->getPrevious()); + $this->assertEquals('Error handler failed', $e->getPrevious()->getMessage()); + } + } + + public function testOptionsHandlerFailure(): void + { + $this->app + ->error() + ->inject('error') + ->action(function($error) { + throw new \Exception('Options error handler failed'); + }); + + // Set up an options handler that throws + App::options() + ->action(function() { + throw new \Exception('Options handler failed'); + }); + + $request = new UtopiaRequestTest(); + $request->setMethod('OPTIONS'); + + try { + $this->app->run($request, new Response()); + $this->fail('Should have thrown an exception'); + } catch (\Exception $e) { + $this->assertEquals('Error handler had an error: Options error handler failed', $e->getMessage()); + $this->assertEquals(500, $e->getCode()); + } + } + + public function testNotFoundErrorHandlerFailure(): void + { + // Set up error handler that throws for 404 cases + $this->app + ->error() + ->action(function() { + throw new \Exception('404 error handler failed'); + }); + + $request = new UtopiaRequestTest(); + $request->setMethod('GET'); + $request->setURI('/nonexistent-path'); + + try { + $this->app->run($request, new Response()); + $this->fail('Should have thrown an exception'); + } catch (\Exception $e) { + $this->assertEquals('Error handler had an error: 404 error handler failed', $e->getMessage()); + $this->assertEquals(500, $e->getCode()); + $this->assertInstanceOf(\Exception::class, $e->getPrevious()); + $this->assertEquals('404 error handler failed', $e->getPrevious()->getMessage()); + } + } + + public function testGroupErrorHandlerFailure(): void + { + // Set up group-specific error handler that throws + $this->app + ->error() + ->groups(['api']) + ->action(function() { + throw new \Exception('Group error handler failed'); + }); + + $route = new Route('GET', '/api/test'); + $route + ->groups(['api']) + ->action(function() { + throw new \Exception('Route action failed'); + }); + + try { + $this->app->execute($route, new Request(), new Response()); + $this->fail('Should have thrown an exception'); + } catch (\Exception $e) { + $this->assertEquals('Error handler had an error: Group error handler failed', $e->getMessage()); + $this->assertEquals(500, $e->getCode()); + $this->assertInstanceOf(\Exception::class, $e->getPrevious()); + $this->assertEquals('Group error handler failed', $e->getPrevious()->getMessage()); + } + } + + public function testErrorHandlerChaining(): void + { + // Set up multiple error handlers to test chaining behavior + $this->app + ->error() + ->groups(['api']) + ->action(function() { + throw new \Exception('First error handler failed'); + }); + + $this->app + ->error() + ->action(function() { + throw new \Exception('Second error handler failed'); + }); + + $route = new Route('GET', '/api/test'); + $route + ->groups(['api']) + ->action(function() { + throw new \Exception('Original error'); + }); + + try { + $this->app->execute($route, new Request(), new Response()); + $this->fail('Should have thrown an exception'); + } catch (\Exception $e) { + $this->assertEquals('Error handler had an error: First error handler failed', $e->getMessage()); + $this->assertEquals(500, $e->getCode()); + + // Verify the error chain + $this->assertInstanceOf(\Exception::class, $e->getPrevious()); + $this->assertEquals('First error handler failed', $e->getPrevious()->getMessage()); + } + } } From c087eb077d9b9d271f8e16af437cc8adcbce1495 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:42:28 +0000 Subject: [PATCH 4/6] chore: fmt --- tests/AppTest.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/AppTest.php b/tests/AppTest.php index 3add518..5242e83 100755 --- a/tests/AppTest.php +++ b/tests/AppTest.php @@ -665,13 +665,13 @@ public function testErrorHandlerFailure(): void $this->app ->error() ->inject('error') - ->action(function($error) { + ->action(function ($error) { throw new \Exception('Error handler failed'); }); $route = new Route('GET', '/path'); $route - ->action(function() { + ->action(function () { throw new \Exception('Route action failed'); }); @@ -691,13 +691,13 @@ public function testOptionsHandlerFailure(): void $this->app ->error() ->inject('error') - ->action(function($error) { + ->action(function ($error) { throw new \Exception('Options error handler failed'); }); // Set up an options handler that throws App::options() - ->action(function() { + ->action(function () { throw new \Exception('Options handler failed'); }); @@ -718,7 +718,7 @@ public function testNotFoundErrorHandlerFailure(): void // Set up error handler that throws for 404 cases $this->app ->error() - ->action(function() { + ->action(function () { throw new \Exception('404 error handler failed'); }); @@ -743,14 +743,14 @@ public function testGroupErrorHandlerFailure(): void $this->app ->error() ->groups(['api']) - ->action(function() { + ->action(function () { throw new \Exception('Group error handler failed'); }); $route = new Route('GET', '/api/test'); $route ->groups(['api']) - ->action(function() { + ->action(function () { throw new \Exception('Route action failed'); }); @@ -771,20 +771,20 @@ public function testErrorHandlerChaining(): void $this->app ->error() ->groups(['api']) - ->action(function() { + ->action(function () { throw new \Exception('First error handler failed'); }); $this->app ->error() - ->action(function() { + ->action(function () { throw new \Exception('Second error handler failed'); }); $route = new Route('GET', '/api/test'); $route ->groups(['api']) - ->action(function() { + ->action(function () { throw new \Exception('Original error'); }); @@ -794,7 +794,7 @@ public function testErrorHandlerChaining(): void } catch (\Exception $e) { $this->assertEquals('Error handler had an error: First error handler failed', $e->getMessage()); $this->assertEquals(500, $e->getCode()); - + // Verify the error chain $this->assertInstanceOf(\Exception::class, $e->getPrevious()); $this->assertEquals('First error handler failed', $e->getPrevious()->getMessage()); From f642ceef8078c5f1454c4142c41fc860ee337a6d Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:43:28 +0000 Subject: [PATCH 5/6] fix: codeql --- .github/workflows/codeql-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 126ef74..70bba1b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -12,6 +12,6 @@ jobs: - name: Run CodeQL run: | - docker run --rm -v $PWD:/app composer sh -c \ + docker run --rm -v $PWD:/app composer:2.6 sh -c \ "composer install --profile --ignore-platform-reqs && composer check" \ No newline at end of file From 68f5fb300ee24aaa2f18deaa67a51b86002437e8 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:47:14 +0000 Subject: [PATCH 6/6] fix: bench --- .github/workflows/bench.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 0d97b2b..43625f9 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -12,5 +12,5 @@ jobs: - name: Run Linter run: | - docker run --rm -v $PWD:/app composer sh -c \ + docker run --rm -v $PWD:/app composer:2.6 sh -c \ "composer install --profile --ignore-platform-reqs && git config --global --add safe.directory /app && composer bench -- --progress=plain"