* MagpieRSS: a simple RSS integration tool
* A compiled file for RSS syndication
* @author Kellan Elliott-McCrea <kellan@protest.net>
* @deprecated 3.0.0 Use SimplePie instead.
* Deprecated. Use SimplePie (class-simplepie.php) instead.
_deprecated_file( basename( __FILE__ ), '3.0.0', WPINC . '/class-simplepie.php' );
* Fires before MagpieRSS is loaded, to optionally replace it.
do_action( 'load_feed_engine' );
/** RSS feed constant. */
define('MAGPIE_USER_AGENT', 'WordPress/' . $GLOBALS['wp_version']);
var $current_item = array(); // item currently being parsed
var $items = array(); // collection of parsed items
var $channel = array(); // hash of channel fields
var $textinput = array();
var $stack = array(); // parser stack
var $incontent = false; // if in Atom <content mode="xml"> field
var $intextinput = false;
var $current_namespace = false;
var $_CONTENT_CONSTRUCTS = array('content', 'summary', 'info', 'title', 'tagline', 'copyright');
function __construct( $source ) {
# Check if PHP xml isn't compiled
if ( ! function_exists('xml_parser_create') ) {
return trigger_error( "PHP's XML extension is not available. Please contact your hosting provider to enable PHP's XML extension." );
$parser = xml_parser_create();
# pass in parser, and a reference to this object
xml_set_object( $this->parser, $this );
xml_set_element_handler($this->parser,
'feed_start_element', 'feed_end_element' );
xml_set_character_data_handler( $this->parser, 'feed_cdata' );
$status = xml_parse( $this->parser, $source );
$errorcode = xml_get_error_code( $this->parser );
if ( $errorcode != XML_ERROR_NONE ) {
$xml_error = xml_error_string( $errorcode );
$error_line = xml_get_current_line_number($this->parser);
$error_col = xml_get_current_column_number($this->parser);
$errormsg = "$xml_error at line $error_line, column $error_col";
$this->error( $errormsg );
xml_parser_free( $this->parser );
public function MagpieRSS( $source ) {
self::__construct( $source );
function feed_start_element($p, $element, &$attrs) {
$el = $element = strtolower($element);
$attrs = array_change_key_case($attrs, CASE_LOWER);
// check for a namespace, and split if found
if ( strpos( $element, ':' ) ) {
list($ns, $el) = explode( ':', $element, 2);
if ( $ns and $ns != 'rdf' ) {
$this->current_namespace = $ns;
# if feed type isn't set, then this is first element of feed
# identify feed from root element
if (!isset($this->feed_type) ) {
$this->feed_version = '1.0';
elseif ( $el == 'rss' ) {
$this->feed_version = $attrs['version'];
elseif ( $el == 'feed' ) {
$this->feed_version = $attrs['version'];
elseif ($el == 'item' or $el == 'entry' )
if ( isset($attrs['rdf:about']) ) {
$this->current_item['about'] = $attrs['rdf:about'];
// if we're in the default namespace of an RSS feed,
// record textinput or image fields
$this->feed_type == RSS and
$this->current_namespace == '' and
$this->intextinput = true;
$this->feed_type == RSS and
$this->current_namespace == '' and
# handle atom content constructs
elseif ( $this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) )
// avoid clashing w/ RSS mod_content
// if inside an Atom content construct (e.g. content or summary) field treat tags as text
elseif ($this->feed_type == ATOM and $this->incontent )
// if tags are inlined, then flatten
array_map(array('MagpieRSS', 'map_attrs'),
array_values($attrs) ) );
$this->append_content( "<$element $attrs_str>" );
array_unshift( $this->stack, $el );
// Atom support many links per containging element.
// Magpie treats link elements of type rel='alternate'
// as being equivalent to RSS's simple link element.
elseif ($this->feed_type == ATOM and $el == 'link' )
if ( isset($attrs['rel']) and $attrs['rel'] == 'alternate' )
$link_el = 'link_' . $attrs['rel'];
$this->append($link_el, $attrs['href']);
// set stack[0] to current element
array_unshift($this->stack, $el);
function feed_cdata ($p, $text) {
if ($this->feed_type == ATOM and $this->incontent)
$this->append_content( $text );
$current_el = join('_', array_reverse($this->stack));
$this->append($current_el, $text);
function feed_end_element ($p, $el) {
if ( $el == 'item' or $el == 'entry' )
$this->items[] = $this->current_item;
$this->current_item = array();
elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'textinput' )
$this->intextinput = false;
elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'image' )
elseif ($this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) )
$this->incontent = false;
elseif ($el == 'channel' or $el == 'feed' )
$this->inchannel = false;
elseif ($this->feed_type == ATOM and $this->incontent ) {
// note: This may not actually be necessary
if ( $this->stack[0] == $el )
$this->append_content("</$el>");
$this->append_content("<$el />");
array_shift( $this->stack );
array_shift( $this->stack );
$this->current_namespace = false;
function concat (&$str1, $str2="") {
function append_content($text) {
$this->concat( $this->current_item[ $this->incontent ], $text );
elseif ( $this->inchannel ) {
$this->concat( $this->channel[ $this->incontent ], $text );
// smart append - field and namespace aware
function append($el, $text) {
if ( $this->current_namespace )
$this->current_item[ $this->current_namespace ][ $el ], $text);
elseif ($this->inchannel) {
$this->channel[ $this->current_namespace][ $el ], $text );
elseif ($this->intextinput) {
$this->textinput[ $this->current_namespace][ $el ], $text );
elseif ($this->inimage) {
$this->image[ $this->current_namespace ][ $el ], $text );
$this->current_item[ $el ], $text);
elseif ($this->intextinput) {
$this->textinput[ $el ], $text );
elseif ($this->inimage) {
$this->image[ $el ], $text );
elseif ($this->inchannel) {
$this->channel[ $el ], $text );
// if atom populate rss fields
if ( $this->is_atom() ) {
$this->channel['descripton'] = $this->channel['tagline'];
for ( $i = 0; $i < count($this->items); $i++) {
$item = $this->items[$i];
if ( isset($item['summary']) )
$item['description'] = $item['summary'];
if ( isset($item['atom_content']))
$item['content']['encoded'] = $item['atom_content'];
$this->items[$i] = $item;
elseif ( $this->is_rss() ) {
$this->channel['tagline'] = $this->channel['description'];
for ( $i = 0; $i < count($this->items); $i++) {
$item = $this->items[$i];
if ( isset($item['description']))
$item['summary'] = $item['description'];
if ( isset($item['content']['encoded'] ) )
$item['atom_content'] = $item['content']['encoded'];
$this->items[$i] = $item;
if ( $this->feed_type == RSS ) {
return $this->feed_version;
if ( $this->feed_type == ATOM ) {
return $this->feed_version;
function map_attrs($k, $v) {
function error( $errormsg, $lvl = E_USER_WARNING ) {
trigger_error( $errormsg, $lvl);
error_log( $errormsg, 0);
if ( !function_exists('fetch_rss') ) :
* Build Magpie object based on RSS from URL.
* @param string $url URL to retrieve feed.
* @return MagpieRSS|false MagpieRSS object on success, false on failure.
function fetch_rss ($url) {
// error("fetch_rss called without a url");
if ( !MAGPIE_CACHE_ON ) {
// fetch file, and parse it
$resp = _fetch_remote_file( $url );
if ( is_success( $resp->status ) ) {
return _response_to_rss( $resp );
// error("Failed to fetch $url and cache is off");
// 2. if there is a hit, make sure it's fresh
// 3. if cached obj fails freshness check, fetch remote
// 4. if remote fails, return stale object, or error
$cache = new RSSCache( MAGPIE_CACHE_DIR, MAGPIE_CACHE_AGE );
if (MAGPIE_DEBUG and $cache->ERROR) {
debug($cache->ERROR, E_USER_WARNING);
$cache_status = 0; // response of check_cache
$request_headers = array(); // HTTP headers to send with fetch
$rss = 0; // parsed RSS object
$errormsg = 0; // errors, if any
// return cache HIT, MISS, or STALE
$cache_status = $cache->check_cache( $url );
// if object cached, and cache is fresh, return cached obj
if ( $cache_status == 'HIT' ) {
$rss = $cache->get( $url );
if ( isset($rss) and $rss ) {
debug("MagpieRSS: Cache HIT", E_USER_NOTICE);
// else attempt a conditional get
if ( $cache_status == 'STALE' ) {
$rss = $cache->get( $url );
if ( isset($rss->etag) and $rss->last_modified ) {
$request_headers['If-None-Match'] = $rss->etag;
$request_headers['If-Last-Modified'] = $rss->last_modified;
$resp = _fetch_remote_file( $url, $request_headers );
if (isset($resp) and $resp) {
if ($resp->status == '304' ) {
// we have the most current copy
debug("Got 304 for $url");
// reset cache on 304 (at minutillo insistent prodding)
elseif ( is_success( $resp->status ) ) {
$rss = _response_to_rss( $resp );
debug("Fetch successful");
$cache->set( $url, $rss );