annotate vendor/zendframework/zend-feed/doc/book/http-clients.md @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children
rev   line source
Chris@0 1 # HTTP Clients and zend-feed
Chris@0 2
Chris@0 3 Several operations in zend-feed's Reader subcomponent require an HTTP client:
Chris@0 4
Chris@0 5 - importing a feed
Chris@0 6 - finding links in a feed
Chris@0 7
Chris@0 8 In order to allow developers a choice in HTTP clients, the subcomponent defines
Chris@0 9 several interfaces and classes. Elsewhere in the documentation, we reference
Chris@0 10 where an HTTP client may be used; this document details what constitutes an HTTP
Chris@0 11 client and its behavior, and some of the concrete classes available within the
Chris@0 12 component for implementing this behavior.
Chris@0 13
Chris@0 14 ## ClientInterface and HeaderAwareClientInterface
Chris@0 15
Chris@0 16 First, we define two interfaces for clients,
Chris@0 17 `Zend\Feed\Reader\Http\ClientInterface` and `HeaderAwareClientInterface`:
Chris@0 18
Chris@0 19 ```php
Chris@0 20 namespace Zend\Feed\Reader\Http;
Chris@0 21
Chris@0 22 interface ClientInterface
Chris@0 23 {
Chris@0 24 /**
Chris@0 25 * Make a GET request to a given URL.
Chris@0 26 *
Chris@0 27 * @param string $url
Chris@0 28 * @return ResponseInterface
Chris@0 29 */
Chris@0 30 public function get($url);
Chris@0 31 }
Chris@0 32
Chris@0 33 interface HeaderAwareClientInterface extends ClientInterface
Chris@0 34 {
Chris@0 35 /**
Chris@0 36 * Make a GET request to a given URL.
Chris@0 37 *
Chris@0 38 * @param string $url
Chris@0 39 * @param array $headers
Chris@0 40 * @return ResponseInterface
Chris@0 41 */
Chris@0 42 public function get($url, array $headers = []);
Chris@0 43 }
Chris@0 44 ```
Chris@0 45
Chris@0 46 The first is header-agnostic, and assumes that the client will simply perform an
Chris@0 47 HTTP GET request. The second allows providing headers to the client; typically,
Chris@0 48 these are used for HTTP caching headers. `$headers` must be in the following
Chris@0 49 structure:
Chris@0 50
Chris@0 51 ```php
Chris@0 52 $headers = [
Chris@0 53 'X-Header-Name' => [
Chris@0 54 'header',
Chris@0 55 'values',
Chris@0 56 ],
Chris@0 57 ];
Chris@0 58 ```
Chris@0 59
Chris@0 60 i.e., each key is a header name, and each value is an array of values for that
Chris@0 61 header. If the header represents only a single value, it should be an array with
Chris@0 62 that value:
Chris@0 63
Chris@0 64 ```php
Chris@0 65 $headers = [
Chris@0 66 'Accept' => [ 'application/rss+xml' ],
Chris@0 67 ];
Chris@0 68 ```
Chris@0 69
Chris@0 70 A call to `get()` should yield a *response*.
Chris@0 71
Chris@0 72 ## ResponseInterface and HeaderAwareResponseInterface
Chris@0 73
Chris@0 74 Responses are modeled using `Zend\Feed\Reader\Http\ResponseInterface` and
Chris@0 75 `HeaderAwareResponseInterface`:
Chris@0 76
Chris@0 77 ```php
Chris@0 78 namespace Zend\Feed\Reader\Http;
Chris@0 79
Chris@0 80 class ResponseInterface
Chris@0 81 {
Chris@0 82 /**
Chris@0 83 * Retrieve the status code.
Chris@0 84 *
Chris@0 85 * @return int
Chris@0 86 */
Chris@0 87 public function getStatusCode();
Chris@0 88
Chris@0 89 /**
Chris@0 90 * Retrieve the response body contents.
Chris@0 91 *
Chris@0 92 * @return string
Chris@0 93 */
Chris@0 94 public function getBody();
Chris@0 95 }
Chris@0 96
Chris@0 97 class HeaderAwareResponseInterface extends ResponseInterface
Chris@0 98 {
Chris@0 99 /**
Chris@0 100 * Retrieve a named header line.
Chris@0 101 *
Chris@0 102 * Retrieve a header by name; all values MUST be concatenated to a single
Chris@0 103 * line. If no matching header is found, return the $default value.
Chris@0 104 *
Chris@0 105 * @param string $name
Chris@0 106 * @param null|string $default
Chris@0 107 * @return string
Chris@0 108 public function getHeaderLine($name, $default = null);
Chris@0 109 }
Chris@0 110 ```
Chris@0 111
Chris@0 112 Internally, `Reader` will typehint against `ClientInterface` for the bulk of
Chris@0 113 operations. In some cases, however, certain capabilities are only possible if
Chris@0 114 the response can provide headers (e.g., for caching); in such cases, it will
Chris@0 115 check the instance against `HeaderAwareResponseInterface`, and only call
Chris@0 116 `getHeaderLine()` if it matches.
Chris@0 117
Chris@0 118 ## Response
Chris@0 119
Chris@0 120 zend-feed ships with a generic `ResponseInterface` implementation,
Chris@0 121 `Zend\Feed\Http\Response`. It implements `HeaderAwareResponseInterface`, and
Chris@0 122 defines the following constructor:
Chris@0 123
Chris@0 124 ```php
Chris@0 125 namespace Zend\Feed\Reader\Http;
Chris@0 126
Chris@0 127 class Response implements HeaderAwareResponseInterface
Chris@0 128 {
Chris@0 129 /**
Chris@0 130 * Constructor
Chris@0 131 *
Chris@0 132 * @param int $statusCode Response status code
Chris@0 133 * @param string $body Response body
Chris@0 134 * @param array $headers Response headers, if available
Chris@0 135 */
Chris@0 136 public function __construct($statusCode, $body, array $headers = []);
Chris@0 137 }
Chris@0 138 ```
Chris@0 139
Chris@0 140 ## PSR-7 Response
Chris@0 141
Chris@0 142 [PSR-7](http://www.php-fig.org/psr/psr-7/) defines a set of HTTP message
Chris@0 143 interfaces, but not a client interface. To facilitate wrapping an HTTP client
Chris@0 144 that uses PSR-7 messages, we provide `Zend\Feed\Reader\Psr7ResponseDecorator`:
Chris@0 145
Chris@0 146 ```php
Chris@0 147 namespace Zend\Feed\Reader\Http;
Chris@0 148
Chris@0 149 use Psr\Http\Message\ResponseInterface as PsrResponseInterface;
Chris@0 150
Chris@0 151 class Psr7ResponseDecorator implements HeaderAwareResponseInterface
Chris@0 152 {
Chris@0 153 /**
Chris@0 154 * @param PsrResponseInterface $response
Chris@0 155 */
Chris@0 156 public function __construct(PsrResponseInterface $response);
Chris@0 157
Chris@0 158 /**
Chris@0 159 * @return PsrResponseInterface
Chris@0 160 */
Chris@0 161 public function getDecoratedResponse();
Chris@0 162 }
Chris@0 163 ```
Chris@0 164
Chris@0 165 Clients can then take the PSR-7 response they receive, pass it to the decorator,
Chris@0 166 and return the decorator.
Chris@0 167
Chris@0 168 To use the PSR-7 response, you will need to add the PSR-7 interfaces to your
Chris@0 169 application, if they are not already installed by the client of your choice:
Chris@0 170
Chris@0 171 ```bash
Chris@0 172 $ composer require psr/http-message
Chris@0 173 ```
Chris@0 174
Chris@0 175 ## zend-http
Chris@0 176
Chris@0 177 We also provide a zend-http client decorator,
Chris@0 178 `Zend\Feed\Reader\Http\ZendHttpClientDecorator`:
Chris@0 179
Chris@0 180 ```php
Chris@0 181 namespace Zend\Feed\Reader\Http;
Chris@0 182
Chris@0 183 use Zend\Http\Client as HttpClient;
Chris@0 184
Chris@0 185 class ZendHttpClientDecorator implements HeaderAwareClientInterface
Chris@0 186 {
Chris@0 187 /**
Chris@0 188 * @param HttpClient $client
Chris@0 189 */
Chris@0 190 public function __construct(HttpClient $client);
Chris@0 191
Chris@0 192 /**
Chris@0 193 * @return HttpClient
Chris@0 194 */
Chris@0 195 public function getDecoratedClient();
Chris@0 196 }
Chris@0 197 ```
Chris@0 198
Chris@0 199 Its `get()` implementation returns a `Response` instance seeded from the
Chris@0 200 zend-http response returned, including status, body, and headers.
Chris@0 201
Chris@0 202 zend-http is the default implementation assumed by `Zend\Feed\Reader\Reader`,
Chris@0 203 but *is not installed by default*. You may install it using composer:
Chris@0 204
Chris@0 205 ```bash
Chris@0 206 $ composer require zendframework/zend-http
Chris@0 207 ```
Chris@0 208
Chris@0 209 ## Providing a client to Reader
Chris@0 210
Chris@0 211 By default, `Zend\Feed\Reader\Reader` will lazy load a zend-http client. If you
Chris@0 212 have not installed zend-http, however, PHP will raise an error indicating the
Chris@0 213 class is not found!
Chris@0 214
Chris@0 215 As such, you have two options:
Chris@0 216
Chris@0 217 1. Install zend-http: `composer require zendframework/zend-http`.
Chris@0 218 2. Inject the `Reader` with your own HTTP client.
Chris@0 219
Chris@0 220 To accomplish the second, you will need an implementation of
Chris@0 221 `Zend\Feed\Reader\Http\ClientInterface` or `HeaderAwareClientInterface`, and an
Chris@0 222 instance of that implementation. Once you do, you can use the static method
Chris@0 223 `setHttpClient()` to inject it.
Chris@0 224
Chris@0 225 As an example, let's say you've created a PSR-7-based implementation named
Chris@0 226 `My\Http\Psr7FeedClient`. You could then do the following:
Chris@0 227
Chris@0 228 ```php
Chris@0 229 use My\Http\Psr7FeedClient;
Chris@0 230 use Zend\Feed\Reader\Reader;
Chris@0 231
Chris@0 232 Reader::setHttpClient(new Psr7FeedClient());
Chris@0 233 ```
Chris@0 234
Chris@0 235 Your client will then be used for all `import()` and `findFeedLinks()`
Chris@0 236 operations.