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