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
47 changes: 18 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class YourApi extends Api
{
parent::__construct();

// minimum required config
// recommended config
$this->setBaseUrl('https://api.example.com/v1');
}

Expand Down Expand Up @@ -77,11 +77,11 @@ Getter and setter for the base URL.
Base URL is the common part of the API URL and will be used in all requests.

```php
$this->setBaseUrl(string $baseUrl): self
$this->setBaseUrl(?string $baseUrl): self
```

```php
$this->getBaseUrl(): string
$this->getBaseUrl(): ?string
```

### Requests
Expand All @@ -99,16 +99,12 @@ use Psr\Http\Message\StreamInterface;
$this->request(
string $method,
string $path,
array $query [],
array $query = [],
array $headers = [],
StreamInterface|string $body = null
): mixed
```

> [!NOTE]
> A `ConfigException` will be thrown if a base URL is not set (this is, if it is empty).
> Check the [`setBaseUrl`](#base-url) method for more information.

> [!NOTE]
> A `ClientException` will be thrown if there is an error while processing the request.

Expand All @@ -123,7 +119,7 @@ class YourApi extends Api
{
parent::__construct();

// minimum required config
// recommended config
$this->setBaseUrl('https://api.example.com/v1');
}

Expand Down Expand Up @@ -165,6 +161,7 @@ class YourApi extends Api
{
parent::__construct();

// recommended config
$this->setBaseUrl('https://api.example.com/v1');
}

Expand Down Expand Up @@ -316,18 +313,11 @@ use Http\Message\Authentication;
$this->getAuthentication(): ?Authentication;
```

Available authentication methods:
- [`BasicAuth`](https://docs.php-http.org/en/latest/message/authentication.html#id1) Username and password
- [`Bearer`](https://docs.php-http.org/en/latest/message/authentication.html#bearer) Token
- [`Wsse`](https://docs.php-http.org/en/latest/message/authentication.html#id2) Username and password
- [`QueryParam`](https://docs.php-http.org/en/latest/message/authentication.html#query-params) Array of query parameter values
- [`Header`](https://docs.php-http.org/en/latest/message/authentication.html#header) Header name and value
- [`Chain`](https://docs.php-http.org/en/latest/message/authentication.html#chain) Array of authentication instances
- `RequestConditional` A request matcher and authentication instances
Check all available authentication methods in the [PHP HTTP documentation](https://docs.php-http.org/en/latest/message/authentication.html#authentication-methods).

You can also [implement your own](https://docs.php-http.org/en/latest/message/authentication.html#implement-your-own) authentication method.

For example, if you have an API that is authenticated with a query parameter:
For example, if you have an API authenticated with a query parameter:

```php
use ProgrammatorDev\Api\Api;
Expand Down Expand Up @@ -367,7 +357,7 @@ class YourApi extends Api

#### `addPreRequestListener`

The `addPreRequestListener` method is used to add a function that is called before a request has been made.
The `addPreRequestListener` method is used to add a function called before a request has been made.
This event listener will be applied to every API request.

```php
Expand Down Expand Up @@ -413,7 +403,7 @@ $this->addPreRequestListener(function(PreRequestEvent $event) {

#### `addPostRequestListener`

The `addPostRequestListener` method is used to add a function that is called after a request has been made.
The `addPostRequestListener` method is used to add a function called after a request has been made.
This function can be used to inspect the request and response data that was sent to, and received from, the API.
This event listener will be applied to every API request.

Expand Down Expand Up @@ -468,7 +458,7 @@ $this->addPostRequestListener(function(PostRequestEvent $event) {

#### `addResponseContentsListener`

The `addResponseContentsListener` method is used to manipulate the response that was received from the API.
The `addResponseContentsListener` method is used to manipulate the response received from the API.
This event listener will be applied to every API request.

```php
Expand Down Expand Up @@ -648,7 +638,7 @@ class YourApi extends Api
This library enables attaching plugins to the HTTP client.
A plugin modifies the behavior of the client by intercepting the request and response flow.

Since plugin order matters, a plugin is added with a priority level, and are executed in descending order from highest to lowest.
Since plugin order matters, a plugin is added with a priority level and is executed in descending order from highest to lowest.

Check all the [available plugins](https://docs.php-http.org/en/latest/plugins/index.html) or [create your own](https://docs.php-http.org/en/latest/plugins/build-your-own.html).

Expand All @@ -673,7 +663,7 @@ The following list has all the implemented plugins with the respective priority
| [`LoggerPlugin`](https://docs.php-http.org/en/latest/plugins/logger.html) | 8 | only if logger is enabled |

For example, if you wanted the client to automatically attempt to re-send a request that failed
(due to unreliable connections and servers, for example) you can add the [RetryPlugin](https://docs.php-http.org/en/latest/plugins/retry.html):
(due to unreliable connections and servers, for example), you can add the [RetryPlugin](https://docs.php-http.org/en/latest/plugins/retry.html):

```php
use ProgrammatorDev\Api\Api;
Expand All @@ -686,7 +676,7 @@ class YourApi extends Api
// ...

// if a request fails, it will retry at least 3 times
// priority is 20 to execute before the cache plugin
// the priority is 20 to execute before the cache plugin
// (check the above plugin order list for more information)
$this->getClientBuilder()->addPlugin(
plugin: new RetryPlugin(['retries' => 3]),
Expand All @@ -709,12 +699,11 @@ use Psr\Cache\CacheItemPoolInterface;
new CacheBuilder(
// a PSR-6 cache adapter
CacheItemPoolInterface $pool,
// default lifetime (in seconds) of cache items
// default lifetime (in seconds) of cached items
?int $ttl = 60,
// An array of HTTP methods for which caching should be applied
$methods = ['GET', 'HEAD'],
// An array of cache directives to be compared with the headers of the HTTP response,
// in order to determine cacheability
// An array of cache directives to be compared with the headers of the HTTP response to determine cacheability
$responseCacheDirectives = ['max-age']
);
```
Expand Down Expand Up @@ -854,7 +843,7 @@ class YourApi extends Api

private function configureOptions(array $options): array
{
// set defaults values, if none were provided
// set defaults values if none were provided
$this->optionsResolver->setDefault('timezone', 'UTC');
$this->optionsResolver->setDefault('language', 'en');

Expand All @@ -872,7 +861,7 @@ class YourApi extends Api

private function configureApi(): void
{
// set required base url
// set the base url
$this->setBaseUrl('https://api.example.com/v1');

// set options as query defaults (will be included in all requests)
Expand Down
26 changes: 9 additions & 17 deletions src/Api.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
use ProgrammatorDev\Api\Event\PostRequestEvent;
use ProgrammatorDev\Api\Event\PreRequestEvent;
use ProgrammatorDev\Api\Event\ResponseContentsEvent;
use ProgrammatorDev\Api\Exception\ConfigException;
use ProgrammatorDev\Api\Helper\StringHelperTrait;
use ProgrammatorDev\Api\Helper\StringHelper;
use Psr\Http\Client\ClientExceptionInterface as ClientException;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\StreamInterface;
Expand All @@ -25,8 +24,6 @@

class Api
{
use StringHelperTrait;

private ?string $baseUrl = null;

private array $queryDefaults = [];
Expand All @@ -53,7 +50,6 @@ public function __construct()
}

/**
* @throws ConfigException If a base URL has not been set.
* @throws ClientException
*/
public function request(
Expand All @@ -64,18 +60,14 @@ public function request(
string|StreamInterface $body = null
): mixed
{
if (!$this->baseUrl) {
throw new ConfigException('A base URL must be set.');
}

$this->configurePlugins();

if (!empty($this->queryDefaults)) {
$query = \array_merge($this->queryDefaults, $query);
$query = array_merge($this->queryDefaults, $query);
}

if (!empty($this->headerDefaults)) {
$headers = \array_merge($this->headerDefaults, $headers);
$headers = array_merge($this->headerDefaults, $headers);
}

$uri = $this->buildUri($path, $query);
Expand Down Expand Up @@ -161,7 +153,7 @@ public function getBaseUrl(): ?string
return $this->baseUrl;
}

public function setBaseUrl(string $baseUrl): self
public function setBaseUrl(?string $baseUrl): self
{
$this->baseUrl = $baseUrl;

Expand Down Expand Up @@ -278,8 +270,8 @@ public function addResponseContentsListener(callable $listener, int $priority =
public function buildPath(string $path, array $parameters): string
{
foreach ($parameters as $parameter => $value) {
$path = \str_replace(
\sprintf('{%s}', $parameter),
$path = str_replace(
sprintf('{%s}', $parameter),
$value,
$path
);
Expand All @@ -290,10 +282,10 @@ public function buildPath(string $path, array $parameters): string

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

if (!empty($query)) {
$uri = \sprintf('%s?%s', $uri, \http_build_query($query));
$uri = sprintf('%s?%s', $uri, http_build_query($query));
}

return $uri;
Expand All @@ -314,7 +306,7 @@ private function createRequest(

if ($body !== null && $body !== '') {
$request = $request->withBody(
\is_string($body) ? $this->clientBuilder->getStreamFactory()->createStream($body) : $body
is_string($body) ? $this->clientBuilder->getStreamFactory()->createStream($body) : $body
);
}

Expand Down
4 changes: 2 additions & 2 deletions src/Builder/ClientBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,13 @@ public function addPlugin(Plugin $plugin, int $priority): self
{
if (isset($this->plugins[$priority])) {
throw new PluginException(
\sprintf('A plugin with priority %d already exists.', $priority)
sprintf('A plugin with priority %d already exists.', $priority)
);
}

$this->plugins[$priority] = $plugin;
// sort plugins by priority (key) in descending order
\krsort($this->plugins);
krsort($this->plugins);

return $this;
}
Expand Down
6 changes: 3 additions & 3 deletions src/Builder/Listener/CacheLoggerListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public function onCacheResponse(
if ($fromCache) {
/** @var $cacheItem CacheItemInterface */
$logger->info(
\sprintf("Cache hit:\n%s", $formatter->formatRequest($request)),
sprintf("Cache hit:\n%s", $formatter->formatRequest($request)),
[
'expires' => $cacheItem->get()['expiresAt'],
'key' => $cacheItem->getKey()
Expand All @@ -36,12 +36,12 @@ public function onCacheResponse(
// if response is a cache miss (and was cached)
else if ($cacheItem instanceof CacheItemInterface) {
// handle future deprecation
$formattedResponse = \method_exists($formatter, 'formatResponseForRequest')
$formattedResponse = method_exists($formatter, 'formatResponseForRequest')
? $formatter->formatResponseForRequest($response, $request)
: $formatter->formatResponse($response);

$logger->info(
\sprintf("Cached response:\n%s", $formattedResponse),
sprintf("Cached response:\n%s", $formattedResponse),
[
'expires' => $cacheItem->get()['expiresAt'],
'key' => $cacheItem->getKey()
Expand Down
5 changes: 0 additions & 5 deletions src/Exception/ConfigException.php

This file was deleted.

11 changes: 11 additions & 0 deletions src/Helper/StringHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace ProgrammatorDev\Api\Helper;

class StringHelper
{
public static function reduceDuplicateSlashes(string $string): string
{
return preg_replace('#(^|[^:])//+#', '\\1/', $string);
}
}
11 changes: 0 additions & 11 deletions src/Helper/StringHelperTrait.php

This file was deleted.

Loading