Guzzle uses adapters to send HTTP requests. Adapters emit the lifecycle events of requests, transfer HTTP requests, and normalize error handling.
Guzzle will use the best possible adapter based on your environment.
If cURL is present, Guzzle will use the following adapters by default:
If cURL is not installed, then Guzzle will use a GuzzleHttp\Adapter\StreamingAdapter to send requests through PHP's HTTP stream wrapper. allow_url_fopen must be enabled if cURL is not installed on your system.
Creating a custom HTTP adapter allows you to completely customize the way an HTTP request is sent over the wire. In some cases, you might need to use a different mechanism for transferring HTTP requests other than cURL or PHP's stream wrapper. For example, you might need to use a socket because the version of cURL on your system has an old bug, maybe you'd like to implement future response objects, or you want to create a thread pool and send parallel requests using pthreads.
The first thing you need to know about implementing custom adapters are the responsibilities of an adapter.
Adapters use a GuzzleHttp\Adapter\TransactionInterface which acts as a mediator between GuzzleHttp\Message\RequestInterface and GuzzleHttp\Message\ResponseInterface objects. The main goal of an adapter is to set a response on the provided transaction object.
Parallel adapters are used when using a client's sendAll() method. Parallel adapters are expected to send one or more transactions in parallel. Parallel adapters accept an \Iterator that yields GuzzleHttp\Adapter\TransactionInterface object. In addition to the iterator, the adapter is also provided an integer representing the number of transactions to execute in parallel.
Parallel adapters are similar to adapters (described earlier), except for the following:
Request lifecycle events MUST be emitted by adapters and parallel adapters. These lifecycle events are used by event listeners to modify requests, modify responses, perform validation, and anything else required by an application.
Emitting request lifecycle events in an adapter is much simpler if you use the static helper method of GuzzleHttp\Event\RequestEvents. These methods are used by the built-in in curl and stream wrapper adapters of Guzzle, so you should use them too.
Here's a really simple example of creating a custom HTTP adapter. For simplicity, this example uses a magic send_request() function.
<?php
namespace MyProject\Adapter;
use GuzzleHttp\Event\RequestEvents;
use GuzzleHttp\Event\HeadersEvent;
use GuzzleHttp\Message\MessageFactoryInterface;
class MyAdapter implements AdapterInterface
{
private $messageFactory;
public function __construct(MessageFactoryInterface $messageFactory)
{
$this->messageFactory = $messageFactory;
}
public function send(TransactionInterface $transaction)
{
RequestEvents::emitBefore($transaction);
// Check if the transaction was intercepted
if (!$transaction->getResponse()) {
// It wasn't intercepted, so send the request
$this->getResponse($transaction);
}
// Adapters always return a response in the successful case.
return $transaction->getResponse();
}
private function getResponse(TransactionInterface $transaction)
{
$request = $transaction->getRequest();
$response = send_request(
$request->getMethod(),
$request->getUrl(),
$request->getHeaders(),
$request->getBody()
);
if ($response) {
$this->processResponse($response, $transaction);
} else {
// Emit the error event which allows listeners to intercept
// the error with a valid response. If it is not intercepted,
// a RequestException is thrown.
RequestEvents::emitError($transaction, $e);
}
}
private function processResponse(
array $response,
TransactionInterface $transaction
) {
// Process the response, create a Guzzle Response object, and
// associate the response with the transaction.
$responseObject = $this->messageFactory->createResponse(
$response['status_code'],
$response['headers']
);
$transaction->setResponse($responseObject);
// Emit the headers event before downloading the body
RequestEvents::emitHeaders($transaction);
if ($response['body']) {
// Assuming the response body is a stream or something,
// associate it with the response object.
$responseObject->setBody(Stream::factory($response['body']));
}
// Emit the complete event
RequestEvents::emitComplete($transaction);
}
}