ru-se.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

rss.php (22978B)


      1 <?php
      2 /**
      3  * MagpieRSS: a simple RSS integration tool
      4  *
      5  * A compiled file for RSS syndication
      6  *
      7  * @author Kellan Elliott-McCrea <kellan@protest.net>
      8  * @version 0.51
      9  * @license GPL
     10  *
     11  * @package External
     12  * @subpackage MagpieRSS
     13  * @deprecated 3.0.0 Use SimplePie instead.
     14  */
     15 
     16 /**
     17  * Deprecated. Use SimplePie (class-simplepie.php) instead.
     18  */
     19 _deprecated_file( basename( __FILE__ ), '3.0.0', WPINC . '/class-simplepie.php' );
     20 
     21 /**
     22  * Fires before MagpieRSS is loaded, to optionally replace it.
     23  *
     24  * @since 2.3.0
     25  * @deprecated 3.0.0
     26  */
     27 do_action( 'load_feed_engine' );
     28 
     29 /** RSS feed constant. */
     30 define('RSS', 'RSS');
     31 define('ATOM', 'Atom');
     32 define('MAGPIE_USER_AGENT', 'WordPress/' . $GLOBALS['wp_version']);
     33 
     34 class MagpieRSS {
     35 	var $parser;
     36 	var $current_item	= array();	// item currently being parsed
     37 	var $items			= array();	// collection of parsed items
     38 	var $channel		= array();	// hash of channel fields
     39 	var $textinput		= array();
     40 	var $image			= array();
     41 	var $feed_type;
     42 	var $feed_version;
     43 
     44 	// parser variables
     45 	var $stack				= array(); // parser stack
     46 	var $inchannel			= false;
     47 	var $initem 			= false;
     48 	var $incontent			= false; // if in Atom <content mode="xml"> field
     49 	var $intextinput		= false;
     50 	var $inimage 			= false;
     51 	var $current_field		= '';
     52 	var $current_namespace	= false;
     53 
     54 	//var $ERROR = "";
     55 
     56 	var $_CONTENT_CONSTRUCTS = array('content', 'summary', 'info', 'title', 'tagline', 'copyright');
     57 
     58 	/**
     59 	 * PHP5 constructor.
     60 	 */
     61 	function __construct( $source ) {
     62 
     63 		# Check if PHP xml isn't compiled
     64 		#
     65 		if ( ! function_exists('xml_parser_create') ) {
     66 			return trigger_error( "PHP's XML extension is not available. Please contact your hosting provider to enable PHP's XML extension." );
     67 		}
     68 
     69 		$parser = xml_parser_create();
     70 
     71 		$this->parser = $parser;
     72 
     73 		# pass in parser, and a reference to this object
     74 		# set up handlers
     75 		#
     76 		xml_set_object( $this->parser, $this );
     77 		xml_set_element_handler($this->parser,
     78 				'feed_start_element', 'feed_end_element' );
     79 
     80 		xml_set_character_data_handler( $this->parser, 'feed_cdata' );
     81 
     82 		$status = xml_parse( $this->parser, $source );
     83 
     84 		if (! $status ) {
     85 			$errorcode = xml_get_error_code( $this->parser );
     86 			if ( $errorcode != XML_ERROR_NONE ) {
     87 				$xml_error = xml_error_string( $errorcode );
     88 				$error_line = xml_get_current_line_number($this->parser);
     89 				$error_col = xml_get_current_column_number($this->parser);
     90 				$errormsg = "$xml_error at line $error_line, column $error_col";
     91 
     92 				$this->error( $errormsg );
     93 			}
     94 		}
     95 
     96 		xml_parser_free( $this->parser );
     97 		unset( $this->parser );
     98 
     99 		$this->normalize();
    100 	}
    101 
    102 	/**
    103 	 * PHP4 constructor.
    104 	 */
    105 	public function MagpieRSS( $source ) {
    106 		self::__construct( $source );
    107 	}
    108 
    109 	function feed_start_element($p, $element, &$attrs) {
    110 		$el = $element = strtolower($element);
    111 		$attrs = array_change_key_case($attrs, CASE_LOWER);
    112 
    113 		// check for a namespace, and split if found
    114 		$ns	= false;
    115 		if ( strpos( $element, ':' ) ) {
    116 			list($ns, $el) = explode( ':', $element, 2);
    117 		}
    118 		if ( $ns and $ns != 'rdf' ) {
    119 			$this->current_namespace = $ns;
    120 		}
    121 
    122 		# if feed type isn't set, then this is first element of feed
    123 		# identify feed from root element
    124 		#
    125 		if (!isset($this->feed_type) ) {
    126 			if ( $el == 'rdf' ) {
    127 				$this->feed_type = RSS;
    128 				$this->feed_version = '1.0';
    129 			}
    130 			elseif ( $el == 'rss' ) {
    131 				$this->feed_type = RSS;
    132 				$this->feed_version = $attrs['version'];
    133 			}
    134 			elseif ( $el == 'feed' ) {
    135 				$this->feed_type = ATOM;
    136 				$this->feed_version = $attrs['version'];
    137 				$this->inchannel = true;
    138 			}
    139 			return;
    140 		}
    141 
    142 		if ( $el == 'channel' )
    143 		{
    144 			$this->inchannel = true;
    145 		}
    146 		elseif ($el == 'item' or $el == 'entry' )
    147 		{
    148 			$this->initem = true;
    149 			if ( isset($attrs['rdf:about']) ) {
    150 				$this->current_item['about'] = $attrs['rdf:about'];
    151 			}
    152 		}
    153 
    154 		// if we're in the default namespace of an RSS feed,
    155 		//  record textinput or image fields
    156 		elseif (
    157 			$this->feed_type == RSS and
    158 			$this->current_namespace == '' and
    159 			$el == 'textinput' )
    160 		{
    161 			$this->intextinput = true;
    162 		}
    163 
    164 		elseif (
    165 			$this->feed_type == RSS and
    166 			$this->current_namespace == '' and
    167 			$el == 'image' )
    168 		{
    169 			$this->inimage = true;
    170 		}
    171 
    172 		# handle atom content constructs
    173 		elseif ( $this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) )
    174 		{
    175 			// avoid clashing w/ RSS mod_content
    176 			if ($el == 'content' ) {
    177 				$el = 'atom_content';
    178 			}
    179 
    180 			$this->incontent = $el;
    181 
    182 		}
    183 
    184 		// if inside an Atom content construct (e.g. content or summary) field treat tags as text
    185 		elseif ($this->feed_type == ATOM and $this->incontent )
    186 		{
    187 			// if tags are inlined, then flatten
    188 			$attrs_str = join(' ',
    189 					array_map(array('MagpieRSS', 'map_attrs'),
    190 					array_keys($attrs),
    191 					array_values($attrs) ) );
    192 
    193 			$this->append_content( "<$element $attrs_str>"  );
    194 
    195 			array_unshift( $this->stack, $el );
    196 		}
    197 
    198 		// Atom support many links per containging element.
    199 		// Magpie treats link elements of type rel='alternate'
    200 		// as being equivalent to RSS's simple link element.
    201 		//
    202 		elseif ($this->feed_type == ATOM and $el == 'link' )
    203 		{
    204 			if ( isset($attrs['rel']) and $attrs['rel'] == 'alternate' )
    205 			{
    206 				$link_el = 'link';
    207 			}
    208 			else {
    209 				$link_el = 'link_' . $attrs['rel'];
    210 			}
    211 
    212 			$this->append($link_el, $attrs['href']);
    213 		}
    214 		// set stack[0] to current element
    215 		else {
    216 			array_unshift($this->stack, $el);
    217 		}
    218 	}
    219 
    220 	function feed_cdata ($p, $text) {
    221 
    222 		if ($this->feed_type == ATOM and $this->incontent)
    223 		{
    224 			$this->append_content( $text );
    225 		}
    226 		else {
    227 			$current_el = join('_', array_reverse($this->stack));
    228 			$this->append($current_el, $text);
    229 		}
    230 	}
    231 
    232 	function feed_end_element ($p, $el) {
    233 		$el = strtolower($el);
    234 
    235 		if ( $el == 'item' or $el == 'entry' )
    236 		{
    237 			$this->items[] = $this->current_item;
    238 			$this->current_item = array();
    239 			$this->initem = false;
    240 		}
    241 		elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'textinput' )
    242 		{
    243 			$this->intextinput = false;
    244 		}
    245 		elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'image' )
    246 		{
    247 			$this->inimage = false;
    248 		}
    249 		elseif ($this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) )
    250 		{
    251 			$this->incontent = false;
    252 		}
    253 		elseif ($el == 'channel' or $el == 'feed' )
    254 		{
    255 			$this->inchannel = false;
    256 		}
    257 		elseif ($this->feed_type == ATOM and $this->incontent  ) {
    258 			// balance tags properly
    259 			// note: This may not actually be necessary
    260 			if ( $this->stack[0] == $el )
    261 			{
    262 				$this->append_content("</$el>");
    263 			}
    264 			else {
    265 				$this->append_content("<$el />");
    266 			}
    267 
    268 			array_shift( $this->stack );
    269 		}
    270 		else {
    271 			array_shift( $this->stack );
    272 		}
    273 
    274 		$this->current_namespace = false;
    275 	}
    276 
    277 	function concat (&$str1, $str2="") {
    278 		if (!isset($str1) ) {
    279 			$str1="";
    280 		}
    281 		$str1 .= $str2;
    282 	}
    283 
    284 	function append_content($text) {
    285 		if ( $this->initem ) {
    286 			$this->concat( $this->current_item[ $this->incontent ], $text );
    287 		}
    288 		elseif ( $this->inchannel ) {
    289 			$this->concat( $this->channel[ $this->incontent ], $text );
    290 		}
    291 	}
    292 
    293 	// smart append - field and namespace aware
    294 	function append($el, $text) {
    295 		if (!$el) {
    296 			return;
    297 		}
    298 		if ( $this->current_namespace )
    299 		{
    300 			if ( $this->initem ) {
    301 				$this->concat(
    302 					$this->current_item[ $this->current_namespace ][ $el ], $text);
    303 			}
    304 			elseif ($this->inchannel) {
    305 				$this->concat(
    306 					$this->channel[ $this->current_namespace][ $el ], $text );
    307 			}
    308 			elseif ($this->intextinput) {
    309 				$this->concat(
    310 					$this->textinput[ $this->current_namespace][ $el ], $text );
    311 			}
    312 			elseif ($this->inimage) {
    313 				$this->concat(
    314 					$this->image[ $this->current_namespace ][ $el ], $text );
    315 			}
    316 		}
    317 		else {
    318 			if ( $this->initem ) {
    319 				$this->concat(
    320 					$this->current_item[ $el ], $text);
    321 			}
    322 			elseif ($this->intextinput) {
    323 				$this->concat(
    324 					$this->textinput[ $el ], $text );
    325 			}
    326 			elseif ($this->inimage) {
    327 				$this->concat(
    328 					$this->image[ $el ], $text );
    329 			}
    330 			elseif ($this->inchannel) {
    331 				$this->concat(
    332 					$this->channel[ $el ], $text );
    333 			}
    334 
    335 		}
    336 	}
    337 
    338 	function normalize () {
    339 		// if atom populate rss fields
    340 		if ( $this->is_atom() ) {
    341 			$this->channel['descripton'] = $this->channel['tagline'];
    342 			for ( $i = 0; $i < count($this->items); $i++) {
    343 				$item = $this->items[$i];
    344 				if ( isset($item['summary']) )
    345 					$item['description'] = $item['summary'];
    346 				if ( isset($item['atom_content']))
    347 					$item['content']['encoded'] = $item['atom_content'];
    348 
    349 				$this->items[$i] = $item;
    350 			}
    351 		}
    352 		elseif ( $this->is_rss() ) {
    353 			$this->channel['tagline'] = $this->channel['description'];
    354 			for ( $i = 0; $i < count($this->items); $i++) {
    355 				$item = $this->items[$i];
    356 				if ( isset($item['description']))
    357 					$item['summary'] = $item['description'];
    358 				if ( isset($item['content']['encoded'] ) )
    359 					$item['atom_content'] = $item['content']['encoded'];
    360 
    361 				$this->items[$i] = $item;
    362 			}
    363 		}
    364 	}
    365 
    366 	function is_rss () {
    367 		if ( $this->feed_type == RSS ) {
    368 			return $this->feed_version;
    369 		}
    370 		else {
    371 			return false;
    372 		}
    373 	}
    374 
    375 	function is_atom() {
    376 		if ( $this->feed_type == ATOM ) {
    377 			return $this->feed_version;
    378 		}
    379 		else {
    380 			return false;
    381 		}
    382 	}
    383 
    384 	function map_attrs($k, $v) {
    385 		return "$k=\"$v\"";
    386 	}
    387 
    388 	function error( $errormsg, $lvl = E_USER_WARNING ) {
    389 		if ( MAGPIE_DEBUG ) {
    390 			trigger_error( $errormsg, $lvl);
    391 		} else {
    392 			error_log( $errormsg, 0);
    393 		}
    394 	}
    395 
    396 }
    397 
    398 if ( !function_exists('fetch_rss') ) :
    399 /**
    400  * Build Magpie object based on RSS from URL.
    401  *
    402  * @since 1.5.0
    403  * @package External
    404  * @subpackage MagpieRSS
    405  *
    406  * @param string $url URL to retrieve feed.
    407  * @return MagpieRSS|false MagpieRSS object on success, false on failure.
    408  */
    409 function fetch_rss ($url) {
    410 	// initialize constants
    411 	init();
    412 
    413 	if ( !isset($url) ) {
    414 		// error("fetch_rss called without a url");
    415 		return false;
    416 	}
    417 
    418 	// if cache is disabled
    419 	if ( !MAGPIE_CACHE_ON ) {
    420 		// fetch file, and parse it
    421 		$resp = _fetch_remote_file( $url );
    422 		if ( is_success( $resp->status ) ) {
    423 			return _response_to_rss( $resp );
    424 		}
    425 		else {
    426 			// error("Failed to fetch $url and cache is off");
    427 			return false;
    428 		}
    429 	}
    430 	// else cache is ON
    431 	else {
    432 		// Flow
    433 		// 1. check cache
    434 		// 2. if there is a hit, make sure it's fresh
    435 		// 3. if cached obj fails freshness check, fetch remote
    436 		// 4. if remote fails, return stale object, or error
    437 
    438 		$cache = new RSSCache( MAGPIE_CACHE_DIR, MAGPIE_CACHE_AGE );
    439 
    440 		if (MAGPIE_DEBUG and $cache->ERROR) {
    441 			debug($cache->ERROR, E_USER_WARNING);
    442 		}
    443 
    444 		$cache_status 	 = 0;		// response of check_cache
    445 		$request_headers = array(); // HTTP headers to send with fetch
    446 		$rss 			 = 0;		// parsed RSS object
    447 		$errormsg		 = 0;		// errors, if any
    448 
    449 		if (!$cache->ERROR) {
    450 			// return cache HIT, MISS, or STALE
    451 			$cache_status = $cache->check_cache( $url );
    452 		}
    453 
    454 		// if object cached, and cache is fresh, return cached obj
    455 		if ( $cache_status == 'HIT' ) {
    456 			$rss = $cache->get( $url );
    457 			if ( isset($rss) and $rss ) {
    458 				$rss->from_cache = 1;
    459 				if ( MAGPIE_DEBUG > 1) {
    460 				debug("MagpieRSS: Cache HIT", E_USER_NOTICE);
    461 			}
    462 				return $rss;
    463 			}
    464 		}
    465 
    466 		// else attempt a conditional get
    467 
    468 		// set up headers
    469 		if ( $cache_status == 'STALE' ) {
    470 			$rss = $cache->get( $url );
    471 			if ( isset($rss->etag) and $rss->last_modified ) {
    472 				$request_headers['If-None-Match'] = $rss->etag;
    473 				$request_headers['If-Last-Modified'] = $rss->last_modified;
    474 			}
    475 		}
    476 
    477 		$resp = _fetch_remote_file( $url, $request_headers );
    478 
    479 		if (isset($resp) and $resp) {
    480 			if ($resp->status == '304' ) {
    481 				// we have the most current copy
    482 				if ( MAGPIE_DEBUG > 1) {
    483 					debug("Got 304 for $url");
    484 				}
    485 				// reset cache on 304 (at minutillo insistent prodding)
    486 				$cache->set($url, $rss);
    487 				return $rss;
    488 			}
    489 			elseif ( is_success( $resp->status ) ) {
    490 				$rss = _response_to_rss( $resp );
    491 				if ( $rss ) {
    492 					if (MAGPIE_DEBUG > 1) {
    493 						debug("Fetch successful");
    494 					}
    495 					// add object to cache
    496 					$cache->set( $url, $rss );
    497 					return $rss;
    498 				}
    499 			}
    500 			else {
    501 				$errormsg = "Failed to fetch $url. ";
    502 				if ( $resp->error ) {
    503 					# compensate for Snoopy's annoying habbit to tacking
    504 					# on '\n'
    505 					$http_error = substr($resp->error, 0, -2);
    506 					$errormsg .= "(HTTP Error: $http_error)";
    507 				}
    508 				else {
    509 					$errormsg .=  "(HTTP Response: " . $resp->response_code .')';
    510 				}
    511 			}
    512 		}
    513 		else {
    514 			$errormsg = "Unable to retrieve RSS file for unknown reasons.";
    515 		}
    516 
    517 		// else fetch failed
    518 
    519 		// attempt to return cached object
    520 		if ($rss) {
    521 			if ( MAGPIE_DEBUG ) {
    522 				debug("Returning STALE object for $url");
    523 			}
    524 			return $rss;
    525 		}
    526 
    527 		// else we totally failed
    528 		// error( $errormsg );
    529 
    530 		return false;
    531 
    532 	} // end if ( !MAGPIE_CACHE_ON ) {
    533 } // end fetch_rss()
    534 endif;
    535 
    536 /**
    537  * Retrieve URL headers and content using WP HTTP Request API.
    538  *
    539  * @since 1.5.0
    540  * @package External
    541  * @subpackage MagpieRSS
    542  *
    543  * @param string $url URL to retrieve
    544  * @param array $headers Optional. Headers to send to the URL.
    545  * @return Snoopy style response
    546  */
    547 function _fetch_remote_file($url, $headers = "" ) {
    548 	$resp = wp_safe_remote_request( $url, array( 'headers' => $headers, 'timeout' => MAGPIE_FETCH_TIME_OUT ) );
    549 	if ( is_wp_error($resp) ) {
    550 		$error = array_shift($resp->errors);
    551 
    552 		$resp = new stdClass;
    553 		$resp->status = 500;
    554 		$resp->response_code = 500;
    555 		$resp->error = $error[0] . "\n"; //\n = Snoopy compatibility
    556 		return $resp;
    557 	}
    558 
    559 	// Snoopy returns headers unprocessed.
    560 	// Also note, WP_HTTP lowercases all keys, Snoopy did not.
    561 	$return_headers = array();
    562 	foreach ( wp_remote_retrieve_headers( $resp ) as $key => $value ) {
    563 		if ( !is_array($value) ) {
    564 			$return_headers[] = "$key: $value";
    565 		} else {
    566 			foreach ( $value as $v )
    567 				$return_headers[] = "$key: $v";
    568 		}
    569 	}
    570 
    571 	$response = new stdClass;
    572 	$response->status = wp_remote_retrieve_response_code( $resp );
    573 	$response->response_code = wp_remote_retrieve_response_code( $resp );
    574 	$response->headers = $return_headers;
    575 	$response->results = wp_remote_retrieve_body( $resp );
    576 
    577 	return $response;
    578 }
    579 
    580 /**
    581  * Retrieve
    582  *
    583  * @since 1.5.0
    584  * @package External
    585  * @subpackage MagpieRSS
    586  *
    587  * @param array $resp
    588  * @return MagpieRSS|bool
    589  */
    590 function _response_to_rss ($resp) {
    591 	$rss = new MagpieRSS( $resp->results );
    592 
    593 	// if RSS parsed successfully
    594 	if ( $rss && (!isset($rss->ERROR) || !$rss->ERROR) ) {
    595 
    596 		// find Etag, and Last-Modified
    597 		foreach ( (array) $resp->headers as $h) {
    598 			// 2003-03-02 - Nicola Asuni (www.tecnick.com) - fixed bug "Undefined offset: 1"
    599 			if (strpos($h, ": ")) {
    600 				list($field, $val) = explode(": ", $h, 2);
    601 			}
    602 			else {
    603 				$field = $h;
    604 				$val = "";
    605 			}
    606 
    607 			if ( $field == 'etag' ) {
    608 				$rss->etag = $val;
    609 			}
    610 
    611 			if ( $field == 'last-modified' ) {
    612 				$rss->last_modified = $val;
    613 			}
    614 		}
    615 
    616 		return $rss;
    617 	} // else construct error message
    618 	else {
    619 		$errormsg = "Failed to parse RSS file.";
    620 
    621 		if ($rss) {
    622 			$errormsg .= " (" . $rss->ERROR . ")";
    623 		}
    624 		// error($errormsg);
    625 
    626 		return false;
    627 	} // end if ($rss and !$rss->error)
    628 }
    629 
    630 /**
    631  * Set up constants with default values, unless user overrides.
    632  *
    633  * @since 1.5.0
    634  * @package External
    635  * @subpackage MagpieRSS
    636  */
    637 function init () {
    638 	if ( defined('MAGPIE_INITALIZED') ) {
    639 		return;
    640 	}
    641 	else {
    642 		define('MAGPIE_INITALIZED', 1);
    643 	}
    644 
    645 	if ( !defined('MAGPIE_CACHE_ON') ) {
    646 		define('MAGPIE_CACHE_ON', 1);
    647 	}
    648 
    649 	if ( !defined('MAGPIE_CACHE_DIR') ) {
    650 		define('MAGPIE_CACHE_DIR', './cache');
    651 	}
    652 
    653 	if ( !defined('MAGPIE_CACHE_AGE') ) {
    654 		define('MAGPIE_CACHE_AGE', 60*60); // one hour
    655 	}
    656 
    657 	if ( !defined('MAGPIE_CACHE_FRESH_ONLY') ) {
    658 		define('MAGPIE_CACHE_FRESH_ONLY', 0);
    659 	}
    660 
    661 		if ( !defined('MAGPIE_DEBUG') ) {
    662 		define('MAGPIE_DEBUG', 0);
    663 	}
    664 
    665 	if ( !defined('MAGPIE_USER_AGENT') ) {
    666 		$ua = 'WordPress/' . $GLOBALS['wp_version'];
    667 
    668 		if ( MAGPIE_CACHE_ON ) {
    669 			$ua = $ua . ')';
    670 		}
    671 		else {
    672 			$ua = $ua . '; No cache)';
    673 		}
    674 
    675 		define('MAGPIE_USER_AGENT', $ua);
    676 	}
    677 
    678 	if ( !defined('MAGPIE_FETCH_TIME_OUT') ) {
    679 		define('MAGPIE_FETCH_TIME_OUT', 2);	// 2 second timeout
    680 	}
    681 
    682 	// use gzip encoding to fetch rss files if supported?
    683 	if ( !defined('MAGPIE_USE_GZIP') ) {
    684 		define('MAGPIE_USE_GZIP', true);
    685 	}
    686 }
    687 
    688 function is_info ($sc) {
    689 	return $sc >= 100 && $sc < 200;
    690 }
    691 
    692 function is_success ($sc) {
    693 	return $sc >= 200 && $sc < 300;
    694 }
    695 
    696 function is_redirect ($sc) {
    697 	return $sc >= 300 && $sc < 400;
    698 }
    699 
    700 function is_error ($sc) {
    701 	return $sc >= 400 && $sc < 600;
    702 }
    703 
    704 function is_client_error ($sc) {
    705 	return $sc >= 400 && $sc < 500;
    706 }
    707 
    708 function is_server_error ($sc) {
    709 	return $sc >= 500 && $sc < 600;
    710 }
    711 
    712 class RSSCache {
    713 	var $BASE_CACHE;	// where the cache files are stored
    714 	var $MAX_AGE	= 43200;  		// when are files stale, default twelve hours
    715 	var $ERROR 		= '';			// accumulate error messages
    716 
    717 	/**
    718 	 * PHP5 constructor.
    719 	 */
    720 	function __construct( $base = '', $age = '' ) {
    721 		$this->BASE_CACHE = WP_CONTENT_DIR . '/cache';
    722 		if ( $base ) {
    723 			$this->BASE_CACHE = $base;
    724 		}
    725 		if ( $age ) {
    726 			$this->MAX_AGE = $age;
    727 		}
    728 
    729 	}
    730 
    731 	/**
    732 	 * PHP4 constructor.
    733 	 */
    734 	public function RSSCache( $base = '', $age = '' ) {
    735 		self::__construct( $base, $age );
    736 	}
    737 
    738 /*=======================================================================*\
    739 	Function:	set
    740 	Purpose:	add an item to the cache, keyed on url
    741 	Input:		url from which the rss file was fetched
    742 	Output:		true on success
    743 \*=======================================================================*/
    744 	function set ($url, $rss) {
    745 		$cache_option = 'rss_' . $this->file_name( $url );
    746 
    747 		set_transient($cache_option, $rss, $this->MAX_AGE);
    748 
    749 		return $cache_option;
    750 	}
    751 
    752 /*=======================================================================*\
    753 	Function:	get
    754 	Purpose:	fetch an item from the cache
    755 	Input:		url from which the rss file was fetched
    756 	Output:		cached object on HIT, false on MISS
    757 \*=======================================================================*/
    758 	function get ($url) {
    759 		$this->ERROR = "";
    760 		$cache_option = 'rss_' . $this->file_name( $url );
    761 
    762 		if ( ! $rss = get_transient( $cache_option ) ) {
    763 			$this->debug(
    764 				"Cache doesn't contain: $url (cache option: $cache_option)"
    765 			);
    766 			return 0;
    767 		}
    768 
    769 		return $rss;
    770 	}
    771 
    772 /*=======================================================================*\
    773 	Function:	check_cache
    774 	Purpose:	check a url for membership in the cache
    775 				and whether the object is older then MAX_AGE (ie. STALE)
    776 	Input:		url from which the rss file was fetched
    777 	Output:		cached object on HIT, false on MISS
    778 \*=======================================================================*/
    779 	function check_cache ( $url ) {
    780 		$this->ERROR = "";
    781 		$cache_option = 'rss_' . $this->file_name( $url );
    782 
    783 		if ( get_transient($cache_option) ) {
    784 			// object exists and is current
    785 				return 'HIT';
    786 		} else {
    787 			// object does not exist
    788 			return 'MISS';
    789 		}
    790 	}
    791 
    792 /*=======================================================================*\
    793 	Function:	serialize
    794 \*=======================================================================*/
    795 	function serialize ( $rss ) {
    796 		return serialize( $rss );
    797 	}
    798 
    799 /*=======================================================================*\
    800 	Function:	unserialize
    801 \*=======================================================================*/
    802 	function unserialize ( $data ) {
    803 		return unserialize( $data );
    804 	}
    805 
    806 /*=======================================================================*\
    807 	Function:	file_name
    808 	Purpose:	map url to location in cache
    809 	Input:		url from which the rss file was fetched
    810 	Output:		a file name
    811 \*=======================================================================*/
    812 	function file_name ($url) {
    813 		return md5( $url );
    814 	}
    815 
    816 /*=======================================================================*\
    817 	Function:	error
    818 	Purpose:	register error
    819 \*=======================================================================*/
    820 	function error ($errormsg, $lvl=E_USER_WARNING) {
    821 		$this->ERROR = $errormsg;
    822 		if ( MAGPIE_DEBUG ) {
    823 			trigger_error( $errormsg, $lvl);
    824 		}
    825 		else {
    826 			error_log( $errormsg, 0);
    827 		}
    828 	}
    829 			function debug ($debugmsg, $lvl=E_USER_NOTICE) {
    830 		if ( MAGPIE_DEBUG ) {
    831 			$this->error("MagpieRSS [debug] $debugmsg", $lvl);
    832 		}
    833 	}
    834 }
    835 
    836 if ( !function_exists('parse_w3cdtf') ) :
    837 function parse_w3cdtf ( $date_str ) {
    838 
    839 	# regex to match wc3dtf
    840 	$pat = "/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(:(\d{2}))?(?:([-+])(\d{2}):?(\d{2})|(Z))?/";
    841 
    842 	if ( preg_match( $pat, $date_str, $match ) ) {
    843 		list( $year, $month, $day, $hours, $minutes, $seconds) =
    844 			array( $match[1], $match[2], $match[3], $match[4], $match[5], $match[7]);
    845 
    846 		# calc epoch for current date assuming GMT
    847 		$epoch = gmmktime( $hours, $minutes, $seconds, $month, $day, $year);
    848 
    849 		$offset = 0;
    850 		if ( $match[11] == 'Z' ) {
    851 			# zulu time, aka GMT
    852 		}
    853 		else {
    854 			list( $tz_mod, $tz_hour, $tz_min ) =
    855 				array( $match[8], $match[9], $match[10]);
    856 
    857 			# zero out the variables
    858 			if ( ! $tz_hour ) { $tz_hour = 0; }
    859 			if ( ! $tz_min ) { $tz_min = 0; }
    860 
    861 			$offset_secs = (($tz_hour*60)+$tz_min)*60;
    862 
    863 			# is timezone ahead of GMT?  then subtract offset
    864 			#
    865 			if ( $tz_mod == '+' ) {
    866 				$offset_secs = $offset_secs * -1;
    867 			}
    868 
    869 			$offset = $offset_secs;
    870 		}
    871 		$epoch = $epoch + $offset;
    872 		return $epoch;
    873 	}
    874 	else {
    875 		return -1;
    876 	}
    877 }
    878 endif;
    879 
    880 if ( !function_exists('wp_rss') ) :
    881 /**
    882  * Display all RSS items in a HTML ordered list.
    883  *
    884  * @since 1.5.0
    885  * @package External
    886  * @subpackage MagpieRSS
    887  *
    888  * @param string $url URL of feed to display. Will not auto sense feed URL.
    889  * @param int $num_items Optional. Number of items to display, default is all.
    890  */
    891 function wp_rss( $url, $num_items = -1 ) {
    892 	if ( $rss = fetch_rss( $url ) ) {
    893 		echo '<ul>';
    894 
    895 		if ( $num_items !== -1 ) {
    896 			$rss->items = array_slice( $rss->items, 0, $num_items );
    897 		}
    898 
    899 		foreach ( (array) $rss->items as $item ) {
    900 			printf(
    901 				'<li><a href="%1$s" title="%2$s">%3$s</a></li>',
    902 				esc_url( $item['link'] ),
    903 				esc_attr( strip_tags( $item['description'] ) ),
    904 				esc_html( $item['title'] )
    905 			);
    906 		}
    907 
    908 		echo '</ul>';
    909 	} else {
    910 		_e( 'An error has occurred, which probably means the feed is down. Try again later.' );
    911 	}
    912 }
    913 endif;
    914 
    915 if ( !function_exists('get_rss') ) :
    916 /**
    917  * Display RSS items in HTML list items.
    918  *
    919  * You have to specify which HTML list you want, either ordered or unordered
    920  * before using the function. You also have to specify how many items you wish
    921  * to display. You can't display all of them like you can with wp_rss()
    922  * function.
    923  *
    924  * @since 1.5.0
    925  * @package External
    926  * @subpackage MagpieRSS
    927  *
    928  * @param string $url URL of feed to display. Will not auto sense feed URL.
    929  * @param int $num_items Optional. Number of items to display, default is all.
    930  * @return bool False on failure.
    931  */
    932 function get_rss ($url, $num_items = 5) { // Like get posts, but for RSS
    933 	$rss = fetch_rss($url);
    934 	if ( $rss ) {
    935 		$rss->items = array_slice($rss->items, 0, $num_items);
    936 		foreach ( (array) $rss->items as $item ) {
    937 			echo "<li>\n";
    938 			echo "<a href='$item[link]' title='$item[description]'>";
    939 			echo esc_html($item['title']);
    940 			echo "</a><br />\n";
    941 			echo "</li>\n";
    942 		}
    943 	} else {
    944 		return false;
    945 	}
    946 }
    947 endif;