]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/HttpClient.php
allow most_popular sortby arguments
[SourceForge/phpwiki.git] / lib / HttpClient.php
1 <?php // -*-php-*-
2 rcs_id('$Id: HttpClient.php,v 1.5 2004-04-29 19:34:24 rurban Exp $');
3
4 /** 
5    Version 0.9, 6th April 2003 - Simon Willison ( http://simon.incutio.com/ )
6    Manual: http://scripts.incutio.com/httpclient/
7
8    Copyright © 2003 Incutio Limited
9    License: http://www.opensource.org/licenses/artistic-license.php
10 */
11
12 class HttpClient {
13     // Request vars
14     var $host;
15     var $port;
16     var $path;
17     var $method;
18     var $postdata = '';
19     var $cookies = array();
20     var $referer;
21     var $accept = 'text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,image/jpeg,image/gif,*/*';
22     var $accept_encoding = 'gzip';
23     var $accept_language = 'en-us';
24     var $user_agent = 'Incutio HttpClient v0.9';
25     // Options
26     var $timeout = 20;
27     var $use_gzip = true;
28     var $persist_cookies = true;  // If true, received cookies are placed in the $this->cookies array ready for the next request
29                                   // Note: This currently ignores the cookie path (and time) completely. Time is not important, 
30                                   //       but path could possibly lead to security problems.
31     var $persist_referers = true; // For each request, sends path of last request as referer
32     var $debug = false;
33     var $handle_redirects = true; // Auaomtically redirect if Location or URI header is found
34     var $max_redirects = 5;
35     var $headers_only = false;    // If true, stops receiving once headers have been read.
36     // Basic authorization variables
37     var $username;
38     var $password;
39     // Response vars
40     var $status;
41     var $headers = array();
42     var $content = '';
43     var $errormsg;
44     // Tracker variables
45     var $redirect_count = 0;
46     var $cookie_host = '';
47
48     function HttpClient($host, $port=80) {
49         $this->host = $host;
50         $this->port = $port;
51     }
52     function get($path, $data = false) {
53         $this->path = $path;
54         $this->method = 'GET';
55         if ($data) {
56             $this->path .= '?'.$this->buildQueryString($data);
57         }
58         return $this->doRequest();
59     }
60     function post($path, $data) {
61         $this->path = $path;
62         $this->method = 'POST';
63         $this->postdata = $this->buildQueryString($data);
64         return $this->doRequest();
65     }
66     function buildQueryString($data) {
67         $querystring = '';
68         if (is_array($data)) {
69             // Change data in to postable data
70                 foreach ($data as $key => $val) {
71                         if (is_array($val)) {
72                                 foreach ($val as $val2) {
73                                         $querystring .= urlencode($key).'='.urlencode($val2).'&';
74                                 }
75                         } else {
76                                 $querystring .= urlencode($key).'='.urlencode($val).'&';
77                         }
78                 }
79                 $querystring = substr($querystring, 0, -1); // Eliminate unnecessary &
80         } else {
81             $querystring = $data;
82         }
83         return $querystring;
84     }
85     function doRequest() {
86         // Performs the actual HTTP request, returning true or false depending on outcome
87         if (!$fp = @fsockopen($this->host, $this->port, $errno, $errstr, $this->timeout)) {
88             // Set error message
89             switch($errno) {
90             case -3:
91                 $this->errormsg = 'Socket creation failed (-3)';
92             case -4:
93                 $this->errormsg = 'DNS lookup failure (-4)';
94             case -5:
95                 $this->errormsg = 'Connection refused or timed out (-5)';
96             default:
97                 $this->errormsg = 'Connection failed ('.$errno.')';
98                 $this->errormsg .= ' '.$errstr;
99                 $this->debug($this->errormsg);
100             }
101             return false;
102         }
103         if (check_php_version(4,3,0))
104             socket_set_timeout($fp, $this->timeout);
105         $request = $this->buildRequest();
106         $this->debug('Request', $request);
107         fwrite($fp, $request);
108         // Reset all the variables that should not persist between requests
109         $this->headers = array();
110         $this->content = '';
111         $this->errormsg = '';
112         // Set a couple of flags
113         $inHeaders = true;
114         $atStart = true;
115         // Now start reading back the response
116         while (!feof($fp)) {
117             $line = fgets($fp, 4096);
118             if ($atStart) {
119                 // Deal with first line of returned data
120                 $atStart = false;
121                 if (!preg_match('/HTTP\/(\\d\\.\\d)\\s*(\\d+)\\s*(.*)/', $line, $m)) {
122                     $this->errormsg = "Status code line invalid: ".htmlentities($line);
123                     $this->debug($this->errormsg);
124                     return false;
125                 }
126                 $http_version = $m[1]; // not used
127                 $this->status = $m[2];
128                 $status_string = $m[3]; // not used
129                 $this->debug(trim($line));
130                 continue;
131             }
132             if ($inHeaders) {
133                 if (trim($line) == '') {
134                     $inHeaders = false;
135                     $this->debug('Received Headers', $this->headers);
136                     if ($this->headers_only) {
137                         break; // Skip the rest of the input
138                     }
139                     continue;
140                 }
141                 if (!preg_match('/([^:]+):\\s*(.*)/', $line, $m)) {
142                     // Skip to the next header
143                     continue;
144                 }
145                 $key = strtolower(trim($m[1]));
146                 $val = trim($m[2]);
147                 // Deal with the possibility of multiple headers of same name
148                 if (isset($this->headers[$key])) {
149                     if (is_array($this->headers[$key])) {
150                         $this->headers[$key][] = $val;
151                     } else {
152                         $this->headers[$key] = array($this->headers[$key], $val);
153                     }
154                 } else {
155                     $this->headers[$key] = $val;
156                 }
157                 continue;
158             }
159             // We're not in the headers, so append the line to the contents
160             $this->content .= $line;
161         }
162         fclose($fp);
163         // If data is compressed, uncompress it
164         if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] == 'gzip') {
165             $this->debug('Content is gzip encoded, unzipping it');
166             $this->content = substr($this->content, 10); // See http://www.php.net/manual/en/function.gzencode.php
167             $this->content = gzinflate($this->content);
168         }
169         // If $persist_cookies, deal with any cookies
170         if ($this->persist_cookies && isset($this->headers['set-cookie']) && $this->host == $this->cookie_host) {
171             $cookies = $this->headers['set-cookie'];
172             if (!is_array($cookies)) {
173                 $cookies = array($cookies);
174             }
175             foreach ($cookies as $cookie) {
176                 if (preg_match('/([^=]+)=([^;]+);/', $cookie, $m)) {
177                     $this->cookies[$m[1]] = $m[2];
178                 }
179             }
180             // Record domain of cookies for security reasons
181             $this->cookie_host = $this->host;
182         }
183         // If $persist_referers, set the referer ready for the next request
184         if (isset($this->persist_referers)) {
185             $this->debug('Persisting referer: '.$this->getRequestURL());
186             $this->referer = $this->getRequestURL();
187         }
188         // Finally, if handle_redirects and a redirect is sent, do that
189         if (isset($this->handle_redirects)) {
190             if (++$this->redirect_count >= $this->max_redirects) {
191                 $this->errormsg = 'Number of redirects exceeded maximum ('.$this->max_redirects.')';
192                 $this->debug($this->errormsg);
193                 $this->redirect_count = 0;
194                 return false;
195             }
196             $location = isset($this->headers['location']) ? $this->headers['location'] : '';
197             $uri = isset($this->headers['uri']) ? $this->headers['uri'] : '';
198             if ($location || $uri) {
199                 $url = parse_url($location.$uri);
200                 // This will FAIL if redirect is to a different site
201                 return $this->get($url['path']);
202             }
203         }
204         return true;
205     }
206     function buildRequest() {
207         $headers = array();
208         $headers[] = "{$this->method} {$this->path} HTTP/1.0"; // Using 1.1 leads to all manner of problems, such as "chunked" encoding
209         $headers[] = "Host: {$this->host}";
210         $headers[] = "User-Agent: {$this->user_agent}";
211         $headers[] = "Accept: {$this->accept}";
212         if ($this->use_gzip) {
213             $headers[] = "Accept-encoding: {$this->accept_encoding}";
214         }
215         $headers[] = "Accept-language: {$this->accept_language}";
216         if (!empty($this->referer)) {
217             $headers[] = "Referer: {$this->referer}";
218         }
219         // Cookies
220         if (!empty($this->cookies)) {
221             $cookie = 'Cookie: ';
222             foreach ($this->cookies as $key => $value) {
223                 $cookie .= "$key=$value; ";
224             }
225             $headers[] = $cookie;
226         }
227         // Basic authentication
228         if (!empty($this->username) && !empty($this->password)) {
229             $headers[] = 'Authorization: BASIC '.base64_encode($this->username.':'.$this->password);
230         }
231         // If this is a POST, set the content type and length
232         if ($this->postdata) {
233             $headers[] = 'Content-Type: application/x-www-form-urlencoded';
234             $headers[] = 'Content-Length: '.strlen($this->postdata);
235         }
236         $request = implode("\r\n", $headers)."\r\n\r\n".$this->postdata;
237         return $request;
238     }
239     function getStatus() {
240         return $this->status;
241     }
242     function getContent() {
243         return $this->content;
244     }
245     function getHeaders() {
246         return $this->headers;
247     }
248     function getHeader($header) {
249         $header = strtolower($header);
250         if (isset($this->headers[$header])) {
251             return $this->headers[$header];
252         } else {
253             return false;
254         }
255     }
256     function getError() {
257         return $this->errormsg;
258     }
259     function getCookies() {
260         return $this->cookies;
261     }
262     function getRequestURL() {
263         $url = 'http://'.$this->host;
264         if ($this->port != 80) {
265             $url .= ':'.$this->port;
266         }            
267         $url .= $this->path;
268         return $url;
269     }
270     // Setter methods
271     function setUserAgent($string) {
272         $this->user_agent = $string;
273     }
274     function setAuthorization($username, $password) {
275         $this->username = $username;
276         $this->password = $password;
277     }
278     function setCookies($array) {
279         $this->cookies = $array;
280     }
281     // Option setting methods
282     function useGzip($boolean) {
283         $this->use_gzip = $boolean;
284     }
285     function setPersistCookies($boolean) {
286         $this->persist_cookies = $boolean;
287     }
288     function setPersistReferers($boolean) {
289         $this->persist_referers = $boolean;
290     }
291     function setHandleRedirects($boolean) {
292         $this->handle_redirects = $boolean;
293     }
294     function setMaxRedirects($num) {
295         $this->max_redirects = $num;
296     }
297     function setHeadersOnly($boolean) {
298         $this->headers_only = $boolean;
299     }
300     function setDebug($boolean) {
301         $this->debug = $boolean;
302     }
303     // "Quick" static methods
304     function quickGet($url) {
305         $bits = parse_url($url);
306         $host = $bits['host'];
307         $port = isset($bits['port']) ? $bits['port'] : 80;
308         $path = isset($bits['path']) ? $bits['path'] : '/';
309         if (isset($bits['query'])) {
310             $path .= '?'.$bits['query'];
311         }
312         $client = new HttpClient($host, $port);
313         if (!$client->get($path)) {
314             return false;
315         } else {
316             return $client->getContent();
317         }
318     }
319     function quickPost($url, $data) {
320         $bits = parse_url($url);
321         $host = $bits['host'];
322         $port = isset($bits['port']) ? $bits['port'] : 80;
323         $path = isset($bits['path']) ? $bits['path'] : '/';
324         $client = new HttpClient($host, $port);
325         if (!$client->post($path, $data)) {
326             return false;
327         } else {
328             return $client->getContent();
329         }
330     }
331     function debug($msg, $object = false) {
332         if ($this->debug) {
333             print '<div style="border: 1px solid red; padding: 0.5em; margin: 0.5em;"><strong>HttpClient Debug:</strong> '.$msg;
334             if ($object) {
335                 ob_start();
336                     print_r($object);
337                     $content = htmlentities(ob_get_contents());
338                     ob_end_clean();
339                     print '<pre>'.$content.'</pre>';
340                 }
341                 print '</div>';
342         }
343     }   
344 }
345
346 // $Log: not supported by cvs2svn $
347 // Revision 1.4  2004/04/12 18:15:28  rurban
348 // standard footer
349 //
350
351 // For emacs users
352 // Local Variables:
353 // mode: php
354 // tab-width: 8
355 // c-basic-offset: 4
356 // c-hanging-comment-ender-p: nil
357 // indent-tabs-mode: nil
358 // End:
359 ?>