fix image text
[pear] / Cache / HTTP_Request.php
1 <?php
2 // +----------------------------------------------------------------------+
3 // | PEAR :: Cache                                                        |
4 // +----------------------------------------------------------------------+
5 // | Copyright (c) 1997-2003 The PHP Group                                |
6 // +----------------------------------------------------------------------+
7 // | This source file is subject to version 2.0 of the PHP license,       |
8 // | that is bundled with this package in the file LICENSE, and is        |
9 // | available at through the world-wide-web at                           |
10 // | http://www.php.net/license/2_02.txt.                                 |
11 // | If you did not receive a copy of the PHP license and are unable to   |
12 // | obtain it through the world-wide-web, please send a note to          |
13 // | license@php.net so we can mail you a copy immediately.               |
14 // +----------------------------------------------------------------------+
15 // | Authors: Fabien MARTY <fabien.marty@free.fr> |
16 // +----------------------------------------------------------------------+
17 //
18 // $Id: HTTP_Request.php 174777 2004-12-15 09:09:33Z dufuz $
19
20 require_once 'Cache.php';
21 require_once 'HTTP/Request.php';
22
23 define('CACHE_HTTP_REQUEST_GROUP_NAME', 'cache_http_request');
24 define('CACHE_HTTP_REQUEST_SUCCESS_RESPONSE_CODE', 200);
25 define('CACHE_HTTP_REQUEST_KEEP_LOCAL_COPY', 1);
26 define('CACHE_HTTP_REQUEST_RETURN_FALSE', 2);
27 define('CACHE_HTTP_REQUEST_RETURN_PEAR_ERROR', 3);
28
29 /**
30 * HTTP_Request Cache
31 *
32 * The classical example is :
33 *
34 * You want to get news from another site through RSS remote files. But you
35 * don't want to access to to the remote site at every time you display
36 * its news on your site. Because, if the remote site is down or slow...
37 * So you you need a class which makes a local cache copy of the remote file.
38 * Every x hours, the cache is updated. But if the remote site is down, the
39 * local cache copy is keeped (you can also get error messages if you want).
40 *
41 * So you need this class!
42 *
43 * Cache_HTTP_Request inherits from Cache and use HTTP_Request to access to
44 * the remote file.
45 *
46 * Usage example :
47 *
48 * <?php
49 * require_once('Cache/HTTP_Request.php');
50 *
51 * $cache = &new Cache_HTTP_Request('http://www.php.net', null, 'file', null, 3600);
52 * $cache->sendRequest();
53 * $remoteFileBody = $cache->getResponseBody();
54 *
55 * (...)
56 * ?>
57 *
58 * @author   Fabien MARTY <fabien.marty@free.fr>
59 * @version  $Id: HTTP_Request.php 174777 2004-12-15 09:09:33Z dufuz $
60 * @package  Cache
61 */
62
63 class Cache_HTTP_Request extends Cache
64 {
65
66     // --- Private properties ---
67
68     /**
69     * Lifetime in seconds (0 endless)
70     *
71     * @var int $_expires
72     */
73     var $_expires;
74
75     /**
76     * HTTP Request
77     *
78     * @var object $_request
79     */
80     var $_request;
81
82     /**
83     * Cache id for the classic cache file
84     *
85     * @see sendRequest()
86     * @var string $_id
87     */
88     var $_id;
89
90     /**
91     * Cache id for the endless cache file
92     *
93     * @see sendRequest()
94     * @var string $_id
95     */
96     var $_id2;
97
98     /**
99     * Data to use
100     *
101     * @see getReponseBody(), getReponseHeader(), getReponseCode()
102     * @var array $_data
103     */
104     var $_data ;
105
106     // --- Public methods ---
107
108     /**
109     * Constructor
110     *
111     * @param $url The url to access
112     * @param $params Associative array of parameters which can be:
113     *                  method     - Method to use, GET, POST etc
114     *                  http       - HTTP Version to use, 1.0 or 1.1
115     *                  user       - Basic Auth username
116     *                  pass       - Basic Auth password
117     *                  proxy_host - Proxy server host
118     *                  proxy_port - Proxy server port
119     *                  proxy_user - Proxy auth username
120     *                  proxy_pass - Proxy auth password
121     * @param string $container Name of container class
122     * @param array $containerOptions Array with container class options
123     * @param int $mode What to do when the remote server is down :
124     *                   CACHE_HTTP_REQUEST_KEEP_LOCAL_COPY or
125     *                   CACHE_HTTP_REQUEST_RETURN_FALSE or
126     *                   CACHE_HTTP_REQUEST_RETURN_PEAR_ERROR
127     * @param int $expires lifetime of the cached data in seconds - 0 for endless
128     * @see Cache, HTTP_Request
129     * @access public
130     */
131     function Cache_HTTP_Request($url, $params = null, $container  = 'file',
132                                 $containerOptions = null, $expires = 3600,
133                                 $mode = CACHE_HTTP_REQUEST_KEEP_LOCAL_COPY)
134     {
135         if (!isset($params)) {
136             $params = array();
137         }
138
139         if (!isset($containerOptions)) {
140             $containerOptions = array (
141                 'cache_dir' => '/tmp/',
142                 'filename_prefix' => 'cache_'
143             );
144         }
145         $this->Cache($container, $containerOptions);
146         $this->_request = &new HTTP_Request($url, $params);
147         $this->_id = md5($url.serialize($params));
148         $this->_id2 = md5($this->_id); // we need two keys
149         $this->_mode = $mode;
150         $this->_expires = $expires;
151     }
152
153     /**
154     * Deconstructor
155     *
156     * @access public
157     */
158     function _Cache_HTTP_Request()
159     {
160         $this->_Cache();
161     }
162
163     /**
164     * Get and return the response body (null if no data available)
165     *
166     * @see sendRequest()
167     * @return mixed response body
168     * @access public
169     */
170     function getResponseBody()
171     {
172         return $this->_data['body'];
173     }
174
175     /**
176     * Get and return the response code (null if no data available)
177     *
178     * @see sendRequest()
179     * @return mixed response code
180     * @access public
181     */
182     function getResponseCode()
183     {
184         return $this->_data['code'];
185     }
186
187     /**
188     * Get and return the response header (null if no data available)
189     *
190     * @see sendRequest()
191     * @return mixed response header
192     * @access public
193     */
194     function getResponseHeader()
195     {
196         return $this->_data['header'];
197     }
198
199
200     /**
201     * Set a new mode when the server is down
202     *
203     * @param int $newMode What to do when the remote server is down :
204     *                      CACHE_HTTP_REQUEST_KEEP_LOCAL_COPY or
205     *                      CACHE_HTTP_REQUEST_RETURN_FALSE or
206     *                      CACHE_HTTP_REQUEST_RETURN_PEAR_ERROR
207     * @access public
208     */
209     function setMode($newMode)
210     {
211         $this->_mode = $newMode;
212     }
213
214     /**
215     * Send the HTTP request or use the cache system
216     *
217     * If there is a cache file for this HTTP request, the request is not re-sent.
218     * Cached response is used. Yet, if the cache is expired, the HTTP request
219     * is re-sent. Then, if the remote server is down, this method will return :
220     * (depending on the selected mode)
221     * - false or
222     * - a PEAR_Error or (better)
223     * - true and the local copy of the latest valid response will be used.
224     *
225     * (technical)
226     * For the last choice, there is a technical tips.
227     * Indeed, there are two cache files. The first one (id key) is a classical one
228     * with the given lifetime. But it can be removed by automatic garbage collection
229     * for example. So to be able to use the latest valid response (when the remote
230     * server is dead), we make a second cache file with no lifetime (id2 key).
231     *
232     * @return mixed true or false or a PEAR_ERROR
233     * @access public
234     */
235     function sendRequest()
236     {
237         if ($data = $this->get($this->_id, CACHE_HTTP_REQUEST_GROUP_NAME)) {
238             // --- Cache hit ---
239             $this->_data = $data;
240             return true;
241         } else {
242             // --- Cache miss ---
243             if ($this->_sendRequestAndGetResponse()) {
244                 // So the remote server is ok...
245                 $this->save($this->_id, $this->_data, $this->_expires, CACHE_HTTP_REQUEST_GROUP_NAME);
246                 $this->save($this->_id2, $this->_data, 0, CACHE_HTTP_REQUEST_GROUP_NAME);
247                 return true;
248             } else {
249                 if ($data_sav = $this->get($this->_id2, CACHE_HTTP_REQUEST_GROUP_NAME)) {
250                     // Ok, the "recover cache" is available...
251                     switch ($this->_mode) {
252                     case CACHE_HTTP_REQUEST_KEEP_LOCAL_COPY:
253                         // We make a new local copy and keep it until it expires...
254                         $this->save($this->_id, $data_sav, $this->_expires, CACHE_HTTP_REQUEST_GROUP_NAME);
255                         $this->_data = $data_sav;
256                         return true;
257                         break;
258                     case CACHE_HTTP_REQUEST_RETURN_FALSE:
259                         // We return false
260                         return false;
261                         break;
262                     case CACHE_HTTP_REQUEST_RETURN_PEAR_ERROR:
263                         // We return a PEAR_Error!
264                         return new Cache_Error('Remote file is not available!');
265                         break;
266                     }
267                 } else {
268                     // It's terrible! The remote server is down and definitively no cache available!
269                     return new Cache_Error('Remote server down and no cache available!');
270                 }
271             }
272         }
273     }
274
275     // --- Private Methods ---
276
277     /**
278     * Send HTTP request and get the response
279     *
280     * @return boolean success or not ?
281     * @see HTTP_Request
282     * @access private
283     */
284     function _sendRequestAndGetResponse()
285     {
286         $this->_request->sendRequest();
287         $body = $this->_request->getResponseBody();
288         $code = $this->_request->getResponseCode();
289         $header = $this->_request->getResponseHeader();
290         $this->_data = array(
291             'body' => $body,
292             'code' => $code,
293             'header' => $header
294         );
295         return (($code==CACHE_HTTP_REQUEST_SUCCESS_RESPONSE_CODE) ? true : false);
296     }
297
298 }
299 ?>