Recently I have been scratching my head over PSR-18. As a new PSR there isn’t a lot of information around it, but I saw that as a challenge. I decided to accept this challenge and build a compliant HTTP client to embrace the future of interoperability.
If you look at the specifications for PSR-18 on PHP-FIG they are typically pretty confusing, but I read between the lines a little. The first step was to build a class that would accept all the necessary components to bootstrap the client.
namespace JustSteveKing\HttpSlim;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Client\ClientExceptionInterface;
class HttpClient implements HttpClientInterface
{
protected ClientInterface $client;
protected StreamFactoryInterface $streamFactory;
protected RequestFactoryInterface $requestFactory;
}
Now if you have read any of my other posts, or used any of my packages, or seen my posts on twitter, you will know I am a huge fan of named constructors.
final protected function __construct(
ClientInterface $client,
RequestFactoryInterface $requestFactory,
StreamFactoryInterface $streamFactory
) {
$this->client = $client;
$this->requestFactory = $requestFactory;
$this->streamFactory = $streamFactory;
}
public static function build(
ClientInterface $client,
RequestFactoryInterface $requestFactory,
StreamFactoryInterface $streamFactory
): self {
return new static(
$client,
$requestFactory,
$streamFactory
);
}
This gives us a good starting point to begin with, from here we can implement our helper methods for making requests and encoding json. Our helper methods follow a typical pattern of, take a URL and an optional body as an array - making sure that we return a PSR-7 compliant Response (but that it typically down to the client library we inject). Here is an example of the post request:
use \JsonException;
use Psr\Http\Message\ResponseInterface;
use JustSteveKing\HttpSlim\Exceptions\RequestError;
public function post(string $uri, array $body): ResponseInterface
{
// build out json body content
try {
$content = $this->encodeJson($body);
} catch (JsonException $exception) {
throw RequestError::invalidJson($exception);
}
$request = $this->requestFactory
->createRequest('POST', $uri)
->withAddedHeader('Content-Type', 'application/json')
->withBody($this->streamFactory->createStream($content));
return $this->client->sendRequest($request);
}
Our encodeJson method simply returns a json encoded version of the passed through array.
This is just one adventure into embracing PSRs, but I have thoroughly enjoyed it so far! Please drop me a tweet if you have found this useful at all, or if you find an issue - feel free to drop an issue on the GitHub repo