Update sync_site.yml
[m6w6/m6w6.github.io] / _posts / 2005-09-12-tutorial-kinda-for-peclhttp.md
1 ---
2 title: Tutorial -kinda- for pecl/http
3 author: m6w6
4 tags:
5 - PHP
6 ---
7
8 I've added a tiny tutorial, or sort of collection of usage examples,
9 to the pecl/http repository.
10
11 You can read it in the extended post, or just check it out from CVS.
12 Please note that the code for the XMLRPC client has just been put into CVS.
13
14 ## GET Queries
15
16 The HttpRequest class can be used to execute any HTTP request method.
17 The following example shows a simple GET request where a few query
18 parameters are supplied. Additionally potential cookies will be read
19 from and written to a file.
20
21 ```php
22 $r = new HttpRequest('http://www.google.com');
23
24 // store Googles cookies in a dedicated file
25 $r->setOptions(
26 array( 'cookiestore' => '../cookies/google.txt',
27 )
28 );
29
30 $r->setQueryData(
31 array( 'q' => '+"pecl_http" -msg -cvs -list',
32 'hl' => 'de'
33 )
34 );
35
36 // HttpRequest::send() returns an HttpMessage object
37 // of type HttpMessage::TYPE_RESPONSE or throws an exception
38 try {
39 print $r->send()->getBody();
40 } catch (HttpException $e) {
41 print $e;
42 }
43 ```
44
45 ## Multipart Posts
46
47
48 The following example shows an multipart POST request, with two form
49 fields and an image that's supposed to be uploaded to the server.
50
51 It's a bad habit as well as common practice to issue a redirect after
52 an received POST request, so we'll allow a redirect by enabling the
53 redirect option.
54
55 ```php
56 $r = new HttpRequest('http://dev.iworks.at/.print_request.php',
57 HTTP_METH_POST);
58
59 // if redirects is set to true, a single redirect is allowed;
60 // one can set any reasonable count of allowed redirects
61 $r->setOptions(
62 array( 'cookies' => array('MyCookie' => 'has a value'),
63 'redirect' => true,
64 )
65 );
66
67 // common form data
68 $r->setPostFields(
69 array( 'name' => 'Mike',
70 'mail' => 'mike@php.net',
71 )
72 );
73 // add the file to post (form name, file name, file type)
74 $r->addPostFile('image', 'profile.jpg', 'image/jpeg');
75
76 try {
77 print $r->send()->getBody();
78 } catch (HttpException $e) {
79 print $e;
80 }
81 ```
82
83 ## Parallel Requests
84
85 It's possible to execute several HttpRequests in parallel with the
86 HttpRequestPool class. HttpRequests to send, do not need to perform
87 the same request method, but can only be attached to one HttpRequestPool
88 at the same time.
89
90 ```php
91 try {
92 $p = new HttpRequestPool;
93 // if you want to set _any_ options of the HttpRequest object,
94 // you need to do so *prior attaching* to the request pool!
95 $p->attach(new HttpRequest('http://pear.php.net',
96 HTTP_METH_HEAD));
97 $p->attach(new HttpRequest('http://pecl.php.net',
98 HTTP_METH_HEAD));
99 } catch (HttpException $e) {
100 print $e;
101 exit;
102 }
103
104 try {
105 $p->send();
106 // HttpRequestPool implements an iterator
107 // over attached HttpRequest objects
108 foreach ($p as $r) {
109 echo "Checking ", $r->getUrl(), " reported ",
110 $r->getResponseCode(), "\n";
111 }
112 } catch (HttpException $e) {
113 print $e;
114 }
115 ```
116
117 ### Parallel Requests?
118
119 You can use a more advanced approach by using the protected interface of
120 the HttpRequestPool class. This allows you to perform some other tasks
121 while the requests are executed.
122
123 ```php
124 class Pool extends HttpRequestPool
125 {
126 public function __construct()
127 {
128 parent::__construct(
129 new HttpRequest('http://pear.php.net',
130 HTTP_METH_HEAD),
131 new HttpRequest('http://pecl.php.net',
132 HTTP_METH_HEAD)
133 );
134
135 // HttpRequestPool methods socketPerform() and
136 // socketSelect() are protected; one could use
137 // this approach to do something else
138 // while the requests are being executed
139 print "Executing requests";
140 for ($i = 0; $this->socketPerform(); $i++) {
141 $i % 10 or print ".";
142 if (!$this->socketSelect()) {
143 throw new HttpException("Socket error!");
144 }
145 }
146 print "nDone!n";
147 }
148 }
149
150 try {
151 foreach (new Pool as $r) {
152 echo "Checking ", $r->getUrl(), " reported ",
153 $r->getResponseCode(), "\n";
154 }
155 } catch (HttpException $ex) {
156 print $e;
157 }
158 ```
159
160 ## Cached Responses
161
162 One of the main key features of HttpResponse is HTTP caching. HttpResponse
163 will calculate an ETag based on the http.etag_mode INI setting as well as
164 it will determine the last modification time of the sent entity. It uses
165 those two indicators to decide if the cache entry on the client side is
166 still valid and will emit an "304 Not Modified" response if applicable.
167
168 ```php
169 HttpResponse::setCacheControl('public');
170 HttpResponse::setCache(true);
171 HttpResponse::capture();
172
173 print "This will be cached until content changes!\n";
174 print "Note that this approach will only save the clients download time.\n";
175 ```
176
177 ## Bandwidth Throttling
178
179 HttpResponse supports a basic throttling mechanism, which is enabled by
180 setting a throttle delay and a buffer size. PHP will sleep the specified
181 amount of seconds after each sent chunk of specified bytes.
182
183 ```php
184 // send 5000 bytes every 0.2 seconds, i.e. max ~25kByte/s
185 HttpResponse::setThrottleDelay(0.2);
186 HttpResponse::setBufferSize(5000);
187 HttpResponse::setCache(true);
188 HttpResponse::setContentType('application/x-zip');
189 HttpResponse::setFile('../archive.zip');
190 HttpResponse::send();
191 ```
192
193 ## KISS XMLRPC Client
194
195 ```php
196 class XmlRpcClient
197 {
198 public $namespace;
199 protected $request;
200
201 public function __construct($url, $namespace = '')
202 {
203 $this->namespace = $namespace;
204 $this->request = new HttpRequest($url, HTTP_METH_POST);
205 $this->request->setContentType('text/xml');
206 }
207
208 public function setOptions($options = array())
209 {
210 return $this->request->setOptions($options);
211 }
212
213 public function addOptions($options)
214 {
215 return $this->request->addOptions($options);
216 }
217
218 public function __call($method, $params)
219 {
220 if ($this->namespace) {
221 $method = $this->namespace .'.'. $method;
222 }
223 $this->request->setRawPostData(
224 xmlrpc_encode_request($method, $params));
225 $response = $this->request->send();
226 if ($response->getResponseCode() != 200) {
227 throw new Exception($response->getBody(),
228 $response->getResponseCode());
229 }
230 return xmlrpc_decode($response->getBody(), 'utf-8');
231 }
232
233 public function getHistory()
234 {
235 return $this->request->getHistory();
236 }
237 }
238 ```
239
240 ## Simple Feed Aggregator
241
242 ```php
243 class FeedAggregator
244 {
245 public $directory;
246 protected $feeds = array();
247
248 public function __construct($directory = 'feeds')
249 {
250 $this->setDirectory($directory);
251 }
252
253 public function setDirectory($directory)
254 {
255 $this->directory = $directory;
256 foreach (glob($this->directory .'/*.xml') as $feed) {
257 $this->feeds[basename($feed, '.xml')] = filemtime($feed);
258 }
259 }
260
261 public function url2name($url)
262 {
263 return preg_replace('/[^w.-]+/', '_', $url);
264 }
265
266 public function hasFeed($url)
267 {
268 return isset($this->feeds[$this->url2name($url)]);
269 }
270
271 public function addFeed($url)
272 {
273 $r = $this->setupRequest($url);
274 $r->send();
275 $this->handleResponse($r);
276 }
277
278 public function addFeeds($urls)
279 {
280 $pool = new HttpRequestPool;
281 foreach ($urls as $url) {
282 $pool->attach($r = $this->setupRequest($url));
283 }
284 $pool->send();
285
286 foreach ($pool as $request) {
287 $this->handleResponse($request);
288 }
289 }
290
291 public function getFeed($url)
292 {
293 $this->addFeed($url);
294 return $this->loadFeed($this->url2name($url));
295 }
296
297 public function getFeeds($urls)
298 {
299 $feeds = array();
300 $this->addFeeds($urls);
301 foreach ($urls as $url) {
302 $feeds[] = $this->loadFeed($this->url2name($url));
303 }
304 return $feeds;
305 }
306
307 protected function saveFeed($file, $contents)
308 {
309 if (file_put_contents($this->directory .'/'. $file .'.xml', $contents)) {
310 $this->feeds[$file] = time();
311 } else {
312 throw new Exception(
313 "Could not save feed contents to $file.xml");
314 }
315 }
316
317 protected function loadFeed($file)
318 {
319 if (isset($this->feeds[$file])) {
320 if ($data = file_get_contents($this->directory .'/'. $file .'.xml')) {
321 return $data;
322 } else {
323 throw new Exception(
324 "Could not load feed contents from $file.xml");
325 }
326 } else {
327 throw new Exception("Unknown feed/file $file.xml");
328 }
329 }
330
331 protected function setupRequest($url)
332 {
333 $r = new HttpRequest($url);
334 $r->setOptions(array('redirect' => true));
335
336 $file = $this->url2name($url);
337
338 if (isset($this->feeds[$file])) {
339 $r->setOptions(array('lastmodified' => $this->feeds[$file]));
340 }
341
342 return $r;
343 }
344
345 protected function handleResponse(HttpRequest $r)
346 {
347 if ($r->getResponseCode() != 304) {
348 if ($r->getResponseCode() != 200) {
349 throw new Exception("Unexpected response code ".
350 $r->getResponseCode());
351 }
352 if (!strlen($body = $r->getResponseBody())) {
353 throw new Exception("Received empty feed from ".
354 $r->getUrl());
355 }
356 $this->saveFeed($this->url2name($r->getUrl()), $body);
357 }
358 }
359 }
360 ```