root/devel-backup/units/magpie/rss_fetch.inc

Revision 45, 12.0 kB (checked in by sven, 3 years ago)

dos2unix to clean line endings, set svn property eol-style native, some whitespace homogenisation

  • Property svn:eol-style set to native
Line 
1 <?php
2 /*
3  * Project:     MagpieRSS: a simple RSS integration tool
4  * File:        rss_fetch.inc, a simple functional interface
5                                 to fetching and parsing RSS files, via the
6                                 function fetch_rss()
7  * Author:      Kellan Elliott-McCrea <kellan@protest.net>
8  * License:             GPL
9  *
10  * The lastest version of MagpieRSS can be obtained from:
11  * http://magpierss.sourceforge.net
12  *
13  * For questions, help, comments, discussion, etc., please join the
14  * Magpie mailing list:
15  * magpierss-general@lists.sourceforge.net
16  *
17  */
18  
19 // Setup MAGPIE_DIR for use on hosts that don't include
20 // the current path in include_path.
21 // with thanks to rajiv and smarty
22 if (!defined('DIR_SEP')) {
23         define('DIR_SEP', DIRECTORY_SEPARATOR);
24 }
25
26 if (!defined('MAGPIE_DIR')) {
27     define('MAGPIE_DIR', dirname(__FILE__) . DIR_SEP);
28 }
29
30 require_once( MAGPIE_DIR . 'rss_parse.inc' );
31 require_once( MAGPIE_DIR . 'rss_cache.inc' );
32
33 // for including 3rd party libraries
34 define('MAGPIE_EXTLIB', MAGPIE_DIR . 'extlib' . DIR_SEP);
35 require_once( MAGPIE_EXTLIB . 'Snoopy.class.inc');
36
37
38 /*
39  * CONSTANTS - redefine these in your script to change the
40  * behaviour of fetch_rss() currently, most options effect the cache
41  *
42  * MAGPIE_CACHE_ON - Should Magpie cache parsed RSS objects?
43  * For me a built in cache was essential to creating a "PHP-like"
44  * feel to Magpie, see rss_cache.inc for rationale
45  *
46  *
47  * MAGPIE_CACHE_DIR - Where should Magpie cache parsed RSS objects?
48  * This should be a location that the webserver can write to.   If this
49  * directory does not already exist Mapie will try to be smart and create
50  * it.  This will often fail for permissions reasons.
51  *
52  *
53  * MAGPIE_CACHE_AGE - How long to store cached RSS objects? In seconds.
54  *
55  *
56  * MAGPIE_CACHE_FRESH_ONLY - If remote fetch fails, throw error
57  * instead of returning stale object?
58  *
59  * MAGPIE_DEBUG - Display debugging notices?
60  *
61 */
62
63
64 /*=======================================================================*\
65         Function: fetch_rss:
66         Purpose:  return RSS object for the give url
67                           maintain the cache
68         Input:    url of RSS file
69         Output:   parsed RSS object (see rss_parse.inc)
70
71         NOTES ON CACHEING: 
72         If caching is on (MAGPIE_CACHE_ON) fetch_rss will first check the cache.
73        
74         NOTES ON RETRIEVING REMOTE FILES:
75         If conditional gets are on (MAGPIE_CONDITIONAL_GET_ON) fetch_rss will
76         return a cached object, and touch the cache object upon recieving a
77         304.
78        
79         NOTES ON FAILED REQUESTS:
80         If there is an HTTP error while fetching an RSS object, the cached
81         version will be return, if it exists (and if MAGPIE_CACHE_FRESH_ONLY is off)
82 \*=======================================================================*/
83
84 define('MAGPIE_VERSION', '0.61');
85
86 $MAGPIE_ERROR = "";
87
88 function fetch_rss ($url) {
89         // initialize constants
90         init();
91        
92         if ( !isset($url) ) {
93                 error("fetch_rss called without a url");
94                 return false;
95         }
96        
97         // if cache is disabled
98         if ( !MAGPIE_CACHE_ON ) {
99                 // fetch file, and parse it
100                 $resp = _fetch_remote_file( $url );
101                 if ( is_success( $resp->status ) ) {
102                         return _response_to_rss( $resp );
103                 }
104                 else {
105                         error("Failed to fetch $url and cache is off");
106                         return false;
107                 }
108         }
109         // else cache is ON
110         else {
111                 // Flow
112                 // 1. check cache
113                 // 2. if there is a hit, make sure its fresh
114                 // 3. if cached obj fails freshness check, fetch remote
115                 // 4. if remote fails, return stale object, or error
116                
117                 $cache = new RSSCache( MAGPIE_CACHE_DIR, MAGPIE_CACHE_AGE );
118                
119                 if (MAGPIE_DEBUG and $cache->ERROR) {
120                         debug($cache->ERROR, E_USER_WARNING);
121                 }
122                
123                
124                 $cache_status    = 0;           // response of check_cache
125                 $request_headers = array(); // HTTP headers to send with fetch
126                 $rss                     = 0;           // parsed RSS object
127                 $errormsg                = 0;           // errors, if any
128                
129                 if (!$cache->ERROR) {
130                         // return cache HIT, MISS, or STALE
131                         $cache_status = $cache->check_cache( $url );
132                 }
133                
134                 // if object cached, and cache is fresh, return cached obj
135                 if ( $cache_status == 'HIT' ) {
136                         $rss = $cache->get( $url );
137                         if ( isset($rss) and $rss ) {
138                                 $rss->from_cache = 1;
139                                 if ( MAGPIE_DEBUG > 1) {
140                                 debug("MagpieRSS: Cache HIT", E_USER_NOTICE);
141                         }
142                                 return $rss;
143                         }
144                 }
145                
146                 // else attempt a conditional get
147                
148                 // setup headers
149                 if ( $cache_status == 'STALE' ) {
150                         $rss = $cache->get( $url );
151                         if ( $rss->etag and $rss->last_modified ) {
152                                 $request_headers['If-None-Match'] = $rss->etag;
153                                 $request_headers['If-Last-Modified'] = $rss->last_modified;
154                         }
155                 }
156                
157                 $resp = _fetch_remote_file( $url, $request_headers );
158                
159                 if (isset($resp) and $resp) {
160                         if ($resp->status == '304' ) {
161                                 // we have the most current copy
162                                 if ( MAGPIE_DEBUG > 1) {
163                                         debug("Got 304 for $url");
164                                 }
165                                 // reset cache on 304 (at minutillo insistent prodding)
166                                 $cache->set($url, $rss);
167                                 return $rss;
168                         }
169                         elseif ( is_success( $resp->status ) ) {
170                                 $rss = _response_to_rss( $resp );
171                                 if ( $rss ) {
172                                         if (MAGPIE_DEBUG > 1) {
173                                                 debug("Fetch successful");
174                                         }
175                                         // add object to cache
176                                         $cache->set( $url, $rss );
177                                         return $rss;
178                                 }
179                         }
180                         else {
181                                 $errormsg = "Failed to fetch $url. ";
182                                 if ( $resp->error ) {
183                                         # compensate for Snoopy's annoying habbit to tacking
184                                         # on '\n'
185                                         $http_error = substr($resp->error, 0, -2);
186                                         $errormsg .= "(HTTP Error: $http_error)";
187                                 }
188                                 else {
189                                         $errormsg .=  "(HTTP Response: " . $resp->response_code .')';
190                                 }
191                         }
192                 }
193                 else {
194                         $errormsg = "Unable to retrieve RSS file for unknown reasons.";
195                 }
196                
197                 // else fetch failed
198                
199                 // attempt to return cached object
200                 if ($rss) {
201                         if ( MAGPIE_DEBUG ) {
202                                 debug("Returning STALE object for $url");
203                         }
204                         return $rss;
205                 }
206                
207                 // else we totally failed
208                 error( $errormsg );     
209                
210                 return false;
211                
212         } // end if ( !MAGPIE_CACHE_ON ) {
213 } // end fetch_rss()
214
215 /*=======================================================================*\
216         Function:       error
217         Purpose:        set MAGPIE_ERROR, and trigger error
218 \*=======================================================================*/
219
220 function error ($errormsg, $lvl=E_USER_WARNING) {
221                 global $MAGPIE_ERROR;
222                
223                 // append PHP's error message if track_errors enabled
224                 if ( isset($php_errormsg) ) {
225                         $errormsg .= " ($php_errormsg)";
226                 }
227                 if ( $errormsg ) {
228                         $errormsg = "MagpieRSS: $errormsg";
229                         $MAGPIE_ERROR = $errormsg;
230                         trigger_error( $errormsg, $lvl);                               
231                 }
232 }
233
234 function debug ($debugmsg, $lvl=E_USER_NOTICE) {
235         trigger_error("MagpieRSS [debug] $debugmsg", $lvl);
236 }
237                        
238 /*=======================================================================*\
239         Function:       magpie_error
240         Purpose:        accessor for the magpie error variable
241 \*=======================================================================*/
242 function magpie_error ($errormsg="") {
243         global $MAGPIE_ERROR;
244        
245         if ( isset($errormsg) and $errormsg ) {
246                 $MAGPIE_ERROR = $errormsg;
247         }
248        
249         return $MAGPIE_ERROR;   
250 }
251
252 /*=======================================================================*\
253         Function:       _fetch_remote_file
254         Purpose:        retrieve an arbitrary remote file
255         Input:          url of the remote file
256                                 headers to send along with the request (optional)
257         Output:         an HTTP response object (see Snoopy.class.inc) 
258 \*=======================================================================*/
259 function _fetch_remote_file ($url, $headers = "" ) {
260         // Snoopy is an HTTP client in PHP
261         $client = new Snoopy();
262         $client->agent = MAGPIE_USER_AGENT;
263         $client->read_timeout = MAGPIE_FETCH_TIME_OUT;
264         $client->use_gzip = MAGPIE_USE_GZIP;
265         if (is_array($headers) ) {
266                 $client->rawheaders = $headers;
267         }
268        
269         @$client->fetch($url);
270         return $client;
271
272 }
273
274 /*=======================================================================*\
275         Function:       _response_to_rss
276         Purpose:        parse an HTTP response object into an RSS object
277         Input:          an HTTP response object (see Snoopy)
278         Output:         parsed RSS object (see rss_parse)
279 \*=======================================================================*/
280 function _response_to_rss ($resp) {
281         $rss = new MagpieRSS( $resp->results );
282        
283         // if RSS parsed successfully           
284         if ( $rss and !$rss->ERROR) {
285                
286                 // find Etag, and Last-Modified
287                 foreach($resp->headers as $h) {
288                         // 2003-03-02 - Nicola Asuni (www.tecnick.com) - fixed bug "Undefined offset: 1"
289                         if (strpos($h, ": ")) {
290                                 list($field, $val) = explode(": ", $h, 2);
291                         }
292                         else {
293                                 $field = $h;
294                                 $val = "";
295                         }
296                        
297                         if ( $field == 'ETag' ) {
298                                 $rss->etag = $val;
299                         }
300                        
301                         if ( $field == 'Last-Modified' ) {
302                                 $rss->last_modified = $val;
303                         }
304                 }
305                
306                 return $rss;   
307         } // else construct error message
308         else {
309                 $errormsg = "Failed to parse RSS file.";
310                
311                 if ($rss) {
312                         $errormsg .= " (" . $rss->ERROR . ")";
313                 }
314                 error($errormsg);
315                
316                 return false;
317         } // end if ($rss and !$rss->error)
318 }
319
320 /*=======================================================================*\
321         Function:       init
322         Purpose:        setup constants with default values
323                                 check for user overrides
324 \*=======================================================================*/
325 function init () {
326         if ( defined('MAGPIE_INITALIZED') ) {
327                 return;
328         }
329         else {
330                 define('MAGPIE_INITALIZED', 1);
331         }
332        
333         if ( !defined('MAGPIE_CACHE_ON') ) {
334                 define('MAGPIE_CACHE_ON', 1);
335         }
336
337         if ( !defined('MAGPIE_CACHE_DIR') ) {
338                 define('MAGPIE_CACHE_DIR', './cache');
339         }
340
341         if ( !defined('MAGPIE_CACHE_AGE') ) {
342                 define('MAGPIE_CACHE_AGE', 60*60); // one hour
343         }
344
345         if ( !defined('MAGPIE_CACHE_FRESH_ONLY') ) {
346                 define('MAGPIE_CACHE_FRESH_ONLY', 0);
347         }
348
349         if ( !defined('MAGPIE_DEBUG') ) {
350                 define('MAGPIE_DEBUG', 0);
351         }
352        
353         if ( !defined('MAGPIE_USER_AGENT') ) {
354                 $ua = 'MagpieRSS/'. MAGPIE_VERSION . ' (+http://magpierss.sf.net';
355                
356                 if ( MAGPIE_CACHE_ON ) {
357                         $ua = $ua . ')';
358                 }
359                 else {
360                         $ua = $ua . '; No cache)';
361                 }
362                
363                 define('MAGPIE_USER_AGENT', $ua);
364         }
365        
366         if ( !defined('MAGPIE_FETCH_TIME_OUT') ) {
367                 define('MAGPIE_FETCH_TIME_OUT', 5);     // 5 second timeout
368         }
369        
370         // use gzip encoding to fetch rss files if supported?
371         if ( !defined('MAGPIE_USE_GZIP') ) {
372                 define('MAGPIE_USE_GZIP', true);       
373         }
374 }
375
376 // NOTE: the following code should really be in Snoopy, or at least
377 // somewhere other then rss_fetch!
378
379 /*=======================================================================*\
380         HTTP STATUS CODE PREDICATES
381         These functions attempt to classify an HTTP status code
382         based on RFC 2616 and RFC 2518.
383        
384         All of them take an HTTP status code as input, and return true or false
385
386         All this code is adapted from LWP's HTTP::Status.
387 \*=======================================================================*/
388
389
390 /*=======================================================================*\
391         Function:       is_info
392         Purpose:        return true if Informational status code
393 \*=======================================================================*/
394 function is_info ($sc) {
395         return $sc >= 100 && $sc < 200;
396 }
397
398 /*=======================================================================*\
399         Function:       is_success
400         Purpose:        return true if Successful status code
401 \*=======================================================================*/
402 function is_success ($sc) {
403         return $sc >= 200 && $sc < 300;
404 }
405
406 /*=======================================================================*\
407         Function:       is_redirect
408         Purpose:        return true if Redirection status code
409 \*=======================================================================*/
410 function is_redirect ($sc) {
411         return $sc >= 300 && $sc < 400;
412 }
413
414 /*=======================================================================*\
415         Function:       is_error
416         Purpose:        return true if Error status code
417 \*=======================================================================*/
418 function is_error ($sc) {
419         return $sc >= 400 && $sc < 600;
420 }
421
422 /*=======================================================================*\
423         Function:       is_client_error
424         Purpose:        return true if Error status code, and its a client error
425 \*=======================================================================*/
426 function is_client_error ($sc) {
427         return $sc >= 400 && $sc < 500;
428 }
429
430 /*=======================================================================*\
431         Function:       is_client_error
432         Purpose:        return true if Error status code, and its a server error
433 \*=======================================================================*/
434 function is_server_error ($sc) {
435         return $sc >= 500 && $sc < 600;
436 }
437
438 ?>
Note: See TracBrowser for help on using the browser.