Reader

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:

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:

$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:

$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:

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:

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 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:

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:

$ composer require psr/http-message

laminas-http

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

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:

$ 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:

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.