Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{
return $_SERVER[$key] ?? $default;
}
Expand Down Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion src/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
4 changes: 2 additions & 2 deletions src/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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;
Expand Down
56 changes: 54 additions & 2 deletions tests/AppTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 = [
Expand Down
17 changes: 17 additions & 0 deletions tests/e2e/ResponseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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', [
Expand Down