diff options
| author | donncha <donncha@7be80a69-a1ef-0310-a953-fb0f7c49ff36> | 2006-06-27 11:03:10 +0000 |
|---|---|---|
| committer | donncha <donncha@7be80a69-a1ef-0310-a953-fb0f7c49ff36> | 2006-06-27 11:03:10 +0000 |
| commit | 8db1e81228b0e295199f6e3ee8a99d223c48f8c2 (patch) | |
| tree | 131d8b23b9e939a9c23f24ec650b343efa918c5c /wp-includes/rss.php | |
| parent | 13aff523358403d08dc7fcb0d844a2e6c12df41a (diff) | |
| download | wordpress-mu-8db1e81228b0e295199f6e3ee8a99d223c48f8c2.tar.gz wordpress-mu-8db1e81228b0e295199f6e3ee8a99d223c48f8c2.tar.xz wordpress-mu-8db1e81228b0e295199f6e3ee8a99d223c48f8c2.zip | |
WP Merge
git-svn-id: http://svn.automattic.com/wordpress-mu/trunk@599 7be80a69-a1ef-0310-a953-fb0f7c49ff36
Diffstat (limited to 'wp-includes/rss.php')
| -rw-r--r-- | wp-includes/rss.php | 1619 |
1 files changed, 975 insertions, 644 deletions
diff --git a/wp-includes/rss.php b/wp-includes/rss.php index 2f99d3f..e0c1d84 100644 --- a/wp-includes/rss.php +++ b/wp-includes/rss.php @@ -1,793 +1,1159 @@ <?php -/* - * Project: MagpieRSS: a simple RSS integration tool - * File: A compiled file for RSS syndication - * Author: Kellan Elliott-McCrea <kellan@protest.net> - * Version: 0.51 - * License: GPL + +/* Much of the code in this file was taken from MagpieRSS + * by Kellan Elliott-McCrea <kellan@protest.net> which is + * released under the GPL license. + * + * The lastest version of MagpieRSS can be obtained from: + * http://magpierss.sourceforge.net */ -define('RSS', 'RSS'); -define('ATOM', 'Atom'); -define('MAGPIE_USER_AGENT', 'WordPress/' . $wp_version); +function fetch_rss($url) { + $url = apply_filters('fetch_rss_url', $url); -class MagpieRSS { - var $parser; - 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 $image = array(); - var $feed_type; - var $feed_version; + $feeder = new WP_Feeder(); - // parser variables - var $stack = array(); // parser stack - var $inchannel = false; - var $initem = false; - var $incontent = false; // if in Atom <content mode="xml"> field - var $intextinput = false; - var $inimage = false; - var $current_field = ''; - var $current_namespace = false; + $feed = $feeder->get($url); - //var $ERROR = ""; + $magpie = $feed->to_magpie(); - var $_CONTENT_CONSTRUCTS = array('content', 'summary', 'info', 'title', 'tagline', 'copyright'); + return $magpie; +} - function MagpieRSS ($source) { +class WP_Feeder { + var $url, $http_client, $last_fetch, $wp_object_cache, $cache; + var $redirects = 0; + var $max_redirects = 3; + var $cache_redirects = true; + + function WP_Feeder () { + global $wp_object_cache; + + if ( $wp_object_cache->cache_enabled ) { + $this->wp_object_cache = true; + } else { + $this->wp_object_cache = false; + $this->cache = new RSSCache(); + } + } - # if PHP xml isn't compiled in, die - # - if ( !function_exists('xml_parser_create') ) - trigger_error( "Failed to load PHP's XML Extension. http://www.php.net/manual/en/ref.xml.php" ); + function get ($url) { + $cached = false; - $parser = @xml_parser_create(); + $feed = $this->cache_get($url); - if ( !is_resource($parser) ) - trigger_error( "Failed to create an instance of PHP's XML parser. http://www.php.net/manual/en/ref.xml.php"); + if ( is_object($feed) ) { + $cached = true; + } else { + unset($feed); + $this->fetch($url); - $this->parser = $parser; - - # pass in parser, and a reference to this object - # setup handlers - # - xml_set_object( $this->parser, $this ); - xml_set_element_handler($this->parser, - 'feed_start_element', 'feed_end_element' ); + $feed = new WP_Feed($this->http_client); + } - xml_set_character_data_handler( $this->parser, 'feed_cdata' ); + // Handle redirects + if ( $feed->status >= 300 && $feed->status < 400 && $this->redirects < $this->max_redirects ) { + ++$this->redirects; - $status = xml_parse( $this->parser, $source ); + if ( $this->cache_redirects && !$cached ) + $this->cache_set($url, $feed); - if (! $status ) { - $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 ); - } + return $this->get($feed->redirect_location); } - xml_parser_free( $this->parser ); + if ( !$cached ) + $this->cache_set($url, $feed); - $this->normalize(); + return $feed; } - function feed_start_element($p, $element, &$attrs) { - $el = $element = strtolower($element); - $attrs = array_change_key_case($attrs, CASE_LOWER); + function fetch ($url) { + $this->last_fetch = $url; + $parts = parse_url($url); + $url = ($parts['path'] ? $parts['path'] : '/') . ($parts['query'] ? '?'.$parts['query'] : ''); + $this->http_client = new HttpClient('', 80); + $this->http_client->handle_redirects = false; + $this->http_client->host = $parts['host']; + $this->http_client->port = $parts['port'] ? $parts['port'] : 80; + $this->http_client->user_agent = 'WordPress ' . $GLOBALS['wp_version'] . ' Feed Client'; + $this->http_client->get($url); + } + + function cache_get ($url) { + if ( $this->wp_object_cache ) + return unserialize(wp_cache_get($url, 'rss')); - // check for a namespace, and split if found - $ns = false; - if ( strpos( $element, ':' ) ) { - list($ns, $el) = split( ':', $element, 2); - } - if ( $ns and $ns != 'rdf' ) { - $this->current_namespace = $ns; - } + return $this->cache->get($url); + } + + function cache_set ($url, $object) { + if ( $this->wp_object_cache ) + return wp_cache_set($url, serialize($object), 'rss', 3600); + + return $this->cache->set($url, $object); + } +} - # if feed type isn't set, then this is first element of feed - # identify feed from root element - # - if (!isset($this->feed_type) ) { - if ( $el == 'rdf' ) { - $this->feed_type = RSS; - $this->feed_version = '1.0'; - } - elseif ( $el == 'rss' ) { - $this->feed_type = RSS; - $this->feed_version = $attrs['version']; - } - elseif ( $el == 'feed' ) { - $this->feed_type = ATOM; - $this->feed_version = $attrs['version']; - $this->inchannel = true; - } - return; - } +class RSSCache { + var $BASE_CACHE = 'wp-content/cache'; // where the cache files are stored + var $MAX_AGE = 43200; // when are files stale, default twelve hours + var $ERROR = ''; // accumulate error messages - if ( $el == 'channel' ) - { - $this->inchannel = true; + function RSSCache ($base='', $age='') { + if ( $base ) { + $this->BASE_CACHE = $base; } - elseif ($el == 'item' or $el == 'entry' ) - { - $this->initem = true; - if ( isset($attrs['rdf:about']) ) { - $this->current_item['about'] = $attrs['rdf:about']; - } + if ( $age ) { + $this->MAX_AGE = $age; } - // if we're in the default namespace of an RSS feed, - // record textinput or image fields - elseif ( - $this->feed_type == RSS and - $this->current_namespace == '' and - $el == 'textinput' ) - { - $this->intextinput = true; - } + } - elseif ( - $this->feed_type == RSS and - $this->current_namespace == '' and - $el == 'image' ) - { - $this->inimage = true; - } + function set ($url, $rss) { + global $wpdb; + $cache_option = 'rss_' . $this->file_name( $url ); + $cache_timestamp = 'rss_' . $this->file_name( $url ) . '_ts'; - # handle atom content constructs - elseif ( $this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) ) - { - // avoid clashing w/ RSS mod_content - if ($el == 'content' ) { - $el = 'atom_content'; - } + if ( !$wpdb->get_var("SELECT option_name FROM $wpdb->options WHERE option_name = '$cache_option'") ) + add_option($cache_option, '', '', 'no'); + if ( !$wpdb->get_var("SELECT option_name FROM $wpdb->options WHERE option_name = '$cache_timestamp'") ) + add_option($cache_timestamp, '', '', 'no'); + + update_option($cache_option, $rss); + update_option($cache_timestamp, time() ); - $this->incontent = $el; + return $cache_option; + } + function get ($url) { + $this->ERROR = ""; + $cache_option = 'rss_' . $this->file_name( $url ); + if ( ! get_option( $cache_option ) ) { + $this->debug( + "Cache doesn't contain: $url (cache option: $cache_option)" + ); + return 0; } - // 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 - $attrs_str = join(' ', - array_map('map_attrs', - array_keys($attrs), - array_values($attrs) ) ); + $rss = get_option( $cache_option ); - $this->append_content( "<$element $attrs_str>" ); + return $rss; + } - array_unshift( $this->stack, $el ); - } + function check_cache ( $url ) { + $this->ERROR = ""; + $cache_option = $this->file_name( $url ); + $cache_timestamp = 'rss_' . $this->file_name( $url ) . '_ts'; - // 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'; + if ( $mtime = get_option($cache_timestamp) ) { + // find how long ago the file was added to the cache + // and whether that is longer then MAX_AGE + $age = time() - $mtime; + if ( $this->MAX_AGE > $age ) { + // object exists and is current + return 'HIT'; } else { - $link_el = 'link_' . $attrs['rel']; + // object exists but is old + return 'STALE'; } - - $this->append($link_el, $attrs['href']); } - // set stack[0] to current element else { - array_unshift($this->stack, $el); + // object does not exist + return 'MISS'; } } + function serialize ( $rss ) { + return serialize( $rss ); + } + function unserialize ( $data ) { + return unserialize( $data ); + } - function feed_cdata ($p, $text) { + function file_name ($url) { + return md5( $url ); + } - if ($this->feed_type == ATOM and $this->incontent) - { - $this->append_content( $text ); + function error ($errormsg, $lvl=E_USER_WARNING) { + // append PHP's error message if track_errors enabled + if ( isset($php_errormsg) ) { + $errormsg .= " ($php_errormsg)"; + } + $this->ERROR = $errormsg; + if ( MAGPIE_DEBUG ) { + trigger_error( $errormsg, $lvl); } else { - $current_el = join('_', array_reverse($this->stack)); - $this->append($current_el, $text); + error_log( $errormsg, 0); + } + } + function debug ($debugmsg, $lvl=E_USER_NOTICE) { + if ( MAGPIE_DEBUG ) { + $this->error("MagpieRSS [debug] $debugmsg", $lvl); } } +} - function feed_end_element ($p, $el) { - $el = strtolower($el); +class WP_Feed { + var $status; + var $raw_xml; + var $last_updated; + var $tree; + var $items; + var $children; - if ( $el == 'item' or $el == 'entry' ) - { - $this->items[] = $this->current_item; - $this->current_item = array(); - $this->initem = false; - } - 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' ) - { - $this->inimage = false; + var $parser; + var $feed_type; + var $feed_version; + var $stack = array(); + + function WP_Feed ($source) + { + # if PHP xml isn't compiled in, die + # + if (!function_exists('xml_parser_create')) { + $this->error( "Failed to load PHP's XML Extension. " . + "http://www.php.net/manual/en/ref.xml.php", + E_USER_ERROR ); } - elseif ($this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) ) - { - $this->incontent = false; + + // Handle overloaded arg (string or HttpClient object) + if ( is_object($source) ) { + if ( $source->status >= 200 && $source->status < 300) { + $this->etag = $source->headers['etag']; + $this->last_modified = $source->headers['last-modified']; + $source = $source->content; + } else { + $this->scour(); + $this->status = $source->status; + $this->redirect_location = $source->headers->location; + $this->bathe(); + return; + } } - elseif ($el == 'channel' or $el == 'feed' ) - { - $this->inchannel = false; + + list($parser, $source) = $this->create_parser($source, 'UTF-8', null, true); + + if (!is_resource($parser)) { + $this->error( "Failed to create an instance of PHP's XML parser. " . + "http://www.php.net/manual/en/ref.xml.php", + E_USER_ERROR ); } - elseif ($this->feed_type == ATOM and $this->incontent ) { - // balance tags properly - // note: i don't think this is actually neccessary - if ( $this->stack[0] == $el ) - { - $this->append_content("</$el>"); - } - else { - $this->append_content("<$el />"); + + $this->parser = $parser; + + # pass in parser, and a reference to this object + # setup handlers + # + xml_set_object($this->parser, $this); + xml_set_element_handler($this->parser, 'start_element', 'end_element'); + xml_set_character_data_handler( $this->parser, 'cdata'); + + $status = xml_parse( $this->parser, $source ); + + if (! $status ) { + $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 ); } + } - array_shift( $this->stack ); + // SUPER SLOPPY FEED DISCOVERY!! TO-DO: AXE THIS CRAP!! + if ( !is_object($this->feed) || !method_exists($this->feed, 'to_xml') ) { + if ( preg_match_all('/<link [^>]*href=([^ >]+)[^>]+>/i', $source, $matches) ) { + $types = array('rss', 'atom'); + foreach ( $types as $type ) + foreach ( $matches[0] as $key => $link ) + if ( preg_match('/rel=.alternate./', $link) && preg_match("/type=[^ >]*{$type}[^ >]*/", $link) ) + break 2; + $this->scour(); + $this->redirect_location = 'http://xml.wordpress.com/get/' . trim($matches[1][$key], '\'"'); + $this->status = 301; + return; + } else { + $this->scour(); + $this->status = 404; + return; + } + } else { + $this->status = 200; } - else { - array_shift( $this->stack ); + + xml_parser_free( $this->parser ); + unset($this->parser); + + $this->bathe(); + } + + function to_xml() { + if ( is_object($this->feed) && method_exists($this->feed, 'to_xml') ) + return $this->feed->to_xml(); + + return false; + } + + // Called internally by xml_parse(). We create an object and call its start_element method. + function start_element($p, $element, &$attrs) { + $el = $element;// = strtolower($element); + // $attrs = array_change_key_case($attrs, CASE_LOWER); + + // If there is an extended class for this element, use it. + $class = 'element'; + + $maybe_class = $test_class = strtolower(str_replace(':', '_', $el)); + if ( class_exists($maybe_class) ) { + for ($classes[] = $test_class; $test_class = get_parent_class ($test_class); $classes[] = $test_class); + if ( in_array($class, $classes) ) + $class = $maybe_class; } - $this->current_namespace = false; + // Instantiate an object for this element. + $object = new $class(); + + // Tell the element to start itself. + $object->start_element($p, $element, $attrs, $this); + } + + function cdata ($p, $data) { + $this->stack[0]->cdata($p, $data, $this); + } + + function end_element ($p, $el) { + $this->stack[0]->end_element($p, $el, $this); } - function concat (&$str1, $str2="") { - if (!isset($str1) ) { - $str1=""; + function create_parser($source, $out_enc, $in_enc, $detect) { + if ( substr(phpversion(),0,1) == 5) { + $parser = $this->php5_create_parser($in_enc, $detect); + } + else { + list($parser, $source) = $this->php4_create_parser($source, $in_enc, $detect); } - $str1 .= $str2; + if ($out_enc) { + $this->encoding = $out_enc; + xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $out_enc); + xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); + } + + return array($parser, $source); } - function append_content($text) { - if ( $this->initem ) { - $this->concat( $this->current_item[ $this->incontent ], $text ); + function php5_create_parser($in_enc, $detect) { + // by default php5 does a fine job of detecting input encodings + if(!$detect && $in_enc) { + return xml_parser_create($in_enc); } - elseif ( $this->inchannel ) { - $this->concat( $this->channel[ $this->incontent ], $text ); + else { + return xml_parser_create(''); } } - // smart append - field and namespace aware - function append($el, $text) { - if (!$el) { - return; + /** + * Instaniate an XML parser under PHP4 + * + * Unfortunately PHP4's support for character encodings + * and especially XML and character encodings sucks. As + * long as the documents you parse only contain characters + * from the ISO-8859-1 character set (a superset of ASCII, + * and a subset of UTF-8) you're fine. However once you + * step out of that comfy little world things get mad, bad, + * and dangerous to know. + * + * The following code is based on SJM's work with FoF + * @see http://minutillo.com/steve/weblog/2004/6/17/php-xml-and-character-encodings-a-tale-of-sadness-rage-and-data-loss + * + */ + function php4_create_parser($source, $in_enc, $detect) { + if ( !$detect ) { + return array(xml_parser_create($in_enc), $source); } - if ( $this->current_namespace ) - { - if ( $this->initem ) { - $this->concat( - $this->current_item[ $this->current_namespace ][ $el ], $text); - } - elseif ($this->inchannel) { - $this->concat( - $this->channel[ $this->current_namespace][ $el ], $text ); - } - elseif ($this->intextinput) { - $this->concat( - $this->textinput[ $this->current_namespace][ $el ], $text ); + + if (!$in_enc) { + if (preg_match('/<?xml.*encoding=[\'"](.*?)[\'"].*?>/m', $source, $m)) { + $in_enc = strtoupper($m[1]); + $this->source_encoding = $in_enc; } - elseif ($this->inimage) { - $this->concat( - $this->image[ $this->current_namespace ][ $el ], $text ); + else { + $in_enc = 'UTF-8'; } } - else { - if ( $this->initem ) { - $this->concat( - $this->current_item[ $el ], $text); - } - elseif ($this->intextinput) { - $this->concat( - $this->textinput[ $el ], $text ); - } - elseif ($this->inimage) { - $this->concat( - $this->image[ $el ], $text ); - } - elseif ($this->inchannel) { - $this->concat( - $this->channel[ $el ], $text ); - } + if ($this->known_encoding($in_enc)) { + return array(xml_parser_create($in_enc), $source); } - } - function normalize () { - // 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']; + // the dectected encoding is not one of the simple encodings PHP knows + + // attempt to use the iconv extension to + // cast the XML to a known encoding + // @see http://php.net/iconv - $this->items[$i] = $item; + if (function_exists('iconv')) { + $encoded_source = iconv($in_enc,'UTF-8', $source); + if ($encoded_source) { + return array(xml_parser_create('UTF-8'), $encoded_source); } } - 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; + // iconv didn't work, try mb_convert_encoding + // @see http://php.net/mbstring + if(function_exists('mb_convert_encoding')) { + $encoded_source = mb_convert_encoding($source, 'UTF-8', $in_enc ); + if ($encoded_source) { + return array(xml_parser_create('UTF-8'), $encoded_source); } } - } - function is_rss () { - if ( $this->feed_type == RSS ) { - return $this->feed_version; - } - else { - return false; - } + // else + $this->error("Feed is in an unsupported character encoding. ($in_enc) " . + "You may see strange artifacts, and mangled characters.", + E_USER_NOTICE); + + return array(xml_parser_create(), $source); } - function is_atom() { - if ( $this->feed_type == ATOM ) { - return $this->feed_version; + function known_encoding($enc) { + $enc = strtoupper($enc); + if ( in_array($enc, array('UTF-8', 'US-ASCII', 'ISO-8859-1')) ) { + return $enc; } else { return false; } } - function map_attrs($k, $v) { - return "$k=\"$v\""; - } - - function error( $errormsg, $lvl = E_USER_WARNING ) { + function error ($errormsg, $lvl=E_USER_WARNING) { // append PHP's error message if track_errors enabled if ( isset($php_errormsg) ) { $errormsg .= " ($php_errormsg)"; } if ( MAGPIE_DEBUG ) { - trigger_error( $errormsg, $lvl); - } else { - error_log( $errormsg, 0); - } - } - -} - -function map_attrs($k, $v) { - return "$k=\"$v\""; -} - -require_once( dirname(__FILE__) . '/class-snoopy.php'); - -function fetch_rss ($url) { - // initialize constants - init(); - - if ( !isset($url) ) { - // error("fetch_rss called without a url"); - return false; - } - - // if cache is disabled - if ( !MAGPIE_CACHE_ON ) { - // fetch file, and parse it - $resp = _fetch_remote_file( $url ); - if ( is_success( $resp->status ) ) { - return _response_to_rss( $resp ); + // trigger_error( $errormsg, $lvl); } else { - // error("Failed to fetch $url and cache is off"); - return false; + error_log( $errormsg, 0); } - } - // else cache is ON - else { - // Flow - // 1. check cache - // 2. if there is a hit, make sure its 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); + $notices = E_USER_NOTICE|E_NOTICE; + if ( $lvl&$notices ) { + $this->WARNING = $errormsg; + } else { + $this->ERROR = $errormsg; } + } + // Remove empty and |^_.*| object vars + function bathe() { + foreach ( get_object_vars($this) as $key => $data ) + if ( empty($this->$key) || substr($key, 0, 1) == '_' ) + unset($this->$key); + } - $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 - - if (!$cache->ERROR) { - // 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 ) { - $rss->from_cache = 1; - if ( MAGPIE_DEBUG > 1) { - debug("MagpieRSS: Cache HIT", E_USER_NOTICE); - } - return $rss; + // Remove ALL object vars + function scour() { + foreach ( get_object_vars($this) as $key => $data ) + unset($this->$key); + } + + function to_magpie() { + $magpie = new stdClass(); + + foreach ( $this as $var => $value ) { + if ( $var == 'feed' ) { + continue; + } else { + $magpie->$var = $this->$var; } } - // else attempt a conditional get - - // setup headers - if ( $cache_status == 'STALE' ) { - $rss = $cache->get( $url ); - if ( $rss->etag and $rss->last_modified ) { - $request_headers['If-None-Match'] = $rss->etag; - $request_headers['If-Last-Modified'] = $rss->last_modified; - } - } + $magpie->items = array(); - $resp = _fetch_remote_file( $url, $request_headers ); + if ( is_object($this->feed) && method_exists($this->feed, 'to_magpie') ) { + $feed = $this->feed->to_magpie(); - if (isset($resp) and $resp) { - if ($resp->status == '304' ) { - // we have the most current copy - if ( MAGPIE_DEBUG > 1) { - debug("Got 304 for $url"); - } - // reset cache on 304 (at minutillo insistent prodding) - $cache->set($url, $rss); - return $rss; - } - elseif ( is_success( $resp->status ) ) { - $rss = _response_to_rss( $resp ); - if ( $rss ) { - if (MAGPIE_DEBUG > 1) { - debug("Fetch successful"); - } - // add object to cache - $cache->set( $url, $rss ); - return $rss; - } - } - else { - $errormsg = "Failed to fetch $url. "; - if ( $resp->error ) { - # compensate for Snoopy's annoying habbit to tacking - # on '\n' - $http_error = substr($resp->error, 0, -2); - $errormsg .= "(HTTP Error: $http_error)"; - } - else { - $errormsg .= "(HTTP Response: " . $resp->response_code .')'; + if ( is_array($feed) ) { + foreach ( $this->feed->to_magpie() as $var => $val ) { + if ( $var == 'items' ) + $magpie->items = $val; + else + $magpie->channel["$var"] = $val; } } } - else { - $errormsg = "Unable to retrieve RSS file for unknown reasons."; - } - // else fetch failed - - // attempt to return cached object - if ($rss) { - if ( MAGPIE_DEBUG ) { - debug("Returning STALE object for $url"); - } - return $rss; - } + return $magpie; + } +} - // else we totally failed - // error( $errormsg ); - return false; +class element { + function element() { + } - } // end if ( !MAGPIE_CACHE_ON ) { -} // end fetch_rss() + function start_element($p, $el, $attr, &$mag) { + $this->name = $el; + $this->attributes = $attr; -function _fetch_remote_file ($url, $headers = "" ) { - // Snoopy is an HTTP client in PHP - $client = new Snoopy(); - $client->agent = MAGPIE_USER_AGENT; - $client->read_timeout = MAGPIE_FETCH_TIME_OUT; - $client->use_gzip = MAGPIE_USE_GZIP; - if (is_array($headers) ) { - $client->rawheaders = $headers; + array_unshift($mag->stack, $this); + } + function cdata($p, $data, &$mag) { + if ( empty($this->children) ) + $this->appendText($data); } - @$client->fetch($url); - return $client; + function end_element($p, $el, &$mag) { + array_shift($mag->stack); -} + $this->bathe(); -function _response_to_rss ($resp) { - $rss = new MagpieRSS( $resp->results ); - - // if RSS parsed successfully - if ( $rss and !$rss->ERROR) { + if ( is_object($mag->stack[0]) ) + $mag->stack[0]->appendChild($this); + } - // find Etag, and Last-Modified - foreach($resp->headers as $h) { - // 2003-03-02 - Nicola Asuni (www.tecnick.com) - fixed bug "Undefined offset: 1" - if (strpos($h, ": ")) { - list($field, $val) = explode(": ", $h, 2); - } - else { - $field = $h; - $val = ""; - } + function wrap_cdata() { + if ( strpos($this->innerText, '<') ) { + $spacer = (substr($this->innerText, -1) == ']') ? ' ' : ''; + $this->innerText = str_replace(']]>', ']]>', $this->innerText); + $this->innerText = '<![CDATA[' . $this->innerText . $spacer . ']]>'; + } + } - if ( $field == 'ETag' ) { - $rss->etag = $val; - } + function appendText($text) { + $this->innerText .= $text; + } - if ( $field == 'Last-Modified' ) { - $rss->last_modified = $val; - } - } + function appendChild($object) { + $this->children[] = & $object; + } - return $rss; - } // else construct error message - else { - $errormsg = "Failed to parse RSS file."; + // Add self to array + function to_array(&$array) { + $self = get_object_vars($this); - if ($rss) { - $errormsg .= " (" . $rss->ERROR . ")"; - } - // error($errormsg); + unset($self['children']); - return false; - } // end if ($rss and !$rss->error) -} + foreach ( $this->children as $child ) + $child->to_array($self); -/*=======================================================================*\ - Function: init - Purpose: setup constants with default values - check for user overrides -\*=======================================================================*/ -function init () { - if ( defined('MAGPIE_INITALIZED') ) { - return; - } - else { - define('MAGPIE_INITALIZED', 1); + $array[$this->name] = $self; } - if ( !defined('MAGPIE_CACHE_ON') ) { - define('MAGPIE_CACHE_ON', 1); + // Return self as XML + function to_xml($indent = "\t", $generation = 0) { + $self = "<$this->name"; + $self .= $this->attributes ? ' '.string_attributes($this->attributes) : ''; + if ( empty($this->innerText) && empty($this->children) ) + $self .= " />\n"; + else { + $self .= ">"; + if ( $this->children ) { + $self .= "\n"; + foreach ( $this->children as $child ) + $self .= $child->to_xml($indent, $generation + 1); + $self .= $indent ? $this->indent('', $indent, $generation) : ''; + } else { + $self .= $this->innerText; + } + $self .= "</$this->name>\n"; + } + return $indent ? $this->indent($self, $indent, $generation) : $self; } - if ( !defined('MAGPIE_CACHE_DIR') ) { - define('MAGPIE_CACHE_DIR', './cache'); + function indent($string, $indent, $generation) { + for ( $i = ''; strlen($i) < $generation; $i .= $indent ) ; + return $i . $string; } - if ( !defined('MAGPIE_CACHE_AGE') ) { - define('MAGPIE_CACHE_AGE', 60*60); // one hour + /** + * Gets the innerText of the first matching element in the vars + * @param string Provide any number of strings to try + * @return mixed + */ + function getChildText() { + foreach ( func_get_args() as $name ) + foreach ( $this->children as $element ) + if ( $element->name == $name ) + return $element->innerText; + return false; } - if ( !defined('MAGPIE_CACHE_FRESH_ONLY') ) { - define('MAGPIE_CACHE_FRESH_ONLY', 0); + /** + * Gets a ref to the first matching element + * @param string Provide any number of strings to try + * @return object + */ + function &getChildElement() { + foreach ( func_get_args() as $name ) + foreach ( $this->children as $key => $element ) + if ( $element->name == $name ) + return $this->children[$key]; + return false; } - if ( !defined('MAGPIE_DEBUG') ) { - define('MAGPIE_DEBUG', 0); + function getAttribute($name) { + if ( is_array($this->attributes) ) + foreach ( $this->attributes as $attr => $value ) + if ( $attr == $name ) + return $value; + return null; } - if ( !defined('MAGPIE_USER_AGENT') ) { - $ua = 'WordPress/' . $wp_version; + function bathe() { + foreach ( get_object_vars($this) as $var => $value ) + if ( empty($this->$var) ) + unset($this->$var); + } - if ( MAGPIE_CACHE_ON ) { - $ua = $ua . ')'; - } - else { - $ua = $ua . '; No cache)'; - } + function to_magpie() { + if ( strlen(trim($this->innerText)) > 0 ) + return $this->innerText; - define('MAGPIE_USER_AGENT', $ua); - } + if ( is_array($this->attributes) ) + $e = $this->attributes; - if ( !defined('MAGPIE_FETCH_TIME_OUT') ) { - define('MAGPIE_FETCH_TIME_OUT', 2); // 2 second timeout - } + if ( is_array($this->children) && count($this->children) > 0 ) + foreach ( $this->children as $k => $c ) + $e[$k] = $this->children[$k]->to_magpie(); - // use gzip encoding to fetch rss files if supported? - if ( !defined('MAGPIE_USE_GZIP') ) { - define('MAGPIE_USE_GZIP', true); + return $e; } } -function is_info ($sc) { - return $sc >= 100 && $sc < 200; -} - -function is_success ($sc) { - return $sc >= 200 && $sc < 300; -} +// Base Atom class +class feed extends element { + function end_element($p, $el, &$mag) { + $mag->feed = $this; + $mag->is_feed = true; + $mag->last_modified = $this->last_modified(); + $mag->stack = array(); + } + function last_modified() { + $time = parse_w3cdtf($this->getChildText('modified')); + return gmdate('D, d M Y H:i:s T', $time); + } + function to_magpie() { + foreach ( $this->children as $k => $c ) { + if ( $this->children[$k]->name == 'entry' ) + $magpie['items'][] = $this->children[$k]->to_magpie(); + else + $magpie[$this->children[$k]->name] = $this->children[$k]->innerText; + } -function is_redirect ($sc) { - return $sc >= 300 && $sc < 400; + return $magpie; + } } -function is_error ($sc) { - return $sc >= 400 && $sc < 600; +// RSS base class +class rss extends feed { + function last_modified() { + $channel =& $this->getChildElement('channel'); + $date = $channel->getChildText('pubDate', 'lastBuildDate'); + return gmdate('D, d M Y H:i:s T', strtotime($date)); + } + function to_magpie() { + return $this->children[0]->to_magpie(); + } } +class rdf_rdf extends feed { + function to_magpie() { + $magpie = array(); + foreach ( $this->children as $k => $child ) { + if ( $this->children[$k]->name == 'item' ) + $magpie['items'][] = $this->children[$k]->to_magpie(); + elseif ( method_exists($this->children[$k], 'to_magpie') ) + $magpie[$this->children[$k]->name] = $this->children[$k]->to_magpie(); + else + $magpie[$this->children[$k]->name] = $this->children[$k]->innerText; + } + if ( is_array($magpie['channel']) ) + $magpie = array_merge($magpie['channel'], $magpie); -function is_client_error ($sc) { - return $sc >= 400 && $sc < 500; + return $magpie; + } } +class channel extends element { + function to_magpie() { + foreach ( $this->children as $k => $c ) { + if ( $this->children[$k]->name == 'item' ) + $magpie['items'][] = $this->children[$k]->to_magpie(); + else + $magpie[$this->children[$k]->name] = $this->children[$k]->innerText; + } -function is_server_error ($sc) { - return $sc >= 500 && $sc < 600; + return $magpie; + } } -class RSSCache { - var $BASE_CACHE = 'wp-content/cache'; // where the cache files are stored - var $MAX_AGE = 43200; // when are files stale, default twelve hours - var $ERROR = ''; // accumulate error messages - - function RSSCache ($base='', $age='') { - if ( $base ) { - $this->BASE_CACHE = $base; - } - if ( $age ) { - $this->MAX_AGE = $age; +// Atom article class +class entry extends element { + function to_magpie() { + foreach ( $this->children as $k => $v ) { + if ( is_object($this->children[$k]) ) + $value = $this->children[$k]->to_magpie(); + else + $value = $this->children[$k]->innerText; + + $name = $this->children[$k]->name; + + $norms = array( + 'dc:subject' => 'categories', + 'summary' => 'description', + ); + $name = str_replace(array_keys($norms), array_values($norms), $name); + + switch ( $name ) { + // The ones that needs to be dereferenced + case 'author': + $magpie[$name] = $value[0]; + break; + + // The ones that can be multiple + case 'categories': + $magpie[$name][] = $value; + break; + + default: + $magpie[$name] = $value; + } } - + return $magpie; } -/*=======================================================================*\ - Function: set - Purpose: add an item to the cache, keyed on url - Input: url from wich the rss file was fetched - Output: true on sucess -\*=======================================================================*/ - function set ($url, $rss) { - global $wpdb; - $cache_option = 'rss_' . $this->file_name( $url ); - $cache_timestamp = 'rss_' . $this->file_name( $url ) . '_ts'; + function to_array(&$array) { + $self = get_object_vars($this); - if ( !$wpdb->get_var("SELECT option_name FROM $wpdb->options WHERE option_name = '$cache_option'") ) - add_option($cache_option, '', '', 'no'); - if ( !$wpdb->get_var("SELECT option_name FROM $wpdb->options WHERE option_name = '$cache_timestamp'") ) - add_option($cache_timestamp, '', '', 'no'); + unset($self['children']); - update_option($cache_option, $rss); - update_option($cache_timestamp, time() ); + foreach ( $this->children as $child ) + $child->to_array($self); - return $cache_option; + $array['items'][] = $self; } +} -/*=======================================================================*\ - Function: get - Purpose: fetch an item from the cache - Input: url from wich the rss file was fetched - Output: cached object on HIT, false on MISS -\*=======================================================================*/ - function get ($url) { - $this->ERROR = ""; - $cache_option = 'rss_' . $this->file_name( $url ); - - if ( ! get_option( $cache_option ) ) { - $this->debug( - "Cache doesn't contain: $url (cache option: $cache_option)" - ); - return 0; +// RSS article class +class item extends entry { + function to_magpie() { + foreach ( $this->children as $k => $v ) { + if ( is_object($this->children[$k]) ) + $value = $this->children[$k]->to_magpie(); + else + $value = $this->children[$k]->innerText; + + $name = $this->children[$k]->name; + + $norms = array( + 'category' => 'categories', + 'content:encoded' => 'content', + 'dc:creator' => 'author', + 'wfw:commentRss' => 'comments', + 'wfw:commentRSS' => 'comments', + 'pubDate' => 'pubdate', + ); + $name = str_replace(array_keys($norms), array_values($norms), $name); + + switch ( $name ) { + // The ones that needs to be dereferenced + case 'taxo:topics': + $magpie[$name] = $value[0]; + break; + + // The ones that can be multiple + case 'categories': + $magpie[$name][] = $value; + break; + + default: + $magpie[$name] = $value; + } } + return $magpie; + } +} - $rss = get_option( $cache_option ); +class category extends element { + function to_array(&$array) { + $self = get_object_vars($this); - return $rss; - } + unset($self['children']); -/*=======================================================================*\ - Function: check_cache - Purpose: check a url for membership in the cache - and whether the object is older then MAX_AGE (ie. STALE) - Input: url from wich the rss file was fetched - Output: cached object on HIT, false on MISS -\*=======================================================================*/ - function check_cache ( $url ) { - $this->ERROR = ""; - $cache_option = $this->file_name( $url ); - $cache_timestamp = 'rss_' . $this->file_name( $url ) . '_ts'; + foreach ( $this->children as $child ) + $child->to_array($self); - if ( $mtime = get_option($cache_timestamp) ) { - // find how long ago the file was added to the cache - // and whether that is longer then MAX_AGE - $age = time() - $mtime; - if ( $this->MAX_AGE > $age ) { - // object exists and is current - return 'HIT'; - } - else { - // object exists but is old - return 'STALE'; - } - } - else { - // object does not exist - return 'MISS'; - } + $array['categories'][] = $self; } +} -/*=======================================================================*\ - Function: serialize -\*=======================================================================*/ - function serialize ( $rss ) { - return serialize( $rss ); - } +class link extends element { + function end_element($p, $el, &$mag) { + if ( $href = $this->getAttribute('href') ) + $this->innerText = $href; -/*=======================================================================*\ - Function: unserialize -\*=======================================================================*/ - function unserialize ( $data ) { - return unserialize( $data ); + parent::end_element($p, $el, $mag); } +} -/*=======================================================================*\ - Function: file_name - Purpose: map url to location in cache - Input: url from wich the rss file was fetched - Output: a file name -\*=======================================================================*/ - function file_name ($url) { - return md5( $url ); - } -/*=======================================================================*\ - Function: error - Purpose: register error -\*=======================================================================*/ - function error ($errormsg, $lvl=E_USER_WARNING) { - // append PHP's error message if track_errors enabled - if ( isset($php_errormsg) ) { - $errormsg .= " ($php_errormsg)"; - } - $this->ERROR = $errormsg; - if ( MAGPIE_DEBUG ) { - trigger_error( $errormsg, $lvl); - } - else { - error_log( $errormsg, 0); - } - } - function debug ($debugmsg, $lvl=E_USER_NOTICE) { - if ( MAGPIE_DEBUG ) { - $this->error("MagpieRSS [debug] $debugmsg", $lvl); - } - } +/* Version 0.9, 6th April 2003 - Simon Willison ( http://simon.incutio.com/ ) + Manual: http://scripts.incutio.com/httpclient/ +*/ + +if ( !class_exists('HttpClient') ) : +class HttpClient { + // Request vars + var $host; + var $port; + var $path; + var $method; + var $postdata = ''; + var $cookies = array(); + var $referer; + var $accept = 'text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,image/jpeg,image/gif,*/*'; + var $accept_encoding = 'gzip'; + var $accept_language = 'en-us'; + var $user_agent = 'Incutio HttpClient v0.9'; + // Options + var $timeout = 20; + var $use_gzip = true; + var $no_cache = false; + var $persist_cookies = true; // If true, received cookies are placed in the $this->cookies array ready for the next request + // Note: This currently ignores the cookie path (and time) completely. Time is not important, + // but path could possibly lead to security problems. + var $persist_referers = true; // For each request, sends path of last request as referer + var $debug = false; + var $handle_redirects = true; // Auaomtically redirect if Location or URI header is found + var $max_redirects = 5; + var $headers_only = false; // If true, stops receiving once headers have been read. + // Basic authorization variables + var $username; + var $password; + // Response vars + var $status; + var $headers = array(); + var $content = ''; + var $errormsg; + // Tracker variables + var $redirect_count = 0; + var $cookie_host = ''; + function HttpClient($host, $port=80) { + $this->host = $host; + $this->port = $port; + } + function get($path, $data = false) { + $this->path = $path; + $this->method = 'GET'; + if ($data) { + $this->path .= '?'.$this->buildQueryString($data); + } + return $this->doRequest(); + } + function post($path, $data) { + $this->path = $path; + $this->method = 'POST'; + $this->postdata = $this->buildQueryString($data); + return $this->doRequest(); + } + function buildQueryString($data) { + $querystring = ''; + if (is_array($data)) { + // Change data in to postable data + foreach ($data as $key => $val) { + if (is_array($val)) { + foreach ($val as $val2) { + $querystring .= urlencode($key).'='.urlencode($val2).'&'; + } + } else { + $querystring .= urlencode($key).'='.urlencode($val).'&'; + } + } + $querystring = substr($querystring, 0, -1); // Eliminate unnecessary & + } else { + $querystring = $data; + } + return $querystring; + } + function doRequest() { + // Performs the actual HTTP request, returning true or false depending on outcome + if (!$fp = @fsockopen($this->host, $this->port, $errno, $errstr, $this->timeout)) { + // Set error message + switch($errno) { + case -3: + $this->errormsg = 'Socket creation failed (-3)'; + case -4: + $this->errormsg = 'DNS lookup failure (-4)'; + case -5: + $this->errormsg = 'Connection refused or timed out (-5)'; + default: + $this->errormsg = 'Connection failed ('.$errno.')'; + $this->errormsg .= ' '.$errstr; + $this->debug($this->errormsg); + } + return false; + } + socket_set_timeout($fp, $this->timeout); + $request = $this->buildRequest(); + $this->debug('Request', $request); + fwrite($fp, $request); + // Reset all the variables that should not persist between requests + $this->headers = array(); + $this->content = ''; + $this->errormsg = ''; + // Set a couple of flags + $inHeaders = true; + // Now start reading back the response + $line = fgets($fp, 4096); + // Deal with first line of returned data + if (!preg_match('/HTTP\/(\\d\\.\\d)\\s*(\\d+)\\s*(.*)/', $line, $m)) { + $this->errormsg = "Status code line invalid: ".htmlentities($line); + $this->debug($this->errormsg); + return false; + } + $http_version = $m[1]; // not used + $this->status = $m[2]; + $status_string = $m[3]; // not used + $this->debug(trim($line)); + while (!feof($fp)) { + $line = fgets($fp, 4096); + if ($inHeaders) { + if (trim($line) == '') { + $inHeaders = false; + $this->debug('Received Headers', $this->headers); + if ($this->headers_only) { + break; // Skip the rest of the input + } + continue; + } + if (!preg_match('/([^:]+):\\s*(.*)/', $line, $m)) { + // Skip to the next header + continue; + } + $key = strtolower(trim($m[1])); + $val = trim($m[2]); + // Deal with the possibility of multiple headers of same name + if (isset($this->headers[$key])) { + if (is_array($this->headers[$key])) { + $this->headers[$key][] = $val; + } else { + $this->headers[$key] = array($this->headers[$key], $val); + } + } else { + $this->headers[$key] = $val; + } + continue; + } + // We're not in the headers, so append the line to the contents + $this->content .= $line; + } + fclose($fp); + // If data is compressed, uncompress it + if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] == 'gzip') { + $this->debug('Content is gzip encoded, unzipping it'); + $this->content = substr($this->content, 10); // See http://www.php.net/manual/en/function.gzencode.php + $this->content = gzinflate($this->content); + } + // If $persist_cookies, deal with any cookies + if ($this->persist_cookies && isset($this->headers['set-cookie']) && $this->host == $this->cookie_host) { + $cookies = $this->headers['set-cookie']; + if (!is_array($cookies)) { + $cookies = array($cookies); + } + foreach ($cookies as $cookie) { + if (preg_match('/([^=]+)=([^;]+);/', $cookie, $m)) { + $this->cookies[$m[1]] = $m[2]; + } + } + // Record domain of cookies for security reasons + $this->cookie_host = $this->host; + } + // If $persist_referers, set the referer ready for the next request + if ($this->persist_referers) { + $this->debug('Persisting referer: '.$this->getRequestURL()); + $this->referer = $this->getRequestURL(); + } + // Finally, if handle_redirects and a redirect is sent, do that + if ($this->handle_redirects) { + if (++$this->redirect_count >= $this->max_redirects) { + $this->errormsg = 'Number of redirects exceeded maximum ('.$this->max_redirects.')'; + $this->debug($this->errormsg); + $this->redirect_count = 0; + return false; + } + $location = isset($this->headers['location']) ? $this->headers['location'] : ''; + $uri = isset($this->headers['uri']) ? $this->headers['uri'] : ''; + if ($location || $uri) { + $url = parse_url($location.$uri); + // This will FAIL if redirect is to a different site + return $this->get($url['path']); + } + } + return true; + } + function buildRequest() { + $headers = array(); + $headers[] = "{$this->method} {$this->path} HTTP/1.0"; // Using 1.1 leads to all manner of problems, such as "chunked" encoding + $headers[] = "Host: {$this->host}"; + $headers[] = "User-Agent: {$this->user_agent}"; + $headers[] = "Accept: {$this->accept}"; + if ($this->use_gzip) { + $headers[] = "Accept-encoding: {$this->accept_encoding}"; + } + $headers[] = "Accept-language: {$this->accept_language}"; + if ($this->referer) { + $headers[] = "Referer: {$this->referer}"; + } + if ($this->no_cache) { + $headers[] = "Pragma: no-cache"; + $headers[] = "Cache-control: no-cache"; + } + // Cookies + if ($this->cookies) { + $cookie = 'Cookie: '; + foreach ($this->cookies as $key => $value) { + $cookie .= "$key=$value; "; + } + $headers[] = $cookie; + } + // Basic authentication + if ($this->username && $this->password) { + $headers[] = 'Authorization: BASIC '.base64_encode($this->username.':'.$this->password); + } + // If this is a POST, set the content type and length + if ($this->postdata) { + $headers[] = 'Content-Type: application/x-www-form-urlencoded'; + $headers[] = 'Content-Length: '.strlen($this->postdata); + } + $request = implode("\r\n", $headers)."\r\n\r\n".$this->postdata; + return $request; + } + function getStatus() { + return $this->status; + } + function getContent() { + return $this->content; + } + function getHeaders() { + return $this->headers; + } + function getHeader($header) { + $header = strtolower($header); + if (isset($this->headers[$header])) { + return $this->headers[$header]; + } else { + return false; + } + } + function getError() { + return $this->errormsg; + } + function getCookies() { + return $this->cookies; + } + function getRequestURL() { + $url = 'http://'.$this->host; + if ($this->port != 80) { + $url .= ':'.$this->port; + } + $url .= $this->path; + return $url; + } + // Setter methods + function setUserAgent($string) { + $this->user_agent = $string; + } + function setAuthorization($username, $password) { + $this->username = $username; + $this->password = $password; + } + function setCookies($array) { + $this->cookies = $array; + } + // Option setting methods + function useGzip($boolean) { + $this->use_gzip = $boolean; + } + function setPersistCookies($boolean) { + $this->persist_cookies = $boolean; + } + function setPersistReferers($boolean) { + $this->persist_referers = $boolean; + } + function setHandleRedirects($boolean) { + $this->handle_redirects = $boolean; + } + function setMaxRedirects($num) { + $this->max_redirects = $num; + } + function setHeadersOnly($boolean) { + $this->headers_only = $boolean; + } + function setDebug($boolean) { + $this->debug = $boolean; + } + // "Quick" static methods + function quickGet($url) { + $bits = parse_url($url); + $host = $bits['host']; + $port = isset($bits['port']) ? $bits['port'] : 80; + $path = isset($bits['path']) ? $bits['path'] : '/'; + if (isset($bits['query'])) { + $path .= '?'.$bits['query']; + } + $client = new HttpClient($host, $port); + if (!$client->get($path)) { + return false; + } else { + return $client->getContent(); + } + } + function quickPost($url, $data) { + $bits = parse_url($url); + $host = $bits['host']; + $port = isset($bits['port']) ? $bits['port'] : 80; + $path = isset($bits['path']) ? $bits['path'] : '/'; + $client = new HttpClient($host, $port); + if (!$client->post($path, $data)) { + return false; + } else { + return $client->getContent(); + } + } + function debug($msg, $object = false) { + if ($this->debug) { + print '<div style="border: 1px solid red; padding: 0.5em; margin: 0.5em;"><strong>HttpClient Debug:</strong> '.$msg; + if ($object) { + ob_start(); + print_r($object); + $content = htmlentities(ob_get_contents()); + ob_end_clean(); + print '<pre>'.$content.'</pre>'; + } + print '</div>'; + } + } } +endif; -function parse_w3cdtf ( $date_str ) { +function string_attributes($attrs) { + return join(' ', array_map(create_function('$k,$v', 'return "$k=\"".htmlspecialchars($v)."\"";'), array_keys($attrs), array_values($attrs) ) ); +} +if ( !function_exists('parse_w3cdtf') ) : +function parse_w3cdtf ( $date_str ) { # regex to match wc3dtf $pat = "/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(:(\d{2}))?(?:([-+])(\d{2}):?(\d{2})|(Z))?/"; if ( preg_match( $pat, $date_str, $match ) ) { - list( $year, $month, $day, $hours, $minutes, $seconds) = - array( $match[1], $match[2], $match[3], $match[4], $match[5], $match[6]); + list( $year, $month, $day, $hours, $minutes, $seconds) = + array( $match[1], $match[2], $match[3], $match[4], $match[5], $match[7]); # calc epoch for current date assuming GMT $epoch = gmmktime( $hours, $minutes, $seconds, $month, $day, $year); @@ -798,7 +1164,7 @@ function parse_w3cdtf ( $date_str ) { } else { list( $tz_mod, $tz_hour, $tz_min ) = - array( $match[8], $match[9], $match[10]); + array( $match[8], $match[9], $match[10]); # zero out the variables if ( ! $tz_hour ) { $tz_hour = 0; } @@ -820,42 +1186,7 @@ function parse_w3cdtf ( $date_str ) { else { return -1; } - } -function wp_rss ($url, $num) { - //ini_set("display_errors", false); uncomment to suppress php errors thrown if the feed is not returned. - $num_items = $num; - $rss = fetch_rss($url); - if ( $rss ) { - echo "<ul>"; - $rss->items = array_slice($rss->items, 0, $num_items); - foreach ($rss->items as $item ) { - echo "<li>\n"; - echo "<a href='$item[link]' title='$item[description]'>"; - echo htmlentities($item['title']); - echo "</a><br />\n"; - echo "</li>\n"; - } - echo "</ul>"; - } - else { - echo "an error has occured the feed is probably down, try again later."; - } } +endif; -function get_rss ($uri, $num = 5) { // Like get posts, but for RSS - $rss = fetch_rss($url); - if ( $rss ) { - $rss->items = array_slice($rss->items, 0, $num_items); - foreach ($rss->items as $item ) { - echo "<li>\n"; - echo "<a href='$item[link]' title='$item[description]'>"; - echo htmlentities($item['title']); - echo "</a><br />\n"; - echo "</li>\n"; - } - return $posts; - } else { - return false; - } -} ?> |
