diff --git a/src/App.php b/src/App.php index aa2249a..d041bc3 100755 --- a/src/App.php +++ b/src/App.php @@ -344,7 +344,7 @@ public static function error(): Hook * @param string|null $default * @return string|null */ - public static function getEnv(string $key, string $default = null): ?string + public static function getEnv(string $key, ?string $default = null): ?string { return $_SERVER[$key] ?? $default; } @@ -561,6 +561,11 @@ public function match(Request $request, bool $fresh = false): ?Route } $url = \parse_url($request->getURI(), PHP_URL_PATH); + + if ($url === null || $url === false) { + $url = '/'; // Default to root path for malformed URLs + } + $method = $request->getMethod(); $method = (self::REQUEST_METHOD_HEAD == $method) ? self::REQUEST_METHOD_GET : $method; diff --git a/src/Request.php b/src/Request.php index b877920..2cd008e 100755 --- a/src/Request.php +++ b/src/Request.php @@ -136,7 +136,7 @@ public function getRawPayload(): string * @param string|null $default * @return string|null */ - public function getServer(string $key, string $default = null): ?string + public function getServer(string $key, ?string $default = null): ?string { return $_SERVER[$key] ?? $default; } diff --git a/src/Response.php b/src/Response.php index 470097e..0bc4d08 100755 --- a/src/Response.php +++ b/src/Response.php @@ -533,7 +533,7 @@ public function getHeaders(): array * @param bool $httponly * @param string $sameSite */ - public function addCookie(string $name, string $value = null, int $expire = null, string $path = null, string $domain = null, bool $secure = null, bool $httponly = null, string $sameSite = null): static + public function addCookie(string $name, ?string $value = null, ?int $expire = null, ?string $path = null, ?string $domain = null, ?bool $secure = null, ?bool $httponly = null, ?string $sameSite = null): static { $name = strtolower($name); @@ -665,7 +665,7 @@ protected function write(string $content): void * @param string $content * @return void */ - protected function end(string $content = null): void + protected function end(?string $content = null): void { if (!is_null($content)) { echo $content; diff --git a/tests/AppTest.php b/tests/AppTest.php index 5242e83..7a3c4f2 100755 --- a/tests/AppTest.php +++ b/tests/AppTest.php @@ -221,7 +221,7 @@ public function testCanExecuteRoute(): void ->param('x', 'x-def', new Text(1, min: 0), 'x param', false) ->param('y', 'y-def', new Text(1, min: 0), 'y param', false) ->action(function ($x, $y) { - echo $x . '-', $y; + echo $x . '-' . $y; }); \ob_start(); @@ -467,7 +467,7 @@ public function providerRouteMatching(): array /** * @dataProvider providerRouteMatching */ - public function testCanMatchRoute(string $method, string $path, string $url = null): void + public function testCanMatchRoute(string $method, string $path, ?string $url = null): void { $url ??= $path; $expected = null; @@ -497,6 +497,58 @@ public function testCanMatchRoute(string $method, string $path, string $url = nu $this->assertEquals($expected, $this->app->getRoute()); } + public function testMatchWithNullPath(): void + { + // Create a route for root path + $expected = App::get('/'); + + // Test case where parse_url returns null (malformed URL) + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['REQUEST_URI'] = '?param=1'; // This will cause parse_url to return null for PATH component + + $matched = $this->app->match(new Request()); + $this->assertEquals($expected, $matched); + } + + public function testMatchWithEmptyPath(): void + { + // Create a route for root path + $expected = App::get('/'); + + // Test case where URI has no path component + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['REQUEST_URI'] = 'https://example.com'; // No path component + + $matched = $this->app->match(new Request()); + $this->assertEquals($expected, $matched); + } + + public function testMatchWithMalformedURL(): void + { + // Create a route for root path + $expected = App::get('/'); + + // Test case where parse_url returns false (severely malformed URL) + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['REQUEST_URI'] = '#fragment'; // Malformed scheme + + $matched = $this->app->match(new Request()); + $this->assertEquals($expected, $matched); + } + + public function testMatchWithOnlyQueryString(): void + { + // Create a route for root path + $expected = App::get('/'); + + // Test case where URI has only query string (no path) + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['REQUEST_URI'] = '?param=value'; // Only query string, no path + + $matched = $this->app->match(new Request()); + $this->assertEquals($expected, $matched); + } + public function testNoMismatchRoute(): void { $requests = [ diff --git a/tests/e2e/ResponseTest.php b/tests/e2e/ResponseTest.php index ecbb976..64ebfef 100644 --- a/tests/e2e/ResponseTest.php +++ b/tests/e2e/ResponseTest.php @@ -57,6 +57,23 @@ public function testEarlyResponse() $this->assertEquals('Init response. Actioned before: no', $response['body']); } + public function testNullPathHandling() + { + // Test that malformed URLs default to root path + $response = $this->client->call(Client::METHOD_GET, '/'); + $this->assertEquals('Hello World!', $response['body']); + $this->assertEquals(200, $response['headers']['status-code']); + } + + public function testRootPathFallback() + { + // Test that when path parsing fails, it falls back to root + // This is tested by ensuring the root route works correctly + $response = $this->client->call(Client::METHOD_GET, '/'); + $this->assertEquals('Hello World!', $response['body']); + $this->assertEquals(200, $response['headers']['status-code']); + } + public function testAliasWithParameter(): void { $response = $this->client->call(Client::METHOD_POST, '/functions/deployment', [