# HTTP Clients

Several operations in laminas-feed's Reader subcomponent require an HTTP client:

- importing a feed
- finding links in a feed

In order to allow developers a choice in HTTP clients, the subcomponent defines
several interfaces and classes. Elsewhere in the documentation, we reference
where an HTTP client may be used; this document details what constitutes an HTTP
client and its behavior, and some of the concrete classes available within the
component for implementing this behavior.

## ClientInterface and HeaderAwareClientInterface

First, we define two interfaces for clients,
`Laminas\Feed\Reader\Http\ClientInterface` and `HeaderAwareClientInterface`:

```php
namespace Laminas\Feed\Reader\Http;

interface ClientInterface
{
    /**
     * Make a GET request to a given URL.
     *
     * @param string $url
     * @return ResponseInterface
     */
    public function get($url);
}

interface HeaderAwareClientInterface extends ClientInterface
{
    /**
     * Make a GET request to a given URL.
     *
     * @param string $url
     * @param array $headers
     * @return ResponseInterface
     */
    public function get($url, array $headers = []);
}
```

The first is header-agnostic, and assumes that the client will simply perform an
HTTP GET request. The second allows providing headers to the client; typically,
these are used for HTTP caching headers. `$headers` must be in the following
structure:

```php
$headers = [
    'X-Header-Name' => [
        'header',
        'values',
    ],
];
```

i.e., each key is a header name, and each value is an array of values for that
header. If the header represents only a single value, it should be an array with
that value:

```php
$headers = [
    'Accept' => [ 'application/rss+xml' ],
];
```

A call to `get()` should yield a *response*.

## ResponseInterface and HeaderAwareResponseInterface

Responses are modeled using `Laminas\Feed\Reader\Http\ResponseInterface` and
`HeaderAwareResponseInterface`:

```php
namespace Laminas\Feed\Reader\Http;

class ResponseInterface
{
    /**
     * Retrieve the status code.
     *
     * @return int
     */
    public function getStatusCode();

    /**
     * Retrieve the response body contents.
     *
     * @return string
     */
    public function getBody();
}

class HeaderAwareResponseInterface extends ResponseInterface
{
    /**
     * Retrieve a named header line.
     *
     * Retrieve a header by name; all values MUST be concatenated to a single
     * line. If no matching header is found, return the $default value.
     *
     * @param string $name
     * @param null|string $default
     * @return string
     */
    public function getHeaderLine($name, $default = null);
}
```

Internally, `Reader` will typehint against `ClientInterface` for the bulk of
operations. In some cases, however, certain capabilities are only possible if
the response can provide headers (e.g., for caching); in such cases, it will
check the instance against `HeaderAwareResponseInterface`, and only call
`getHeaderLine()` if it matches.

## Response

laminas-feed ships with a generic `ResponseInterface` implementation,
`Laminas\Feed\Http\Response`. It implements `HeaderAwareResponseInterface`, and
defines the following constructor:

```php
namespace Laminas\Feed\Reader\Http;

class Response implements HeaderAwareResponseInterface
{
    /**
     * @param int $statusCode Response status code
     * @param string $body Response body
     * @param array $headers Response headers, if available
     */
    public function __construct($statusCode, $body, array $headers = []);
}
```

## PSR-7 Response

[PSR-7](http://www.php-fig.org/psr/psr-7/) defines a set of HTTP message
interfaces, but not a client interface. To facilitate wrapping an HTTP client
that uses PSR-7 messages, we provide `Laminas\Feed\Reader\Psr7ResponseDecorator`:

```php
namespace Laminas\Feed\Reader\Http;

use Psr\Http\Message\ResponseInterface as PsrResponseInterface;

class Psr7ResponseDecorator implements HeaderAwareResponseInterface
{
    /**
     * @param PsrResponseInterface $response
     */
    public function __construct(PsrResponseInterface $response);

    /**
     * @return PsrResponseInterface
     */
    public function getDecoratedResponse();
}
```

Clients can then take the PSR-7 response they receive, pass it to the decorator,
and return the decorator.

To use the PSR-7 response, you will need to add the PSR-7 interfaces to your
application, if they are not already installed by the client of your choice:

```bash
$ composer require psr/http-message
```

## laminas-http

We also provide a laminas-http client decorator,
`Laminas\Feed\Reader\Http\LaminasHttpClientDecorator`:

```php
namespace Laminas\Feed\Reader\Http;

use Laminas\Http\Client as HttpClient;

class LaminasHttpClientDecorator implements HeaderAwareClientInterface
{
    /**
     * @param HttpClient $client
     */
    public function __construct(HttpClient $client);

    /**
     * @return HttpClient
     */
    public function getDecoratedClient();
}
```

Its `get()` implementation returns a `Response` instance seeded from the
laminas-http response returned, including status, body, and headers.

laminas-http is the default implementation assumed by `Laminas\Feed\Reader\Reader`,
but *is not installed by default*. You may install it using composer:

```bash
$ composer require laminas/laminas-http
```

## Providing a client to Reader

By default, `Laminas\Feed\Reader\Reader` will lazy load a laminas-http client. If you
have not installed laminas-http, however, PHP will raise an error indicating the
class is not found!

As such, you have two options:

1. Install laminas-http: `composer require laminas/laminas-http`.
2. Inject the `Reader` with your own HTTP client.

To accomplish the second, you will need an implementation of
`Laminas\Feed\Reader\Http\ClientInterface` or `HeaderAwareClientInterface`, and an
instance of that implementation. Once you do, you can use the static method
`setHttpClient()` to inject it.

As an example, let's say you've created a PSR-7-based implementation named
`My\Http\Psr7FeedClient`. You could then do the following:

```php
use My\Http\Psr7FeedClient;
use Laminas\Feed\Reader\Reader;

Reader::setHttpClient(new Psr7FeedClient());
```

Your client will then be used for all `import()` and `findFeedLinks()`
operations.
