From 570209894551027b28375024ceae87ed39c24b40 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 17 Nov 2025 06:27:14 +0545 Subject: [PATCH 1/6] Feat: support base URL --- src/Client.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Client.php b/src/Client.php index 37659eb..ab6e614 100644 --- a/src/Client.php +++ b/src/Client.php @@ -37,6 +37,18 @@ class Client private array $retryStatusCodes = [500, 503]; private mixed $jsonEncodeFlags; + protected string $baseUrl = ''; + + public function setBaseUrl(string $baseUrl): void + { + $this->baseUrl = $baseUrl; + } + + public function getBaseUrl(): string + { + return $this->baseUrl; + } + /** * @param string $key * @param string $value @@ -248,6 +260,8 @@ public function fetch( ?int $timeoutMs = null, ?int $connectTimeoutMs = null, ): Response { + $url = "{$this->baseUrl}{$url}"; + if (!in_array($method, [self::METHOD_PATCH, self::METHOD_GET, self::METHOD_CONNECT, self::METHOD_DELETE, self::METHOD_POST, self::METHOD_HEAD, self::METHOD_OPTIONS, self::METHOD_PUT, self::METHOD_TRACE])) { throw new Exception("Unsupported HTTP method"); } From 330fabdd32ac26c81ddb1a41eb610a59b75930f9 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 17 Nov 2025 01:52:38 +0000 Subject: [PATCH 2/6] Format --- src/Client.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Client.php b/src/Client.php index ab6e614..90c83de 100644 --- a/src/Client.php +++ b/src/Client.php @@ -261,7 +261,7 @@ public function fetch( ?int $connectTimeoutMs = null, ): Response { $url = "{$this->baseUrl}{$url}"; - + if (!in_array($method, [self::METHOD_PATCH, self::METHOD_GET, self::METHOD_CONNECT, self::METHOD_DELETE, self::METHOD_POST, self::METHOD_HEAD, self::METHOD_OPTIONS, self::METHOD_PUT, self::METHOD_TRACE])) { throw new Exception("Unsupported HTTP method"); } From 4221e075a61eb43509d99a001b51676ad69a877f Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 1 Dec 2025 00:57:48 +0000 Subject: [PATCH 3/6] Fix suggestions --- src/Client.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Client.php b/src/Client.php index 90c83de..bebcede 100644 --- a/src/Client.php +++ b/src/Client.php @@ -39,9 +39,10 @@ class Client protected string $baseUrl = ''; - public function setBaseUrl(string $baseUrl): void + public function setBaseUrl(string $baseUrl): static { $this->baseUrl = $baseUrl; + return static; } public function getBaseUrl(): string @@ -260,7 +261,9 @@ public function fetch( ?int $timeoutMs = null, ?int $connectTimeoutMs = null, ): Response { - $url = "{$this->baseUrl}{$url}"; + if (!empty($this->baseUrl) && !preg_match('~^https?://~i', $url)) { + $url = rtrim($this->baseUrl, '/') . '/' . ltrim($url, '/'); + } if (!in_array($method, [self::METHOD_PATCH, self::METHOD_GET, self::METHOD_CONNECT, self::METHOD_DELETE, self::METHOD_POST, self::METHOD_HEAD, self::METHOD_OPTIONS, self::METHOD_PUT, self::METHOD_TRACE])) { throw new Exception("Unsupported HTTP method"); From 9ed8075b537ce1ff48f7b7f1276cf44a337490ec Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Mon, 1 Dec 2025 01:03:20 +0000 Subject: [PATCH 4/6] Fixes and add test for base url --- src/Client.php | 6 +- tests/ClientTest.php | 135 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+), 3 deletions(-) diff --git a/src/Client.php b/src/Client.php index bebcede..2f0f367 100644 --- a/src/Client.php +++ b/src/Client.php @@ -39,10 +39,10 @@ class Client protected string $baseUrl = ''; - public function setBaseUrl(string $baseUrl): static + public function setBaseUrl(string $baseUrl): self { $this->baseUrl = $baseUrl; - return static; + return $this; } public function getBaseUrl(): string @@ -140,7 +140,7 @@ public function setMaxRetries(int $maxRetries): self * * @param array $flags * @return self - */ + */ public function setJsonEncodeFlags(array $flags): self { $this->jsonEncodeFlags = implode('|', $flags); diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 028670e..989274e 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -271,6 +271,141 @@ public function testSetGetUserAgent(): void $this->assertEquals($userAgent, $client->getUserAgent()); } + /** + * Test setting and getting the base URL. + * @return void + */ + public function testSetGetBaseUrl(): void + { + $client = new Client(); + $baseUrl = "http://localhost:8000"; + + $client->setBaseUrl($baseUrl); + + $this->assertEquals($baseUrl, $client->getBaseUrl()); + } + + /** + * Test base URL prepending to relative URLs. + * @return void + */ + public function testBaseUrlWithRelativePath(): void + { + $client = new Client(); + $client->setBaseUrl('http://localhost:8000'); + + $resp = $client->fetch( + url: '', + method: Client::METHOD_GET + ); + + if ($resp->getStatusCode() === 200) { + $respData = $resp->json(); + $this->assertEquals($respData['method'], Client::METHOD_GET); + $this->assertEquals($respData['url'], 'localhost:8000'); + } else { + echo "Please configure your PHP inbuilt SERVER"; + } + } + + /** + * Test base URL with path appending. + * @return void + */ + public function testBaseUrlWithPath(): void + { + $client = new Client(); + $client->setBaseUrl('http://localhost:8000'); + + $resp = $client->fetch( + url: 'redirect', + method: Client::METHOD_GET + ); + + if ($resp->getStatusCode() === 200) { + $respData = $resp->json(); + $this->assertEquals($respData['page'], "redirectedPage"); + } else { + echo "Please configure your PHP inbuilt SERVER"; + } + } + + /** + * Test base URL doesn't interfere with absolute URLs. + * @return void + */ + public function testBaseUrlWithAbsoluteUrl(): void + { + $client = new Client(); + $client->setBaseUrl('http://example.com'); + + $resp = $client->fetch( + url: 'http://localhost:8000', + method: Client::METHOD_GET + ); + + if ($resp->getStatusCode() === 200) { + $respData = $resp->json(); + $this->assertEquals($respData['method'], Client::METHOD_GET); + $this->assertEquals($respData['url'], 'localhost:8000'); + } else { + echo "Please configure your PHP inbuilt SERVER"; + } + } + + /** + * Test base URL with query parameters. + * @return void + */ + public function testBaseUrlWithQuery(): void + { + $client = new Client(); + $client->setBaseUrl('http://localhost:8000'); + + $resp = $client->fetch( + url: '', + method: Client::METHOD_GET, + query: [ + 'name' => 'John Doe', + 'age' => '30', + ] + ); + + if ($resp->getStatusCode() === 200) { + $respData = $resp->json(); + $this->assertEquals($respData['method'], Client::METHOD_GET); + $this->assertEquals( + json_encode($respData['query']), + json_encode(['name' => 'John Doe', 'age' => '30']) + ); + } else { + echo "Please configure your PHP inbuilt SERVER"; + } + } + + /** + * Test base URL with trailing slash normalization. + * @return void + */ + public function testBaseUrlTrailingSlashNormalization(): void + { + $client = new Client(); + + // Test with trailing slash in base URL + $client->setBaseUrl('http://localhost:8000/'); + $resp = $client->fetch( + url: '/redirect', + method: Client::METHOD_GET + ); + + if ($resp->getStatusCode() === 200) { + $respData = $resp->json(); + $this->assertEquals($respData['page'], "redirectedPage"); + } else { + echo "Please configure your PHP inbuilt SERVER"; + } + } + /** * Data provider for testFetch * @return array> From 2adc246a9e6f588be0bfb50693fc459eabb9b434 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 30 Mar 2026 03:28:59 +0000 Subject: [PATCH 5/6] Fix PHPStan errors: add type annotations for json() return values The json() method returns mixed, so array offset access requires explicit type annotations for PHPStan to accept the code. https://claude.ai/code/session_01YNnPHeCYQYWibVJ3m6uKht --- tests/ClientTest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/ClientTest.php b/tests/ClientTest.php index bbed7fa..c453a90 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -349,6 +349,7 @@ public function testBaseUrlWithRelativePath(): void ); if ($resp->getStatusCode() === 200) { + /** @var array $respData */ $respData = $resp->json(); $this->assertEquals($respData['method'], Client::METHOD_GET); $this->assertEquals($respData['url'], 'localhost:8000'); @@ -372,6 +373,7 @@ public function testBaseUrlWithPath(): void ); if ($resp->getStatusCode() === 200) { + /** @var array $respData */ $respData = $resp->json(); $this->assertEquals($respData['page'], "redirectedPage"); } else { @@ -394,6 +396,7 @@ public function testBaseUrlWithAbsoluteUrl(): void ); if ($resp->getStatusCode() === 200) { + /** @var array $respData */ $respData = $resp->json(); $this->assertEquals($respData['method'], Client::METHOD_GET); $this->assertEquals($respData['url'], 'localhost:8000'); @@ -421,6 +424,7 @@ public function testBaseUrlWithQuery(): void ); if ($resp->getStatusCode() === 200) { + /** @var array $respData */ $respData = $resp->json(); $this->assertEquals($respData['method'], Client::METHOD_GET); $this->assertEquals( @@ -448,6 +452,7 @@ public function testBaseUrlTrailingSlashNormalization(): void ); if ($resp->getStatusCode() === 200) { + /** @var array $respData */ $respData = $resp->json(); $this->assertEquals($respData['page'], "redirectedPage"); } else { From 6052db07290e8842c91f0f465abe52818630a8eb Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 30 Mar 2026 04:02:17 +0000 Subject: [PATCH 6/6] Replace preg_match with str_starts_with for URL scheme checks https://claude.ai/code/session_01Ya6TAJHW3vz2zhEbTZrbKt --- src/Adapter/Swoole.php | 2 +- src/Client.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Adapter/Swoole.php b/src/Adapter/Swoole.php index 627c431..65308ba 100644 --- a/src/Adapter/Swoole.php +++ b/src/Adapter/Swoole.php @@ -208,7 +208,7 @@ private function executeRequest( RequestOptions $options, ?callable $chunkCallback ): Response { - if (!preg_match('~^https?://~i', $url)) { + if (!str_starts_with($url, 'http://') && !str_starts_with($url, 'https://')) { $url = 'http://' . $url; } diff --git a/src/Client.php b/src/Client.php index 8fa47ad..ebbac89 100644 --- a/src/Client.php +++ b/src/Client.php @@ -302,7 +302,7 @@ public function fetch( ?int $timeoutMs = null, ?int $connectTimeoutMs = null, ): Response { - if (!empty($this->baseUrl) && !preg_match('~^https?://~i', $url)) { + if (!empty($this->baseUrl) && !str_starts_with($url, 'http://') && !str_starts_with($url, 'https://')) { $url = rtrim($this->baseUrl, '/') . '/' . ltrim($url, '/'); }