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
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ class YourApi extends Api
- [HTTP client (PSR-18) and HTTP factories (PSR-17)](#http-client-psr-18-and-http-factories-psr-17)
- [Cache (PSR-6)](#cache-psr-6)
- [Logger (PSR-3)](#logger-psr-3)
- [Configure options](#configure-options)

### Base URL

Expand Down Expand Up @@ -142,6 +141,9 @@ By default, this method will return a `string` as it will be the response of the
If you want to change how the response is handled in all requests (for example, decode a JSON string into an array),
check the [`addResponseContentsListener`](#addresponsecontentslistener) method in the [Event Listeners](#event-listeners) section.

> [!NOTE]
> If the `path` set is a full URL, it will be used as the request URL even if a `baseUrl` is set.

#### `buildPath`

The purpose of this method is to have an easy way to build a properly formatted path depending on the inputs or parameters you might have.
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
],
"require": {
"php": ">=8.1",
"nyholm/append-query-string": "^1.0",
"php-http/cache-plugin": "^2.0",
"php-http/client-common": "^2.7",
"php-http/discovery": "^1.20",
Expand Down
19 changes: 10 additions & 9 deletions src/Api.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ public function request(
$headers = array_merge($this->headerDefaults, $headers);
}

$uri = $this->buildUri($path, $query);
$request = $this->createRequest($method, $uri, $headers, $body);
$url = $this->buildUrl($path, $query);
$request = $this->createRequest($method, $url, $headers, $body);

// pre request listener
$request = $this->eventDispatcher->dispatch(new PreRequestEvent($request))->getRequest();
Expand Down Expand Up @@ -276,25 +276,26 @@ public function buildPath(string $path, array $parameters): string
return $path;
}

private function buildUri(string $path, array $query = []): string
private function buildUrl(string $path, array $query = []): string
{
$uri = StringHelper::reduceDuplicateSlashes($this->baseUrl . $path);
$appendQuery = http_build_query($query);

if (!empty($query)) {
$uri = sprintf('%s?%s', $uri, http_build_query($query));
if (StringHelper::isUrl($path)) {
return append_query_string($path, $appendQuery, APPEND_QUERY_STRING_REPLACE_DUPLICATE);
}

return $uri;
$url = StringHelper::reduceDuplicateSlashes($this->baseUrl . $path);
return append_query_string($url, $appendQuery, APPEND_QUERY_STRING_REPLACE_DUPLICATE);
}

private function createRequest(
string $method,
string $uri,
string $url,
array $headers = [],
string|StreamInterface $body = null
): RequestInterface
{
$request = $this->clientBuilder->getRequestFactory()->createRequest($method, $uri);
$request = $this->clientBuilder->getRequestFactory()->createRequest($method, $url);

foreach ($headers as $key => $value) {
$request = $request->withHeader($key, $value);
Expand Down
5 changes: 5 additions & 0 deletions src/Helper/StringHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,9 @@ public static function reduceDuplicateSlashes(string $string): string
{
return preg_replace('#(^|[^:])//+#', '\\1/', $string);
}

public static function isUrl(string $string): bool
{
return filter_var($string, FILTER_VALIDATE_URL) !== false;
}
}
26 changes: 26 additions & 0 deletions tests/Integration/ApiTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
use Http\Message\Authentication;
use Http\Mock\Client;
use Nyholm\Psr7\Response;
use PHPUnit\Framework\Attributes\DataProvider;
use ProgrammatorDev\Api\Api;
use ProgrammatorDev\Api\Builder\CacheBuilder;
use ProgrammatorDev\Api\Builder\ClientBuilder;
use ProgrammatorDev\Api\Builder\LoggerBuilder;
use ProgrammatorDev\Api\Event\PreRequestEvent;
use ProgrammatorDev\Api\Event\ResponseContentsEvent;
use ProgrammatorDev\Api\Test\AbstractTestCase;
use ProgrammatorDev\Api\Test\MockResponse;
Expand Down Expand Up @@ -192,6 +194,30 @@ public function testResponseContentsListener()
$this->assertIsArray($response);
}

#[DataProvider('provideBuildUrlData')]
public function testBuildUrl(?string $baseUrl, string $path, array $query, string $expectedUrl)
{
$this->api->addPreRequestListener(function(PreRequestEvent $event) use ($expectedUrl) {
$url = (string) $event->getRequest()->getUri();

$this->assertSame($expectedUrl, $url);
});

$this->api->setBaseUrl($baseUrl);
$this->api->request(method: 'GET', path: $path, query: $query);
}

public static function provideBuildUrlData(): \Generator
{
yield 'no base url' => [null, '/path', [], '/path'];
yield 'base url' => [self::BASE_URL, '/path', [], 'https://base.com/url/path'];
yield 'path full url' => [self::BASE_URL, 'https://fullurl.com/path', [], 'https://fullurl.com/path'];
yield 'duplicated slashes' => [self::BASE_URL, '////path', [], 'https://base.com/url/path'];
yield 'query' => [self::BASE_URL, '/path', ['foo' => 'bar'], 'https://base.com/url/path?foo=bar'];
yield 'path query' => [self::BASE_URL, '/path?test=true', ['foo' => 'bar'], 'https://base.com/url/path?test=true&foo=bar'];
yield 'query replace' => [self::BASE_URL, '/path?test=true', ['test' => 'false'], 'https://base.com/url/path?test=false'];
}

public function testBuildPath()
{
$path = $this->api->buildPath('/path/{parameter1}/multiple/{parameter2}', [
Expand Down