2 title: Tutorial -kinda- for pecl/http
8 I've added a tiny tutorial, or sort of collection of usage examples,
9 to the pecl/http repository.
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.
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.
22 $r = new HttpRequest('http://www.google.com');
24 // store Googles cookies in a dedicated file
26 array( 'cookiestore' => '../cookies/google.txt',
31 array( 'q' => '+"pecl_http" -msg -cvs -list',
36 // HttpRequest::send() returns an HttpMessage object
37 // of type HttpMessage::TYPE_RESPONSE or throws an exception
39 print $r->send()->getBody();
40 } catch (HttpException $e) {
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.
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
56 $r = new HttpRequest('http://dev.iworks.at/.print_request.php',
59 // if redirects is set to true, a single redirect is allowed;
60 // one can set any reasonable count of allowed redirects
62 array( 'cookies' => array('MyCookie' => 'has a value'),
69 array( 'name' => 'Mike',
70 'mail' => 'mike@php.net',
73 // add the file to post (form name, file name, file type)
74 $r->addPostFile('image', 'profile.jpg', 'image/jpeg');
77 print $r->send()->getBody();
78 } catch (HttpException $e) {
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
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',
97 $p->attach(new HttpRequest('http://pecl.php.net',
99 } catch (HttpException $e) {
106 // HttpRequestPool implements an iterator
107 // over attached HttpRequest objects
109 echo "Checking ", $r->getUrl(), " reported ",
110 $r->getResponseCode(), "\n";
112 } catch (HttpException $e) {
117 ### Parallel Requests?
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.
124 class Pool extends HttpRequestPool
126 public function __construct()
129 new HttpRequest('http://pear.php.net',
131 new HttpRequest('http://pecl.php.net',
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!");
151 foreach (new Pool as $r) {
152 echo "Checking ", $r->getUrl(), " reported ",
153 $r->getResponseCode(), "\n";
155 } catch (HttpException $ex) {
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.
169 HttpResponse::setCacheControl('public');
170 HttpResponse::setCache(true);
171 HttpResponse::capture();
173 print "This will be cached until content changes!\n";
174 print "Note that this approach will only save the clients download time.\n";
177 ## Bandwidth Throttling
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.
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();
193 ## KISS XMLRPC Client
201 public function __construct($url, $namespace = '')
203 $this->namespace = $namespace;
204 $this->request = new HttpRequest($url, HTTP_METH_POST);
205 $this->request->setContentType('text/xml');
208 public function setOptions($options = array())
210 return $this->request->setOptions($options);
213 public function addOptions($options)
215 return $this->request->addOptions($options);
218 public function __call($method, $params)
220 if ($this->namespace) {
221 $method = $this->namespace .'.'. $method;
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());
230 return xmlrpc_decode($response->getBody(), 'utf-8');
233 public function getHistory()
235 return $this->request->getHistory();
240 ## Simple Feed Aggregator
246 protected $feeds = array();
248 public function __construct($directory = 'feeds')
250 $this->setDirectory($directory);
253 public function setDirectory($directory)
255 $this->directory = $directory;
256 foreach (glob($this->directory .'/*.xml') as $feed) {
257 $this->feeds[basename($feed, '.xml')] = filemtime($feed);
261 public function url2name($url)
263 return preg_replace('/[^w.-]+/', '_', $url);
266 public function hasFeed($url)
268 return isset($this->feeds[$this->url2name($url)]);
271 public function addFeed($url)
273 $r = $this->setupRequest($url);
275 $this->handleResponse($r);
278 public function addFeeds($urls)
280 $pool = new HttpRequestPool;
281 foreach ($urls as $url) {
282 $pool->attach($r = $this->setupRequest($url));
286 foreach ($pool as $request) {
287 $this->handleResponse($request);
291 public function getFeed($url)
293 $this->addFeed($url);
294 return $this->loadFeed($this->url2name($url));
297 public function getFeeds($urls)
300 $this->addFeeds($urls);
301 foreach ($urls as $url) {
302 $feeds[] = $this->loadFeed($this->url2name($url));
307 protected function saveFeed($file, $contents)
309 if (file_put_contents($this->directory .'/'. $file .'.xml', $contents)) {
310 $this->feeds[$file] = time();
313 "Could not save feed contents to $file.xml");
317 protected function loadFeed($file)
319 if (isset($this->feeds[$file])) {
320 if ($data = file_get_contents($this->directory .'/'. $file .'.xml')) {
324 "Could not load feed contents from $file.xml");
327 throw new Exception("Unknown feed/file $file.xml");
331 protected function setupRequest($url)
333 $r = new HttpRequest($url);
334 $r->setOptions(array('redirect' => true));
336 $file = $this->url2name($url);
338 if (isset($this->feeds[$file])) {
339 $r->setOptions(array('lastmodified' => $this->feeds[$file]));
345 protected function handleResponse(HttpRequest $r)
347 if ($r->getResponseCode() != 304) {
348 if ($r->getResponseCode() != 200) {
349 throw new Exception("Unexpected response code ".
350 $r->getResponseCode());
352 if (!strlen($body = $r->getResponseBody())) {
353 throw new Exception("Received empty feed from ".
356 $this->saveFeed($this->url2name($r->getUrl()), $body);