MDL-56001 lib: Upgrade simplepie to 1.4.2
[moodle.git] / lib / simplepie / library / SimplePie.php
1 <?php
2 /**
3  * SimplePie
4  *
5  * A PHP-Based RSS and Atom Feed Framework.
6  * Takes the hard work out of managing a complete RSS/Atom solution.
7  *
8  * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without modification, are
12  * permitted provided that the following conditions are met:
13  *
14  *      * Redistributions of source code must retain the above copyright notice, this list of
15  *        conditions and the following disclaimer.
16  *
17  *      * Redistributions in binary form must reproduce the above copyright notice, this list
18  *        of conditions and the following disclaimer in the documentation and/or other materials
19  *        provided with the distribution.
20  *
21  *      * Neither the name of the SimplePie Team nor the names of its contributors may be used
22  *        to endorse or promote products derived from this software without specific prior
23  *        written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
26  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
28  * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
32  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  *
35  * @package SimplePie
36  * @version 1.4.1
37  * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38  * @author Ryan Parman
39  * @author Geoffrey Sneddon
40  * @author Ryan McCue
41  * @link http://simplepie.org/ SimplePie
42  * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43  */
45 /**
46  * SimplePie Name
47  */
48 define('SIMPLEPIE_NAME', 'SimplePie');
50 /**
51  * SimplePie Version
52  */
53 define('SIMPLEPIE_VERSION', '1.4.1');
55 /**
56  * SimplePie Build
57  * @todo Hardcode for release (there's no need to have to call SimplePie_Misc::get_build() only every load of simplepie.inc)
58  */
59 define('SIMPLEPIE_BUILD', gmdate('YmdHis', SimplePie_Misc::get_build()));
61 /**
62  * SimplePie Website URL
63  */
64 define('SIMPLEPIE_URL', 'http://simplepie.org');
66 /**
67  * SimplePie Useragent
68  * @see SimplePie::set_useragent()
69  */
70 define('SIMPLEPIE_USERAGENT', SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION . ' (Feed Parser; ' . SIMPLEPIE_URL . '; Allow like Gecko) Build/' . SIMPLEPIE_BUILD);
72 /**
73  * SimplePie Linkback
74  */
75 define('SIMPLEPIE_LINKBACK', '<a href="' . SIMPLEPIE_URL . '" title="' . SIMPLEPIE_NAME . ' ' . SIMPLEPIE_VERSION . '">' . SIMPLEPIE_NAME . '</a>');
77 /**
78  * No Autodiscovery
79  * @see SimplePie::set_autodiscovery_level()
80  */
81 define('SIMPLEPIE_LOCATOR_NONE', 0);
83 /**
84  * Feed Link Element Autodiscovery
85  * @see SimplePie::set_autodiscovery_level()
86  */
87 define('SIMPLEPIE_LOCATOR_AUTODISCOVERY', 1);
89 /**
90  * Local Feed Extension Autodiscovery
91  * @see SimplePie::set_autodiscovery_level()
92  */
93 define('SIMPLEPIE_LOCATOR_LOCAL_EXTENSION', 2);
95 /**
96  * Local Feed Body Autodiscovery
97  * @see SimplePie::set_autodiscovery_level()
98  */
99 define('SIMPLEPIE_LOCATOR_LOCAL_BODY', 4);
101 /**
102  * Remote Feed Extension Autodiscovery
103  * @see SimplePie::set_autodiscovery_level()
104  */
105 define('SIMPLEPIE_LOCATOR_REMOTE_EXTENSION', 8);
107 /**
108  * Remote Feed Body Autodiscovery
109  * @see SimplePie::set_autodiscovery_level()
110  */
111 define('SIMPLEPIE_LOCATOR_REMOTE_BODY', 16);
113 /**
114  * All Feed Autodiscovery
115  * @see SimplePie::set_autodiscovery_level()
116  */
117 define('SIMPLEPIE_LOCATOR_ALL', 31);
119 /**
120  * No known feed type
121  */
122 define('SIMPLEPIE_TYPE_NONE', 0);
124 /**
125  * RSS 0.90
126  */
127 define('SIMPLEPIE_TYPE_RSS_090', 1);
129 /**
130  * RSS 0.91 (Netscape)
131  */
132 define('SIMPLEPIE_TYPE_RSS_091_NETSCAPE', 2);
134 /**
135  * RSS 0.91 (Userland)
136  */
137 define('SIMPLEPIE_TYPE_RSS_091_USERLAND', 4);
139 /**
140  * RSS 0.91 (both Netscape and Userland)
141  */
142 define('SIMPLEPIE_TYPE_RSS_091', 6);
144 /**
145  * RSS 0.92
146  */
147 define('SIMPLEPIE_TYPE_RSS_092', 8);
149 /**
150  * RSS 0.93
151  */
152 define('SIMPLEPIE_TYPE_RSS_093', 16);
154 /**
155  * RSS 0.94
156  */
157 define('SIMPLEPIE_TYPE_RSS_094', 32);
159 /**
160  * RSS 1.0
161  */
162 define('SIMPLEPIE_TYPE_RSS_10', 64);
164 /**
165  * RSS 2.0
166  */
167 define('SIMPLEPIE_TYPE_RSS_20', 128);
169 /**
170  * RDF-based RSS
171  */
172 define('SIMPLEPIE_TYPE_RSS_RDF', 65);
174 /**
175  * Non-RDF-based RSS (truly intended as syndication format)
176  */
177 define('SIMPLEPIE_TYPE_RSS_SYNDICATION', 190);
179 /**
180  * All RSS
181  */
182 define('SIMPLEPIE_TYPE_RSS_ALL', 255);
184 /**
185  * Atom 0.3
186  */
187 define('SIMPLEPIE_TYPE_ATOM_03', 256);
189 /**
190  * Atom 1.0
191  */
192 define('SIMPLEPIE_TYPE_ATOM_10', 512);
194 /**
195  * All Atom
196  */
197 define('SIMPLEPIE_TYPE_ATOM_ALL', 768);
199 /**
200  * All feed types
201  */
202 define('SIMPLEPIE_TYPE_ALL', 1023);
204 /**
205  * No construct
206  */
207 define('SIMPLEPIE_CONSTRUCT_NONE', 0);
209 /**
210  * Text construct
211  */
212 define('SIMPLEPIE_CONSTRUCT_TEXT', 1);
214 /**
215  * HTML construct
216  */
217 define('SIMPLEPIE_CONSTRUCT_HTML', 2);
219 /**
220  * XHTML construct
221  */
222 define('SIMPLEPIE_CONSTRUCT_XHTML', 4);
224 /**
225  * base64-encoded construct
226  */
227 define('SIMPLEPIE_CONSTRUCT_BASE64', 8);
229 /**
230  * IRI construct
231  */
232 define('SIMPLEPIE_CONSTRUCT_IRI', 16);
234 /**
235  * A construct that might be HTML
236  */
237 define('SIMPLEPIE_CONSTRUCT_MAYBE_HTML', 32);
239 /**
240  * All constructs
241  */
242 define('SIMPLEPIE_CONSTRUCT_ALL', 63);
244 /**
245  * Don't change case
246  */
247 define('SIMPLEPIE_SAME_CASE', 1);
249 /**
250  * Change to lowercase
251  */
252 define('SIMPLEPIE_LOWERCASE', 2);
254 /**
255  * Change to uppercase
256  */
257 define('SIMPLEPIE_UPPERCASE', 4);
259 /**
260  * PCRE for HTML attributes
261  */
262 define('SIMPLEPIE_PCRE_HTML_ATTRIBUTE', '((?:[\x09\x0A\x0B\x0C\x0D\x20]+[^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"(?:[^"]*)"|\'(?:[^\']*)\'|(?:[^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?)*)[\x09\x0A\x0B\x0C\x0D\x20]*');
264 /**
265  * PCRE for XML attributes
266  */
267 define('SIMPLEPIE_PCRE_XML_ATTRIBUTE', '((?:\s+(?:(?:[^\s:]+:)?[^\s:]+)\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'))*)\s*');
269 /**
270  * XML Namespace
271  */
272 define('SIMPLEPIE_NAMESPACE_XML', 'http://www.w3.org/XML/1998/namespace');
274 /**
275  * Atom 1.0 Namespace
276  */
277 define('SIMPLEPIE_NAMESPACE_ATOM_10', 'http://www.w3.org/2005/Atom');
279 /**
280  * Atom 0.3 Namespace
281  */
282 define('SIMPLEPIE_NAMESPACE_ATOM_03', 'http://purl.org/atom/ns#');
284 /**
285  * RDF Namespace
286  */
287 define('SIMPLEPIE_NAMESPACE_RDF', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
289 /**
290  * RSS 0.90 Namespace
291  */
292 define('SIMPLEPIE_NAMESPACE_RSS_090', 'http://my.netscape.com/rdf/simple/0.9/');
294 /**
295  * RSS 1.0 Namespace
296  */
297 define('SIMPLEPIE_NAMESPACE_RSS_10', 'http://purl.org/rss/1.0/');
299 /**
300  * RSS 1.0 Content Module Namespace
301  */
302 define('SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT', 'http://purl.org/rss/1.0/modules/content/');
304 /**
305  * RSS 2.0 Namespace
306  * (Stupid, I know, but I'm certain it will confuse people less with support.)
307  */
308 define('SIMPLEPIE_NAMESPACE_RSS_20', '');
310 /**
311  * DC 1.0 Namespace
312  */
313 define('SIMPLEPIE_NAMESPACE_DC_10', 'http://purl.org/dc/elements/1.0/');
315 /**
316  * DC 1.1 Namespace
317  */
318 define('SIMPLEPIE_NAMESPACE_DC_11', 'http://purl.org/dc/elements/1.1/');
320 /**
321  * W3C Basic Geo (WGS84 lat/long) Vocabulary Namespace
322  */
323 define('SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO', 'http://www.w3.org/2003/01/geo/wgs84_pos#');
325 /**
326  * GeoRSS Namespace
327  */
328 define('SIMPLEPIE_NAMESPACE_GEORSS', 'http://www.georss.org/georss');
330 /**
331  * Media RSS Namespace
332  */
333 define('SIMPLEPIE_NAMESPACE_MEDIARSS', 'http://search.yahoo.com/mrss/');
335 /**
336  * Wrong Media RSS Namespace. Caused by a long-standing typo in the spec.
337  */
338 define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG', 'http://search.yahoo.com/mrss');
340 /**
341  * Wrong Media RSS Namespace #2. New namespace introduced in Media RSS 1.5.
342  */
343 define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG2', 'http://video.search.yahoo.com/mrss');
345 /**
346  * Wrong Media RSS Namespace #3. A possible typo of the Media RSS 1.5 namespace.
347  */
348 define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG3', 'http://video.search.yahoo.com/mrss/');
350 /**
351  * Wrong Media RSS Namespace #4. New spec location after the RSS Advisory Board takes it over, but not a valid namespace.
352  */
353 define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG4', 'http://www.rssboard.org/media-rss');
355 /**
356  * Wrong Media RSS Namespace #5. A possible typo of the RSS Advisory Board URL.
357  */
358 define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG5', 'http://www.rssboard.org/media-rss/');
360 /**
361  * iTunes RSS Namespace
362  */
363 define('SIMPLEPIE_NAMESPACE_ITUNES', 'http://www.itunes.com/dtds/podcast-1.0.dtd');
365 /**
366  * XHTML Namespace
367  */
368 define('SIMPLEPIE_NAMESPACE_XHTML', 'http://www.w3.org/1999/xhtml');
370 /**
371  * IANA Link Relations Registry
372  */
373 define('SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY', 'http://www.iana.org/assignments/relation/');
375 /**
376  * No file source
377  */
378 define('SIMPLEPIE_FILE_SOURCE_NONE', 0);
380 /**
381  * Remote file source
382  */
383 define('SIMPLEPIE_FILE_SOURCE_REMOTE', 1);
385 /**
386  * Local file source
387  */
388 define('SIMPLEPIE_FILE_SOURCE_LOCAL', 2);
390 /**
391  * fsockopen() file source
392  */
393 define('SIMPLEPIE_FILE_SOURCE_FSOCKOPEN', 4);
395 /**
396  * cURL file source
397  */
398 define('SIMPLEPIE_FILE_SOURCE_CURL', 8);
400 /**
401  * file_get_contents() file source
402  */
403 define('SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS', 16);
407 /**
408  * SimplePie
409  *
410  * @package SimplePie
411  * @subpackage API
412  */
413 class SimplePie
415         /**
416          * @var array Raw data
417          * @access private
418          */
419         public $data = array();
421         /**
422          * @var mixed Error string
423          * @access private
424          */
425         public $error;
427         /**
428          * @var object Instance of SimplePie_Sanitize (or other class)
429          * @see SimplePie::set_sanitize_class()
430          * @access private
431          */
432         public $sanitize;
434         /**
435          * @var string SimplePie Useragent
436          * @see SimplePie::set_useragent()
437          * @access private
438          */
439         public $useragent = SIMPLEPIE_USERAGENT;
441         /**
442          * @var string Feed URL
443          * @see SimplePie::set_feed_url()
444          * @access private
445          */
446         public $feed_url;
448         /**
449          * @var string Original feed URL, or new feed URL iff HTTP 301 Moved Permanently
450          * @see SimplePie::subscribe_url()
451          * @access private
452          */
453         public $permanent_url = null;
455         /**
456          * @var object Instance of SimplePie_File to use as a feed
457          * @see SimplePie::set_file()
458          * @access private
459          */
460         public $file;
462         /**
463          * @var string Raw feed data
464          * @see SimplePie::set_raw_data()
465          * @access private
466          */
467         public $raw_data;
469         /**
470          * @var int Timeout for fetching remote files
471          * @see SimplePie::set_timeout()
472          * @access private
473          */
474         public $timeout = 10;
476         /**
477          * @var array Custom curl options
478          * @see SimplePie::set_curl_options()
479          * @access private
480          */
481         public $curl_options = array();
483         /**
484          * @var bool Forces fsockopen() to be used for remote files instead
485          * of cURL, even if a new enough version is installed
486          * @see SimplePie::force_fsockopen()
487          * @access private
488          */
489         public $force_fsockopen = false;
491         /**
492          * @var bool Force the given data/URL to be treated as a feed no matter what
493          * it appears like
494          * @see SimplePie::force_feed()
495          * @access private
496          */
497         public $force_feed = false;
499         /**
500          * @var bool Enable/Disable Caching
501          * @see SimplePie::enable_cache()
502          * @access private
503          */
504         public $cache = true;
506         /**
507          * @var bool Force SimplePie to fallback to expired cache, if enabled,
508          * when feed is unavailable.
509          * @see SimplePie::force_cache_fallback()
510          * @access private
511          */
512         public $force_cache_fallback = false;
514         /**
515          * @var int Cache duration (in seconds)
516          * @see SimplePie::set_cache_duration()
517          * @access private
518          */
519         public $cache_duration = 3600;
521         /**
522          * @var int Auto-discovery cache duration (in seconds)
523          * @see SimplePie::set_autodiscovery_cache_duration()
524          * @access private
525          */
526         public $autodiscovery_cache_duration = 604800; // 7 Days.
528         /**
529          * @var string Cache location (relative to executing script)
530          * @see SimplePie::set_cache_location()
531          * @access private
532          */
533         public $cache_location = './cache';
535         /**
536          * @var string Function that creates the cache filename
537          * @see SimplePie::set_cache_name_function()
538          * @access private
539          */
540         public $cache_name_function = 'md5';
542         /**
543          * @var bool Reorder feed by date descending
544          * @see SimplePie::enable_order_by_date()
545          * @access private
546          */
547         public $order_by_date = true;
549         /**
550          * @var mixed Force input encoding to be set to the follow value
551          * (false, or anything type-cast to false, disables this feature)
552          * @see SimplePie::set_input_encoding()
553          * @access private
554          */
555         public $input_encoding = false;
557         /**
558          * @var int Feed Autodiscovery Level
559          * @see SimplePie::set_autodiscovery_level()
560          * @access private
561          */
562         public $autodiscovery = SIMPLEPIE_LOCATOR_ALL;
564         /**
565          * Class registry object
566          *
567          * @var SimplePie_Registry
568          */
569         public $registry;
571         /**
572          * @var int Maximum number of feeds to check with autodiscovery
573          * @see SimplePie::set_max_checked_feeds()
574          * @access private
575          */
576         public $max_checked_feeds = 10;
578         /**
579          * @var array All the feeds found during the autodiscovery process
580          * @see SimplePie::get_all_discovered_feeds()
581          * @access private
582          */
583         public $all_discovered_feeds = array();
585         /**
586          * @var string Web-accessible path to the handler_image.php file.
587          * @see SimplePie::set_image_handler()
588          * @access private
589          */
590         public $image_handler = '';
592         /**
593          * @var array Stores the URLs when multiple feeds are being initialized.
594          * @see SimplePie::set_feed_url()
595          * @access private
596          */
597         public $multifeed_url = array();
599         /**
600          * @var array Stores SimplePie objects when multiple feeds initialized.
601          * @access private
602          */
603         public $multifeed_objects = array();
605         /**
606          * @var array Stores the get_object_vars() array for use with multifeeds.
607          * @see SimplePie::set_feed_url()
608          * @access private
609          */
610         public $config_settings = null;
612         /**
613          * @var integer Stores the number of items to return per-feed with multifeeds.
614          * @see SimplePie::set_item_limit()
615          * @access private
616          */
617         public $item_limit = 0;
619         /**
620          * @var bool Stores if last-modified and/or etag headers were sent with the
621          * request when checking a feed.
622          */
623         public $check_modified = false;
625         /**
626          * @var array Stores the default attributes to be stripped by strip_attributes().
627          * @see SimplePie::strip_attributes()
628          * @access private
629          */
630         public $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc');
632         /**
633          * @var array Stores the default attributes to add to different tags by add_attributes().
634          * @see SimplePie::add_attributes()
635          * @access private
636          */
637         public $add_attributes = array('audio' => array('preload' => 'none'), 'iframe' => array('sandbox' => 'allow-scripts allow-same-origin'), 'video' => array('preload' => 'none'));
639         /**
640          * @var array Stores the default tags to be stripped by strip_htmltags().
641          * @see SimplePie::strip_htmltags()
642          * @access private
643          */
644         public $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style');
646         /**
647          * The SimplePie class contains feed level data and options
648          *
649          * To use SimplePie, create the SimplePie object with no parameters. You can
650          * then set configuration options using the provided methods. After setting
651          * them, you must initialise the feed using $feed->init(). At that point the
652          * object's methods and properties will be available to you.
653          *
654          * Previously, it was possible to pass in the feed URL along with cache
655          * options directly into the constructor. This has been removed as of 1.3 as
656          * it caused a lot of confusion.
657          *
658          * @since 1.0 Preview Release
659          */
660         public function __construct()
661         {
662                 if (version_compare(PHP_VERSION, '5.2', '<'))
663                 {
664                         trigger_error('PHP 4.x, 5.0 and 5.1 are no longer supported. Please upgrade to PHP 5.2 or newer.');
665                         die();
666                 }
668                 // Other objects, instances created here so we can set options on them
669                 $this->sanitize = new SimplePie_Sanitize();
670                 $this->registry = new SimplePie_Registry();
672                 if (func_num_args() > 0)
673                 {
674                         $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING;
675                         trigger_error('Passing parameters to the constructor is no longer supported. Please use set_feed_url(), set_cache_location(), and set_cache_duration() directly.', $level);
677                         $args = func_get_args();
678                         switch (count($args)) {
679                                 case 3:
680                                         $this->set_cache_duration($args[2]);
681                                 case 2:
682                                         $this->set_cache_location($args[1]);
683                                 case 1:
684                                         $this->set_feed_url($args[0]);
685                                         $this->init();
686                         }
687                 }
688         }
690         /**
691          * Used for converting object to a string
692          */
693         public function __toString()
694         {
695                 return md5(serialize($this->data));
696         }
698         /**
699          * Remove items that link back to this before destroying this object
700          */
701         public function __destruct()
702         {
703                 if ((version_compare(PHP_VERSION, '5.3', '<') || !gc_enabled()) && !ini_get('zend.ze1_compatibility_mode'))
704                 {
705                         if (!empty($this->data['items']))
706                         {
707                                 foreach ($this->data['items'] as $item)
708                                 {
709                                         $item->__destruct();
710                                 }
711                                 unset($item, $this->data['items']);
712                         }
713                         if (!empty($this->data['ordered_items']))
714                         {
715                                 foreach ($this->data['ordered_items'] as $item)
716                                 {
717                                         $item->__destruct();
718                                 }
719                                 unset($item, $this->data['ordered_items']);
720                         }
721                 }
722         }
724         /**
725          * Force the given data/URL to be treated as a feed
726          *
727          * This tells SimplePie to ignore the content-type provided by the server.
728          * Be careful when using this option, as it will also disable autodiscovery.
729          *
730          * @since 1.1
731          * @param bool $enable Force the given data/URL to be treated as a feed
732          */
733         public function force_feed($enable = false)
734         {
735                 $this->force_feed = (bool) $enable;
736         }
738         /**
739          * Set the URL of the feed you want to parse
740          *
741          * This allows you to enter the URL of the feed you want to parse, or the
742          * website you want to try to use auto-discovery on. This takes priority
743          * over any set raw data.
744          *
745          * You can set multiple feeds to mash together by passing an array instead
746          * of a string for the $url. Remember that with each additional feed comes
747          * additional processing and resources.
748          *
749          * @since 1.0 Preview Release
750          * @see set_raw_data()
751          * @param string|array $url This is the URL (or array of URLs) that you want to parse.
752          */
753         public function set_feed_url($url)
754         {
755                 $this->multifeed_url = array();
756                 if (is_array($url))
757                 {
758                         foreach ($url as $value)
759                         {
760                                 $this->multifeed_url[] = $this->registry->call('Misc', 'fix_protocol', array($value, 1));
761                         }
762                 }
763                 else
764                 {
765                         $this->feed_url = $this->registry->call('Misc', 'fix_protocol', array($url, 1));
766                         $this->permanent_url = $this->feed_url;
767                 }
768         }
770         /**
771          * Set an instance of {@see SimplePie_File} to use as a feed
772          *
773          * @param SimplePie_File &$file
774          * @return bool True on success, false on failure
775          */
776         public function set_file(&$file)
777         {
778                 if ($file instanceof SimplePie_File)
779                 {
780                         $this->feed_url = $file->url;
781                         $this->permanent_url = $this->feed_url;
782                         $this->file =& $file;
783                         return true;
784                 }
785                 return false;
786         }
788         /**
789          * Set the raw XML data to parse
790          *
791          * Allows you to use a string of RSS/Atom data instead of a remote feed.
792          *
793          * If you have a feed available as a string in PHP, you can tell SimplePie
794          * to parse that data string instead of a remote feed. Any set feed URL
795          * takes precedence.
796          *
797          * @since 1.0 Beta 3
798          * @param string $data RSS or Atom data as a string.
799          * @see set_feed_url()
800          */
801         public function set_raw_data($data)
802         {
803                 $this->raw_data = $data;
804         }
806         /**
807          * Set the the default timeout for fetching remote feeds
808          *
809          * This allows you to change the maximum time the feed's server to respond
810          * and send the feed back.
811          *
812          * @since 1.0 Beta 3
813          * @param int $timeout The maximum number of seconds to spend waiting to retrieve a feed.
814          */
815         public function set_timeout($timeout = 10)
816         {
817                 $this->timeout = (int) $timeout;
818         }
820         /**
821          * Set custom curl options
822          *
823          * This allows you to change default curl options
824          *
825          * @since 1.0 Beta 3
826          * @param array $curl_options Curl options to add to default settings
827          */
828         public function set_curl_options(array $curl_options = array())
829         {
830                 $this->curl_options = $curl_options;
831         }
833         /**
834          * Force SimplePie to use fsockopen() instead of cURL
835          *
836          * @since 1.0 Beta 3
837          * @param bool $enable Force fsockopen() to be used
838          */
839         public function force_fsockopen($enable = false)
840         {
841                 $this->force_fsockopen = (bool) $enable;
842         }
844         /**
845          * Enable/disable caching in SimplePie.
846          *
847          * This option allows you to disable caching all-together in SimplePie.
848          * However, disabling the cache can lead to longer load times.
849          *
850          * @since 1.0 Preview Release
851          * @param bool $enable Enable caching
852          */
853         public function enable_cache($enable = true)
854         {
855                 $this->cache = (bool) $enable;
856         }
858         /**
859          * SimplePie to continue to fall back to expired cache, if enabled, when
860          * feed is unavailable.
861          *
862          * This tells SimplePie to ignore any file errors and fall back to cache
863          * instead. This only works if caching is enabled and cached content
864          * still exists.
866          * @param bool $enable Force use of cache on fail.
867          */
868         public function force_cache_fallback($enable = false)
869         {
870                 $this->force_cache_fallback= (bool) $enable;
871         }
873         /**
874          * Set the length of time (in seconds) that the contents of a feed will be
875          * cached
876          *
877          * @param int $seconds The feed content cache duration
878          */
879         public function set_cache_duration($seconds = 3600)
880         {
881                 $this->cache_duration = (int) $seconds;
882         }
884         /**
885          * Set the length of time (in seconds) that the autodiscovered feed URL will
886          * be cached
887          *
888          * @param int $seconds The autodiscovered feed URL cache duration.
889          */
890         public function set_autodiscovery_cache_duration($seconds = 604800)
891         {
892                 $this->autodiscovery_cache_duration = (int) $seconds;
893         }
895         /**
896          * Set the file system location where the cached files should be stored
897          *
898          * @param string $location The file system location.
899          */
900         public function set_cache_location($location = './cache')
901         {
902                 $this->cache_location = (string) $location;
903         }
905         /**
906          * Set whether feed items should be sorted into reverse chronological order
907          *
908          * @param bool $enable Sort as reverse chronological order.
909          */
910         public function enable_order_by_date($enable = true)
911         {
912                 $this->order_by_date = (bool) $enable;
913         }
915         /**
916          * Set the character encoding used to parse the feed
917          *
918          * This overrides the encoding reported by the feed, however it will fall
919          * back to the normal encoding detection if the override fails
920          *
921          * @param string $encoding Character encoding
922          */
923         public function set_input_encoding($encoding = false)
924         {
925                 if ($encoding)
926                 {
927                         $this->input_encoding = (string) $encoding;
928                 }
929                 else
930                 {
931                         $this->input_encoding = false;
932                 }
933         }
935         /**
936          * Set how much feed autodiscovery to do
937          *
938          * @see SIMPLEPIE_LOCATOR_NONE
939          * @see SIMPLEPIE_LOCATOR_AUTODISCOVERY
940          * @see SIMPLEPIE_LOCATOR_LOCAL_EXTENSION
941          * @see SIMPLEPIE_LOCATOR_LOCAL_BODY
942          * @see SIMPLEPIE_LOCATOR_REMOTE_EXTENSION
943          * @see SIMPLEPIE_LOCATOR_REMOTE_BODY
944          * @see SIMPLEPIE_LOCATOR_ALL
945          * @param int $level Feed Autodiscovery Level (level can be a combination of the above constants, see bitwise OR operator)
946          */
947         public function set_autodiscovery_level($level = SIMPLEPIE_LOCATOR_ALL)
948         {
949                 $this->autodiscovery = (int) $level;
950         }
952         /**
953          * Get the class registry
954          *
955          * Use this to override SimplePie's default classes
956          * @see SimplePie_Registry
957          * @return SimplePie_Registry
958          */
959         public function &get_registry()
960         {
961                 return $this->registry;
962         }
964         /**#@+
965          * Useful when you are overloading or extending SimplePie's default classes.
966          *
967          * @deprecated Use {@see get_registry()} instead
968          * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
969          * @param string $class Name of custom class
970          * @return boolean True on success, false otherwise
971          */
972         /**
973          * Set which class SimplePie uses for caching
974          */
975         public function set_cache_class($class = 'SimplePie_Cache')
976         {
977                 return $this->registry->register('Cache', $class, true);
978         }
980         /**
981          * Set which class SimplePie uses for auto-discovery
982          */
983         public function set_locator_class($class = 'SimplePie_Locator')
984         {
985                 return $this->registry->register('Locator', $class, true);
986         }
988         /**
989          * Set which class SimplePie uses for XML parsing
990          */
991         public function set_parser_class($class = 'SimplePie_Parser')
992         {
993                 return $this->registry->register('Parser', $class, true);
994         }
996         /**
997          * Set which class SimplePie uses for remote file fetching
998          */
999         public function set_file_class($class = 'SimplePie_File')
1000         {
1001                 return $this->registry->register('File', $class, true);
1002         }
1004         /**
1005          * Set which class SimplePie uses for data sanitization
1006          */
1007         public function set_sanitize_class($class = 'SimplePie_Sanitize')
1008         {
1009                 return $this->registry->register('Sanitize', $class, true);
1010         }
1012         /**
1013          * Set which class SimplePie uses for handling feed items
1014          */
1015         public function set_item_class($class = 'SimplePie_Item')
1016         {
1017                 return $this->registry->register('Item', $class, true);
1018         }
1020         /**
1021          * Set which class SimplePie uses for handling author data
1022          */
1023         public function set_author_class($class = 'SimplePie_Author')
1024         {
1025                 return $this->registry->register('Author', $class, true);
1026         }
1028         /**
1029          * Set which class SimplePie uses for handling category data
1030          */
1031         public function set_category_class($class = 'SimplePie_Category')
1032         {
1033                 return $this->registry->register('Category', $class, true);
1034         }
1036         /**
1037          * Set which class SimplePie uses for feed enclosures
1038          */
1039         public function set_enclosure_class($class = 'SimplePie_Enclosure')
1040         {
1041                 return $this->registry->register('Enclosure', $class, true);
1042         }
1044         /**
1045          * Set which class SimplePie uses for `<media:text>` captions
1046          */
1047         public function set_caption_class($class = 'SimplePie_Caption')
1048         {
1049                 return $this->registry->register('Caption', $class, true);
1050         }
1052         /**
1053          * Set which class SimplePie uses for `<media:copyright>`
1054          */
1055         public function set_copyright_class($class = 'SimplePie_Copyright')
1056         {
1057                 return $this->registry->register('Copyright', $class, true);
1058         }
1060         /**
1061          * Set which class SimplePie uses for `<media:credit>`
1062          */
1063         public function set_credit_class($class = 'SimplePie_Credit')
1064         {
1065                 return $this->registry->register('Credit', $class, true);
1066         }
1068         /**
1069          * Set which class SimplePie uses for `<media:rating>`
1070          */
1071         public function set_rating_class($class = 'SimplePie_Rating')
1072         {
1073                 return $this->registry->register('Rating', $class, true);
1074         }
1076         /**
1077          * Set which class SimplePie uses for `<media:restriction>`
1078          */
1079         public function set_restriction_class($class = 'SimplePie_Restriction')
1080         {
1081                 return $this->registry->register('Restriction', $class, true);
1082         }
1084         /**
1085          * Set which class SimplePie uses for content-type sniffing
1086          */
1087         public function set_content_type_sniffer_class($class = 'SimplePie_Content_Type_Sniffer')
1088         {
1089                 return $this->registry->register('Content_Type_Sniffer', $class, true);
1090         }
1092         /**
1093          * Set which class SimplePie uses item sources
1094          */
1095         public function set_source_class($class = 'SimplePie_Source')
1096         {
1097                 return $this->registry->register('Source', $class, true);
1098         }
1099         /**#@-*/
1101         /**
1102          * Set the user agent string
1103          *
1104          * @param string $ua New user agent string.
1105          */
1106         public function set_useragent($ua = SIMPLEPIE_USERAGENT)
1107         {
1108                 $this->useragent = (string) $ua;
1109         }
1111         /**
1112          * Set callback function to create cache filename with
1113          *
1114          * @param mixed $function Callback function
1115          */
1116         public function set_cache_name_function($function = 'md5')
1117         {
1118                 if (is_callable($function))
1119                 {
1120                         $this->cache_name_function = $function;
1121                 }
1122         }
1124         /**
1125          * Set options to make SP as fast as possible
1126          *
1127          * Forgoes a substantial amount of data sanitization in favor of speed. This
1128          * turns SimplePie into a dumb parser of feeds.
1129          *
1130          * @param bool $set Whether to set them or not
1131          */
1132         public function set_stupidly_fast($set = false)
1133         {
1134                 if ($set)
1135                 {
1136                         $this->enable_order_by_date(false);
1137                         $this->remove_div(false);
1138                         $this->strip_comments(false);
1139                         $this->strip_htmltags(false);
1140                         $this->strip_attributes(false);
1141                         $this->add_attributes(false);
1142                         $this->set_image_handler(false);
1143                 }
1144         }
1146         /**
1147          * Set maximum number of feeds to check with autodiscovery
1148          *
1149          * @param int $max Maximum number of feeds to check
1150          */
1151         public function set_max_checked_feeds($max = 10)
1152         {
1153                 $this->max_checked_feeds = (int) $max;
1154         }
1156         public function remove_div($enable = true)
1157         {
1158                 $this->sanitize->remove_div($enable);
1159         }
1161         public function strip_htmltags($tags = '', $encode = null)
1162         {
1163                 if ($tags === '')
1164                 {
1165                         $tags = $this->strip_htmltags;
1166                 }
1167                 $this->sanitize->strip_htmltags($tags);
1168                 if ($encode !== null)
1169                 {
1170                         $this->sanitize->encode_instead_of_strip($tags);
1171                 }
1172         }
1174         public function encode_instead_of_strip($enable = true)
1175         {
1176                 $this->sanitize->encode_instead_of_strip($enable);
1177         }
1179         public function strip_attributes($attribs = '')
1180         {
1181                 if ($attribs === '')
1182                 {
1183                         $attribs = $this->strip_attributes;
1184                 }
1185                 $this->sanitize->strip_attributes($attribs);
1186         }
1188         public function add_attributes($attribs = '')
1189         {
1190                 if ($attribs === '')
1191                 {
1192                         $attribs = $this->add_attributes;
1193                 }
1194                 $this->sanitize->add_attributes($attribs);
1195         }
1197         /**
1198          * Set the output encoding
1199          *
1200          * Allows you to override SimplePie's output to match that of your webpage.
1201          * This is useful for times when your webpages are not being served as
1202          * UTF-8. This setting will be obeyed by {@see handle_content_type()}, and
1203          * is similar to {@see set_input_encoding()}.
1204          *
1205          * It should be noted, however, that not all character encodings can support
1206          * all characters. If your page is being served as ISO-8859-1 and you try
1207          * to display a Japanese feed, you'll likely see garbled characters.
1208          * Because of this, it is highly recommended to ensure that your webpages
1209          * are served as UTF-8.
1210          *
1211          * The number of supported character encodings depends on whether your web
1212          * host supports {@link http://php.net/mbstring mbstring},
1213          * {@link http://php.net/iconv iconv}, or both. See
1214          * {@link http://simplepie.org/wiki/faq/Supported_Character_Encodings} for
1215          * more information.
1216          *
1217          * @param string $encoding
1218          */
1219         public function set_output_encoding($encoding = 'UTF-8')
1220         {
1221                 $this->sanitize->set_output_encoding($encoding);
1222         }
1224         public function strip_comments($strip = false)
1225         {
1226                 $this->sanitize->strip_comments($strip);
1227         }
1229         /**
1230          * Set element/attribute key/value pairs of HTML attributes
1231          * containing URLs that need to be resolved relative to the feed
1232          *
1233          * Defaults to |a|@href, |area|@href, |blockquote|@cite, |del|@cite,
1234          * |form|@action, |img|@longdesc, |img|@src, |input|@src, |ins|@cite,
1235          * |q|@cite
1236          *
1237          * @since 1.0
1238          * @param array|null $element_attribute Element/attribute key/value pairs, null for default
1239          */
1240         public function set_url_replacements($element_attribute = null)
1241         {
1242                 $this->sanitize->set_url_replacements($element_attribute);
1243         }
1245         /**
1246          * Set the handler to enable the display of cached images.
1247          *
1248          * @param str $page Web-accessible path to the handler_image.php file.
1249          * @param str $qs The query string that the value should be passed to.
1250          */
1251         public function set_image_handler($page = false, $qs = 'i')
1252         {
1253                 if ($page !== false)
1254                 {
1255                         $this->sanitize->set_image_handler($page . '?' . $qs . '=');
1256                 }
1257                 else
1258                 {
1259                         $this->image_handler = '';
1260                 }
1261         }
1263         /**
1264          * Set the limit for items returned per-feed with multifeeds
1265          *
1266          * @param integer $limit The maximum number of items to return.
1267          */
1268         public function set_item_limit($limit = 0)
1269         {
1270                 $this->item_limit = (int) $limit;
1271         }
1273         /**
1274          * Enable throwing exceptions
1275          *
1276          * @param boolean $enable Should we throw exceptions, or use the old-style error property?
1277          */
1278         public function enable_exceptions($enable = true)
1279         {
1280                 $this->enable_exceptions = $enable;
1281         }
1283         /**
1284          * Initialize the feed object
1285          *
1286          * This is what makes everything happen. Period. This is where all of the
1287          * configuration options get processed, feeds are fetched, cached, and
1288          * parsed, and all of that other good stuff.
1289          *
1290          * @return boolean True if successful, false otherwise
1291          */
1292         public function init()
1293         {
1294                 // Check absolute bare minimum requirements.
1295                 if (!extension_loaded('xml') || !extension_loaded('pcre'))
1296                 {
1297                         return false;
1298                 }
1299                 // Then check the xml extension is sane (i.e., libxml 2.7.x issue on PHP < 5.2.9 and libxml 2.7.0 to 2.7.2 on any version) if we don't have xmlreader.
1300                 elseif (!extension_loaded('xmlreader'))
1301                 {
1302                         static $xml_is_sane = null;
1303                         if ($xml_is_sane === null)
1304                         {
1305                                 $parser_check = xml_parser_create();
1306                                 xml_parse_into_struct($parser_check, '<foo>&amp;</foo>', $values);
1307                                 xml_parser_free($parser_check);
1308                                 $xml_is_sane = isset($values[0]['value']);
1309                         }
1310                         if (!$xml_is_sane)
1311                         {
1312                                 return false;
1313                         }
1314                 }
1316                 if (method_exists($this->sanitize, 'set_registry'))
1317                 {
1318                         $this->sanitize->set_registry($this->registry);
1319                 }
1321                 // Pass whatever was set with config options over to the sanitizer.
1322                 // Pass the classes in for legacy support; new classes should use the registry instead
1323                 $this->sanitize->pass_cache_data($this->cache, $this->cache_location, $this->cache_name_function, $this->registry->get_class('Cache'));
1324                 $this->sanitize->pass_file_data($this->registry->get_class('File'), $this->timeout, $this->useragent, $this->force_fsockopen, $this->curl_options);
1326                 if (!empty($this->multifeed_url))
1327                 {
1328                         $i = 0;
1329                         $success = 0;
1330                         $this->multifeed_objects = array();
1331                         $this->error = array();
1332                         foreach ($this->multifeed_url as $url)
1333                         {
1334                                 $this->multifeed_objects[$i] = clone $this;
1335                                 $this->multifeed_objects[$i]->set_feed_url($url);
1336                                 $single_success = $this->multifeed_objects[$i]->init();
1337                                 $success |= $single_success;
1338                                 if (!$single_success)
1339                                 {
1340                                         $this->error[$i] = $this->multifeed_objects[$i]->error();
1341                                 }
1342                                 $i++;
1343                         }
1344                         return (bool) $success;
1345                 }
1346                 elseif ($this->feed_url === null && $this->raw_data === null)
1347                 {
1348                         return false;
1349                 }
1351                 $this->error = null;
1352                 $this->data = array();
1353                 $this->check_modified = false;
1354                 $this->multifeed_objects = array();
1355                 $cache = false;
1357                 if ($this->feed_url !== null)
1358                 {
1359                         $parsed_feed_url = $this->registry->call('Misc', 'parse_url', array($this->feed_url));
1361                         // Decide whether to enable caching
1362                         if ($this->cache && $parsed_feed_url['scheme'] !== '')
1363                         {
1364                                 $cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $this->feed_url), 'spc'));
1365                         }
1367                         // Fetch the data via SimplePie_File into $this->raw_data
1368                         if (($fetched = $this->fetch_data($cache)) === true)
1369                         {
1370                                 return true;
1371                         }
1372                         elseif ($fetched === false) {
1373                                 return false;
1374                         }
1376                         list($headers, $sniffed) = $fetched;
1377                 }
1379                 // Set up array of possible encodings
1380                 $encodings = array();
1382                 // First check to see if input has been overridden.
1383                 if ($this->input_encoding !== false)
1384                 {
1385                         $encodings[] = strtoupper($this->input_encoding);
1386                 }
1388                 $application_types = array('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity');
1389                 $text_types = array('text/xml', 'text/xml-external-parsed-entity');
1391                 // RFC 3023 (only applies to sniffed content)
1392                 if (isset($sniffed))
1393                 {
1394                         if (in_array($sniffed, $application_types) || substr($sniffed, 0, 12) === 'application/' && substr($sniffed, -4) === '+xml')
1395                         {
1396                                 if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset))
1397                                 {
1398                                         $encodings[] = strtoupper($charset[1]);
1399                                 }
1400                                 $encodings = array_merge($encodings, $this->registry->call('Misc', 'xml_encoding', array($this->raw_data, &$this->registry)));
1401                                 $encodings[] = 'UTF-8';
1402                         }
1403                         elseif (in_array($sniffed, $text_types) || substr($sniffed, 0, 5) === 'text/' && substr($sniffed, -4) === '+xml')
1404                         {
1405                                 if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset))
1406                                 {
1407                                         $encodings[] = strtoupper($charset[1]);
1408                                 }
1409                                 $encodings[] = 'US-ASCII';
1410                         }
1411                         // Text MIME-type default
1412                         elseif (substr($sniffed, 0, 5) === 'text/')
1413                         {
1414                                 $encodings[] = 'UTF-8';
1415                         }
1416                 }
1418                 // Fallback to XML 1.0 Appendix F.1/UTF-8/ISO-8859-1
1419                 $encodings = array_merge($encodings, $this->registry->call('Misc', 'xml_encoding', array($this->raw_data, &$this->registry)));
1420                 $encodings[] = 'UTF-8';
1421                 $encodings[] = 'ISO-8859-1';
1423                 // There's no point in trying an encoding twice
1424                 $encodings = array_unique($encodings);
1426                 // Loop through each possible encoding, till we return something, or run out of possibilities
1427                 foreach ($encodings as $encoding)
1428                 {
1429                         // Change the encoding to UTF-8 (as we always use UTF-8 internally)
1430                         if ($utf8_data = $this->registry->call('Misc', 'change_encoding', array($this->raw_data, $encoding, 'UTF-8')))
1431                         {
1432                                 // Create new parser
1433                                 $parser = $this->registry->create('Parser');
1435                                 // If it's parsed fine
1436                                 if ($parser->parse($utf8_data, 'UTF-8', $this->permanent_url))
1437                                 {
1438                                         $this->data = $parser->get_data();
1439                                         if (!($this->get_type() & ~SIMPLEPIE_TYPE_NONE))
1440                                         {
1441                                                 $this->error = "A feed could not be found at $this->feed_url. This does not appear to be a valid RSS or Atom feed.";
1442                                                 $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__));
1443                                                 return false;
1444                                         }
1446                                         if (isset($headers))
1447                                         {
1448                                                 $this->data['headers'] = $headers;
1449                                         }
1450                                         $this->data['build'] = SIMPLEPIE_BUILD;
1452                                         // Cache the file if caching is enabled
1453                                         if ($cache && !$cache->save($this))
1454                                         {
1455                                                 trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING);
1456                                         }
1457                                         return true;
1458                                 }
1459                         }
1460                 }
1462                 if (isset($parser))
1463                 {
1464                         // We have an error, just set SimplePie_Misc::error to it and quit
1465                         $this->error = $this->feed_url;
1466                         $this->error .= sprintf(' is invalid XML, likely due to invalid characters. XML error: %s at line %d, column %d', $parser->get_error_string(), $parser->get_current_line(), $parser->get_current_column());
1467                 }
1468                 else
1469                 {
1470                         $this->error = 'The data could not be converted to UTF-8. You MUST have either the iconv or mbstring extension installed. Upgrading to PHP 5.x (which includes iconv) is highly recommended.';
1471                 }
1473                 $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__));
1475                 return false;
1476         }
1478         /**
1479          * Fetch the data via SimplePie_File
1480          *
1481          * If the data is already cached, attempt to fetch it from there instead
1482          * @param SimplePie_Cache|false $cache Cache handler, or false to not load from the cache
1483          * @return array|true Returns true if the data was loaded from the cache, or an array of HTTP headers and sniffed type
1484          */
1485         protected function fetch_data(&$cache)
1486         {
1487                 // If it's enabled, use the cache
1488                 if ($cache)
1489                 {
1490                         // Load the Cache
1491                         $this->data = $cache->load();
1492                         if (!empty($this->data))
1493                         {
1494                                 // If the cache is for an outdated build of SimplePie
1495                                 if (!isset($this->data['build']) || $this->data['build'] !== SIMPLEPIE_BUILD)
1496                                 {
1497                                         $cache->unlink();
1498                                         $this->data = array();
1499                                 }
1500                                 // If we've hit a collision just rerun it with caching disabled
1501                                 elseif (isset($this->data['url']) && $this->data['url'] !== $this->feed_url)
1502                                 {
1503                                         $cache = false;
1504                                         $this->data = array();
1505                                 }
1506                                 // If we've got a non feed_url stored (if the page isn't actually a feed, or is a redirect) use that URL.
1507                                 elseif (isset($this->data['feed_url']))
1508                                 {
1509                                         // If the autodiscovery cache is still valid use it.
1510                                         if ($cache->mtime() + $this->autodiscovery_cache_duration > time())
1511                                         {
1512                                                 // Do not need to do feed autodiscovery yet.
1513                                                 if ($this->data['feed_url'] !== $this->data['url'])
1514                                                 {
1515                                                         $this->set_feed_url($this->data['feed_url']);
1516                                                         return $this->init();
1517                                                 }
1519                                                 $cache->unlink();
1520                                                 $this->data = array();
1521                                         }
1522                                 }
1523                                 // Check if the cache has been updated
1524                                 elseif ($cache->mtime() + $this->cache_duration < time())
1525                                 {
1526                                         // Want to know if we tried to send last-modified and/or etag headers
1527                                         // when requesting this file. (Note that it's up to the file to
1528                                         // support this, but we don't always send the headers either.)
1529                                         $this->check_modified = true;
1530                                         if (isset($this->data['headers']['last-modified']) || isset($this->data['headers']['etag']))
1531                                         {
1532                                                 $headers = array(
1533                                                         'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1',
1534                                                 );
1535                                                 if (isset($this->data['headers']['last-modified']))
1536                                                 {
1537                                                         $headers['if-modified-since'] = $this->data['headers']['last-modified'];
1538                                                 }
1539                                                 if (isset($this->data['headers']['etag']))
1540                                                 {
1541                                                         $headers['if-none-match'] = $this->data['headers']['etag'];
1542                                                 }
1544                                                 $file = $this->registry->create('File', array($this->feed_url, $this->timeout/10, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options));
1546                                                 if ($file->success)
1547                                                 {
1548                                                         if ($file->status_code === 304)
1549                                                         {
1550                                                                 // Set raw_data to false here too, to signify that the cache
1551                                                                 // is still valid.
1552                                                                 $this->raw_data = false;
1553                                                                 $cache->touch();
1554                                                                 return true;
1555                                                         }
1556                                                 }
1557                                                 else
1558                                                 {
1559                                                         $this->check_modified = false;
1560                                                         if($this->force_cache_fallback)
1561                                                         {
1562                                                                 $cache->touch();
1563                                                                 return true;
1564                                                         }
1566                                                         unset($file);
1567                                                 }
1568                                         }
1569                                 }
1570                                 // If the cache is still valid, just return true
1571                                 else
1572                                 {
1573                                         $this->raw_data = false;
1574                                         return true;
1575                                 }
1576                         }
1577                         // If the cache is empty, delete it
1578                         else
1579                         {
1580                                 $cache->unlink();
1581                                 $this->data = array();
1582                         }
1583                 }
1584                 // If we don't already have the file (it'll only exist if we've opened it to check if the cache has been modified), open it.
1585                 if (!isset($file))
1586                 {
1587                         if ($this->file instanceof SimplePie_File && $this->file->url === $this->feed_url)
1588                         {
1589                                 $file =& $this->file;
1590                         }
1591                         else
1592                         {
1593                                 $headers = array(
1594                                         'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1',
1595                                 );
1596                                 $file = $this->registry->create('File', array($this->feed_url, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options));
1597                         }
1598                 }
1599                 // If the file connection has an error, set SimplePie::error to that and quit
1600                 if (!$file->success && !($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300)))
1601                 {
1602                         $this->error = $file->error;
1603                         return !empty($this->data);
1604                 }
1606                 if (!$this->force_feed)
1607                 {
1608                         // Check if the supplied URL is a feed, if it isn't, look for it.
1609                         $locate = $this->registry->create('Locator', array(&$file, $this->timeout, $this->useragent, $this->max_checked_feeds));
1611                         if (!$locate->is_feed($file))
1612                         {
1613                                 $copyStatusCode = $file->status_code;
1614                                 $copyContentType = $file->headers['content-type'];
1615                                 try
1616                                 {
1617                                         $microformats = false;
1618                                         if (function_exists('Mf2\parse')) {
1619                                                 // Check for both h-feed and h-entry, as both a feed with no entries
1620                                                 // and a list of entries without an h-feed wrapper are both valid.
1621                                                 $position = 0;
1622                                                 while ($position = strpos($file->body, 'h-feed', $position))
1623                                                 {
1624                                                         $start = $position < 200 ? 0 : $position - 200;
1625                                                         $check = substr($file->body, $start, 400);
1626                                                         if ($microformats = preg_match('/class="[^"]*h-feed/', $check))
1627                                                         {
1628                                                                 break;
1629                                                         }
1630                                                         $position += 7;
1631                                                 }
1632                                                 $position = 0;
1633                                                 while ($position = strpos($file->body, 'h-entry', $position))
1634                                                 {
1635                                                         $start = $position < 200 ? 0 : $position - 200;
1636                                                         $check = substr($file->body, $start, 400);
1637                                                         if ($microformats = preg_match('/class="[^"]*h-entry/', $check))
1638                                                         {
1639                                                                 break;
1640                                                         }
1641                                                         $position += 7;
1642                                                 }
1643                                         }
1644                                         // Now also do feed discovery, but if an h-entry was found don't
1645                                         // overwrite the current value of file.
1646                                         $discovered = $locate->find($this->autodiscovery,
1647                                                                     $this->all_discovered_feeds);
1648                                         if ($microformats)
1649                                         {
1650                                                 if ($hub = $locate->get_rel_link('hub'))
1651                                                 {
1652                                                         $self = $locate->get_rel_link('self');
1653                                                         $this->store_links($file, $hub, $self);
1654                                                 }
1655                                                 // Push the current file onto all_discovered feeds so the user can
1656                                                 // be shown this as one of the options.
1657                                                 if (isset($this->all_discovered_feeds)) {
1658                                                         $this->all_discovered_feeds[] = $file;
1659                                                 }
1660                                         }
1661                                         else
1662                                         {
1663                                                 if ($discovered)
1664                                                 {
1665                                                         $file = $discovered;
1666                                                 }
1667                                                 else
1668                                                 {
1669                                                         // We need to unset this so that if SimplePie::set_file() has
1670                                                         // been called that object is untouched
1671                                                         unset($file);
1672                                                         $this->error = "A feed could not be found at `$this->feed_url`; the status code is `$copyStatusCode` and content-type is `$copyContentType`";
1673                                                         $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__));
1674                                                         return false;
1675                                                 }
1676                                         }
1677                                 }
1678                                 catch (SimplePie_Exception $e)
1679                                 {
1680                                         // We need to unset this so that if SimplePie::set_file() has been called that object is untouched
1681                                         unset($file);
1682                                         // This is usually because DOMDocument doesn't exist
1683                                         $this->error = $e->getMessage();
1684                                         $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, $e->getFile(), $e->getLine()));
1685                                         return false;
1686                                 }
1687                                 if ($cache)
1688                                 {
1689                                         $this->data = array('url' => $this->feed_url, 'feed_url' => $file->url, 'build' => SIMPLEPIE_BUILD);
1690                                         if (!$cache->save($this))
1691                                         {
1692                                                 trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING);
1693                                         }
1694                                         $cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $file->url), 'spc'));
1695                                 }
1696                                 $this->feed_url = $file->url;
1697                         }
1698                         $locate = null;
1699                 }
1701                 $this->raw_data = $file->body;
1702                 $this->permanent_url = $file->permanent_url;
1703                 $headers = $file->headers;
1704                 $sniffer = $this->registry->create('Content_Type_Sniffer', array(&$file));
1705                 $sniffed = $sniffer->get_type();
1707                 return array($headers, $sniffed);
1708         }
1710         /**
1711          * Get the error message for the occured error
1712          *
1713          * @return string|array Error message, or array of messages for multifeeds
1714          */
1715         public function error()
1716         {
1717                 return $this->error;
1718         }
1720         /**
1721          * Get the raw XML
1722          *
1723          * This is the same as the old `$feed->enable_xml_dump(true)`, but returns
1724          * the data instead of printing it.
1725          *
1726          * @return string|boolean Raw XML data, false if the cache is used
1727          */
1728         public function get_raw_data()
1729         {
1730                 return $this->raw_data;
1731         }
1733         /**
1734          * Get the character encoding used for output
1735          *
1736          * @since Preview Release
1737          * @return string
1738          */
1739         public function get_encoding()
1740         {
1741                 return $this->sanitize->output_encoding;
1742         }
1744         /**
1745          * Send the content-type header with correct encoding
1746          *
1747          * This method ensures that the SimplePie-enabled page is being served with
1748          * the correct {@link http://www.iana.org/assignments/media-types/ mime-type}
1749          * and character encoding HTTP headers (character encoding determined by the
1750          * {@see set_output_encoding} config option).
1751          *
1752          * This won't work properly if any content or whitespace has already been
1753          * sent to the browser, because it relies on PHP's
1754          * {@link http://php.net/header header()} function, and these are the
1755          * circumstances under which the function works.
1756          *
1757          * Because it's setting these settings for the entire page (as is the nature
1758          * of HTTP headers), this should only be used once per page (again, at the
1759          * top).
1760          *
1761          * @param string $mime MIME type to serve the page as
1762          */
1763         public function handle_content_type($mime = 'text/html')
1764         {
1765                 if (!headers_sent())
1766                 {
1767                         $header = "Content-type: $mime;";
1768                         if ($this->get_encoding())
1769                         {
1770                                 $header .= ' charset=' . $this->get_encoding();
1771                         }
1772                         else
1773                         {
1774                                 $header .= ' charset=UTF-8';
1775                         }
1776                         header($header);
1777                 }
1778         }
1780         /**
1781          * Get the type of the feed
1782          *
1783          * This returns a SIMPLEPIE_TYPE_* constant, which can be tested against
1784          * using {@link http://php.net/language.operators.bitwise bitwise operators}
1785          *
1786          * @since 0.8 (usage changed to using constants in 1.0)
1787          * @see SIMPLEPIE_TYPE_NONE Unknown.
1788          * @see SIMPLEPIE_TYPE_RSS_090 RSS 0.90.
1789          * @see SIMPLEPIE_TYPE_RSS_091_NETSCAPE RSS 0.91 (Netscape).
1790          * @see SIMPLEPIE_TYPE_RSS_091_USERLAND RSS 0.91 (Userland).
1791          * @see SIMPLEPIE_TYPE_RSS_091 RSS 0.91.
1792          * @see SIMPLEPIE_TYPE_RSS_092 RSS 0.92.
1793          * @see SIMPLEPIE_TYPE_RSS_093 RSS 0.93.
1794          * @see SIMPLEPIE_TYPE_RSS_094 RSS 0.94.
1795          * @see SIMPLEPIE_TYPE_RSS_10 RSS 1.0.
1796          * @see SIMPLEPIE_TYPE_RSS_20 RSS 2.0.x.
1797          * @see SIMPLEPIE_TYPE_RSS_RDF RDF-based RSS.
1798          * @see SIMPLEPIE_TYPE_RSS_SYNDICATION Non-RDF-based RSS (truly intended as syndication format).
1799          * @see SIMPLEPIE_TYPE_RSS_ALL Any version of RSS.
1800          * @see SIMPLEPIE_TYPE_ATOM_03 Atom 0.3.
1801          * @see SIMPLEPIE_TYPE_ATOM_10 Atom 1.0.
1802          * @see SIMPLEPIE_TYPE_ATOM_ALL Any version of Atom.
1803          * @see SIMPLEPIE_TYPE_ALL Any known/supported feed type.
1804          * @return int SIMPLEPIE_TYPE_* constant
1805          */
1806         public function get_type()
1807         {
1808                 if (!isset($this->data['type']))
1809                 {
1810                         $this->data['type'] = SIMPLEPIE_TYPE_ALL;
1811                         if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed']))
1812                         {
1813                                 $this->data['type'] &= SIMPLEPIE_TYPE_ATOM_10;
1814                         }
1815                         elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed']))
1816                         {
1817                                 $this->data['type'] &= SIMPLEPIE_TYPE_ATOM_03;
1818                         }
1819                         elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF']))
1820                         {
1821                                 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['channel'])
1822                                 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['image'])
1823                                 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item'])
1824                                 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['textinput']))
1825                                 {
1826                                         $this->data['type'] &= SIMPLEPIE_TYPE_RSS_10;
1827                                 }
1828                                 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['channel'])
1829                                 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['image'])
1830                                 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item'])
1831                                 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['textinput']))
1832                                 {
1833                                         $this->data['type'] &= SIMPLEPIE_TYPE_RSS_090;
1834                                 }
1835                         }
1836                         elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss']))
1837                         {
1838                                 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_ALL;
1839                                 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version']))
1840                                 {
1841                                         switch (trim($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version']))
1842                                         {
1843                                                 case '0.91':
1844                                                         $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091;
1845                                                         if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['skiphours']['hour'][0]['data']))
1846                                                         {
1847                                                                 switch (trim($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['skiphours']['hour'][0]['data']))
1848                                                                 {
1849                                                                         case '0':
1850                                                                                 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_NETSCAPE;
1851                                                                                 break;
1853                                                                         case '24':
1854                                                                                 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_USERLAND;
1855                                                                                 break;
1856                                                                 }
1857                                                         }
1858                                                         break;
1860                                                 case '0.92':
1861                                                         $this->data['type'] &= SIMPLEPIE_TYPE_RSS_092;
1862                                                         break;
1864                                                 case '0.93':
1865                                                         $this->data['type'] &= SIMPLEPIE_TYPE_RSS_093;
1866                                                         break;
1868                                                 case '0.94':
1869                                                         $this->data['type'] &= SIMPLEPIE_TYPE_RSS_094;
1870                                                         break;
1872                                                 case '2.0':
1873                                                         $this->data['type'] &= SIMPLEPIE_TYPE_RSS_20;
1874                                                         break;
1875                                         }
1876                                 }
1877                         }
1878                         else
1879                         {
1880                                 $this->data['type'] = SIMPLEPIE_TYPE_NONE;
1881                         }
1882                 }
1883                 return $this->data['type'];
1884         }
1886         /**
1887          * Get the URL for the feed
1888          * 
1889          * When the 'permanent' mode is enabled, returns the original feed URL,
1890          * except in the case of an `HTTP 301 Moved Permanently` status response,
1891          * in which case the location of the first redirection is returned.
1892          *
1893          * When the 'permanent' mode is disabled (default),
1894          * may or may not be different from the URL passed to {@see set_feed_url()},
1895          * depending on whether auto-discovery was used.
1896          *
1897          * @since Preview Release (previously called `get_feed_url()` since SimplePie 0.8.)
1898          * @todo Support <itunes:new-feed-url>
1899          * @todo Also, |atom:link|@rel=self
1900          * @param bool $permanent Permanent mode to return only the original URL or the first redirection
1901          * iff it is a 301 redirection
1902          * @return string|null
1903          */
1904         public function subscribe_url($permanent = false)
1905         {
1906                 if ($permanent)
1907                 {
1908                         if ($this->permanent_url !== null)
1909                         {
1910                                 // sanitize encodes ampersands which are required when used in a url.
1911                                 return str_replace('&amp;', '&',
1912                                                    $this->sanitize($this->permanent_url,
1913                                                                    SIMPLEPIE_CONSTRUCT_IRI));
1914                         }
1915                 }
1916                 else
1917                 {
1918                         if ($this->feed_url !== null)
1919                         {
1920                                 return str_replace('&amp;', '&',
1921                                                    $this->sanitize($this->feed_url,
1922                                                                    SIMPLEPIE_CONSTRUCT_IRI));
1923                         }
1924                 }
1925                 return null;
1926         }
1928         /**
1929          * Get data for an feed-level element
1930          *
1931          * This method allows you to get access to ANY element/attribute that is a
1932          * sub-element of the opening feed tag.
1933          *
1934          * The return value is an indexed array of elements matching the given
1935          * namespace and tag name. Each element has `attribs`, `data` and `child`
1936          * subkeys. For `attribs` and `child`, these contain namespace subkeys.
1937          * `attribs` then has one level of associative name => value data (where
1938          * `value` is a string) after the namespace. `child` has tag-indexed keys
1939          * after the namespace, each member of which is an indexed array matching
1940          * this same format.
1941          *
1942          * For example:
1943          * <pre>
1944          * // This is probably a bad example because we already support
1945          * // <media:content> natively, but it shows you how to parse through
1946          * // the nodes.
1947          * $group = $item->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'group');
1948          * $content = $group[0]['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'];
1949          * $file = $content[0]['attribs']['']['url'];
1950          * echo $file;
1951          * </pre>
1952          *
1953          * @since 1.0
1954          * @see http://simplepie.org/wiki/faq/supported_xml_namespaces
1955          * @param string $namespace The URL of the XML namespace of the elements you're trying to access
1956          * @param string $tag Tag name
1957          * @return array
1958          */
1959         public function get_feed_tags($namespace, $tag)
1960         {
1961                 $type = $this->get_type();
1962                 if ($type & SIMPLEPIE_TYPE_ATOM_10)
1963                 {
1964                         if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag]))
1965                         {
1966                                 return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag];
1967                         }
1968                 }
1969                 if ($type & SIMPLEPIE_TYPE_ATOM_03)
1970                 {
1971                         if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag]))
1972                         {
1973                                 return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag];
1974                         }
1975                 }
1976                 if ($type & SIMPLEPIE_TYPE_RSS_RDF)
1977                 {
1978                         if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag]))
1979                         {
1980                                 return $this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag];
1981                         }
1982                 }
1983                 if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION)
1984                 {
1985                         if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag]))
1986                         {
1987                                 return $this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag];
1988                         }
1989                 }
1990                 return null;
1991         }
1993         /**
1994          * Get data for an channel-level element
1995          *
1996          * This method allows you to get access to ANY element/attribute in the
1997          * channel/header section of the feed.
1998          *
1999          * See {@see SimplePie::get_feed_tags()} for a description of the return value
2000          *
2001          * @since 1.0
2002          * @see http://simplepie.org/wiki/faq/supported_xml_namespaces
2003          * @param string $namespace The URL of the XML namespace of the elements you're trying to access
2004          * @param string $tag Tag name
2005          * @return array
2006          */
2007         public function get_channel_tags($namespace, $tag)
2008         {
2009                 $type = $this->get_type();
2010                 if ($type & SIMPLEPIE_TYPE_ATOM_ALL)
2011                 {
2012                         if ($return = $this->get_feed_tags($namespace, $tag))
2013                         {
2014                                 return $return;
2015                         }
2016                 }
2017                 if ($type & SIMPLEPIE_TYPE_RSS_10)
2018                 {
2019                         if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'channel'))
2020                         {
2021                                 if (isset($channel[0]['child'][$namespace][$tag]))
2022                                 {
2023                                         return $channel[0]['child'][$namespace][$tag];
2024                                 }
2025                         }
2026                 }
2027                 if ($type & SIMPLEPIE_TYPE_RSS_090)
2028                 {
2029                         if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'channel'))
2030                         {
2031                                 if (isset($channel[0]['child'][$namespace][$tag]))
2032                                 {
2033                                         return $channel[0]['child'][$namespace][$tag];
2034                                 }
2035                         }
2036                 }
2037                 if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION)
2038                 {
2039                         if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'channel'))
2040                         {
2041                                 if (isset($channel[0]['child'][$namespace][$tag]))
2042                                 {
2043                                         return $channel[0]['child'][$namespace][$tag];
2044                                 }
2045                         }
2046                 }
2047                 return null;
2048         }
2050         /**
2051          * Get data for an channel-level element
2052          *
2053          * This method allows you to get access to ANY element/attribute in the
2054          * image/logo section of the feed.
2055          *
2056          * See {@see SimplePie::get_feed_tags()} for a description of the return value
2057          *
2058          * @since 1.0
2059          * @see http://simplepie.org/wiki/faq/supported_xml_namespaces
2060          * @param string $namespace The URL of the XML namespace of the elements you're trying to access
2061          * @param string $tag Tag name
2062          * @return array
2063          */
2064         public function get_image_tags($namespace, $tag)
2065         {
2066                 $type = $this->get_type();
2067                 if ($type & SIMPLEPIE_TYPE_RSS_10)
2068                 {
2069                         if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'image'))
2070                         {
2071                                 if (isset($image[0]['child'][$namespace][$tag]))
2072                                 {
2073                                         return $image[0]['child'][$namespace][$tag];
2074                                 }
2075                         }
2076                 }
2077                 if ($type & SIMPLEPIE_TYPE_RSS_090)
2078                 {
2079                         if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'image'))
2080                         {
2081                                 if (isset($image[0]['child'][$namespace][$tag]))
2082                                 {
2083                                         return $image[0]['child'][$namespace][$tag];
2084                                 }
2085                         }
2086                 }
2087                 if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION)
2088                 {
2089                         if ($image = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'image'))
2090                         {
2091                                 if (isset($image[0]['child'][$namespace][$tag]))
2092                                 {
2093                                         return $image[0]['child'][$namespace][$tag];
2094                                 }
2095                         }
2096                 }
2097                 return null;
2098         }
2100         /**
2101          * Get the base URL value from the feed
2102          *
2103          * Uses `<xml:base>` if available, otherwise uses the first link in the
2104          * feed, or failing that, the URL of the feed itself.
2105          *
2106          * @see get_link
2107          * @see subscribe_url
2108          *
2109          * @param array $element
2110          * @return string
2111          */
2112         public function get_base($element = array())
2113         {
2114                 if (!($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION) && !empty($element['xml_base_explicit']) && isset($element['xml_base']))
2115                 {
2116                         return $element['xml_base'];
2117                 }
2118                 elseif ($this->get_link() !== null)
2119                 {
2120                         return $this->get_link();
2121                 }
2122                 else
2123                 {
2124                         return $this->subscribe_url();
2125                 }
2126         }
2128         /**
2129          * Sanitize feed data
2130          *
2131          * @access private
2132          * @see SimplePie_Sanitize::sanitize()
2133          * @param string $data Data to sanitize
2134          * @param int $type One of the SIMPLEPIE_CONSTRUCT_* constants
2135          * @param string $base Base URL to resolve URLs against
2136          * @return string Sanitized data
2137          */
2138         public function sanitize($data, $type, $base = '')
2139         {
2140                 try
2141                 {
2142                         return $this->sanitize->sanitize($data, $type, $base);
2143                 }
2144                 catch (SimplePie_Exception $e)
2145                 {
2146                         if (!$this->enable_exceptions)
2147                         {
2148                                 $this->error = $e->getMessage();
2149                                 $this->registry->call('Misc', 'error', array($this->error, E_USER_WARNING, $e->getFile(), $e->getLine()));
2150                                 return '';
2151                         }
2153                         throw $e;
2154                 }
2155         }
2157         /**
2158          * Get the title of the feed
2159          *
2160          * Uses `<atom:title>`, `<title>` or `<dc:title>`
2161          *
2162          * @since 1.0 (previously called `get_feed_title` since 0.8)
2163          * @return string|null
2164          */
2165         public function get_title()
2166         {
2167                 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title'))
2168                 {
2169                         return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
2170                 }
2171                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title'))
2172                 {
2173                         return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
2174                 }
2175                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
2176                 {
2177                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
2178                 }
2179                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
2180                 {
2181                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
2182                 }
2183                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
2184                 {
2185                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
2186                 }
2187                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
2188                 {
2189                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2190                 }
2191                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
2192                 {
2193                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2194                 }
2195                 else
2196                 {
2197                         return null;
2198                 }
2199         }
2201         /**
2202          * Get a category for the feed
2203          *
2204          * @since Unknown
2205          * @param int $key The category that you want to return. Remember that arrays begin with 0, not 1
2206          * @return SimplePie_Category|null
2207          */
2208         public function get_category($key = 0)
2209         {
2210                 $categories = $this->get_categories();
2211                 if (isset($categories[$key]))
2212                 {
2213                         return $categories[$key];
2214                 }
2215                 else
2216                 {
2217                         return null;
2218                 }
2219         }
2221         /**
2222          * Get all categories for the feed
2223          *
2224          * Uses `<atom:category>`, `<category>` or `<dc:subject>`
2225          *
2226          * @since Unknown
2227          * @return array|null List of {@see SimplePie_Category} objects
2228          */
2229         public function get_categories()
2230         {
2231                 $categories = array();
2233                 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category)
2234                 {
2235                         $term = null;
2236                         $scheme = null;
2237                         $label = null;
2238                         if (isset($category['attribs']['']['term']))
2239                         {
2240                                 $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT);
2241                         }
2242                         if (isset($category['attribs']['']['scheme']))
2243                         {
2244                                 $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
2245                         }
2246                         if (isset($category['attribs']['']['label']))
2247                         {
2248                                 $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
2249                         }
2250                         $categories[] = $this->registry->create('Category', array($term, $scheme, $label));
2251                 }
2252                 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category)
2253                 {
2254                         // This is really the label, but keep this as the term also for BC.
2255                         // Label will also work on retrieving because that falls back to term.
2256                         $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2257                         if (isset($category['attribs']['']['domain']))
2258                         {
2259                                 $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT);
2260                         }
2261                         else
2262                         {
2263                                 $scheme = null;
2264                         }
2265                         $categories[] = $this->registry->create('Category', array($term, $scheme, null));
2266                 }
2267                 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category)
2268                 {
2269                         $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2270                 }
2271                 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category)
2272                 {
2273                         $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2274                 }
2276                 if (!empty($categories))
2277                 {
2278                         return array_unique($categories);
2279                 }
2280                 else
2281                 {
2282                         return null;
2283                 }
2284         }
2286         /**
2287          * Get an author for the feed
2288          *
2289          * @since 1.1
2290          * @param int $key The author that you want to return. Remember that arrays begin with 0, not 1
2291          * @return SimplePie_Author|null
2292          */
2293         public function get_author($key = 0)
2294         {
2295                 $authors = $this->get_authors();
2296                 if (isset($authors[$key]))
2297                 {
2298                         return $authors[$key];
2299                 }
2300                 else
2301                 {
2302                         return null;
2303                 }
2304         }
2306         /**
2307          * Get all authors for the feed
2308          *
2309          * Uses `<atom:author>`, `<author>`, `<dc:creator>` or `<itunes:author>`
2310          *
2311          * @since 1.1
2312          * @return array|null List of {@see SimplePie_Author} objects
2313          */
2314         public function get_authors()
2315         {
2316                 $authors = array();
2317                 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author)
2318                 {
2319                         $name = null;
2320                         $uri = null;
2321                         $email = null;
2322                         if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
2323                         {
2324                                 $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2325                         }
2326                         if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
2327                         {
2328                                 $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
2329                         }
2330                         if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
2331                         {
2332                                 $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2333                         }
2334                         if ($name !== null || $email !== null || $uri !== null)
2335                         {
2336                                 $authors[] = $this->registry->create('Author', array($name, $uri, $email));
2337                         }
2338                 }
2339                 if ($author = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author'))
2340                 {
2341                         $name = null;
2342                         $url = null;
2343                         $email = null;
2344                         if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
2345                         {
2346                                 $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2347                         }
2348                         if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
2349                         {
2350                                 $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
2351                         }
2352                         if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
2353                         {
2354                                 $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2355                         }
2356                         if ($name !== null || $email !== null || $url !== null)
2357                         {
2358                                 $authors[] = $this->registry->create('Author', array($name, $url, $email));
2359                         }
2360                 }
2361                 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author)
2362                 {
2363                         $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2364                 }
2365                 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author)
2366                 {
2367                         $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2368                 }
2369                 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author)
2370                 {
2371                         $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2372                 }
2374                 if (!empty($authors))
2375                 {
2376                         return array_unique($authors);
2377                 }
2378                 else
2379                 {
2380                         return null;
2381                 }
2382         }
2384         /**
2385          * Get a contributor for the feed
2386          *
2387          * @since 1.1
2388          * @param int $key The contrbutor that you want to return. Remember that arrays begin with 0, not 1
2389          * @return SimplePie_Author|null
2390          */
2391         public function get_contributor($key = 0)
2392         {
2393                 $contributors = $this->get_contributors();
2394                 if (isset($contributors[$key]))
2395                 {
2396                         return $contributors[$key];
2397                 }
2398                 else
2399                 {
2400                         return null;
2401                 }
2402         }
2404         /**
2405          * Get all contributors for the feed
2406          *
2407          * Uses `<atom:contributor>`
2408          *
2409          * @since 1.1
2410          * @return array|null List of {@see SimplePie_Author} objects
2411          */
2412         public function get_contributors()
2413         {
2414                 $contributors = array();
2415                 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor)
2416                 {
2417                         $name = null;
2418                         $uri = null;
2419                         $email = null;
2420                         if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
2421                         {
2422                                 $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2423                         }
2424                         if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
2425                         {
2426                                 $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
2427                         }
2428                         if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
2429                         {
2430                                 $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2431                         }
2432                         if ($name !== null || $email !== null || $uri !== null)
2433                         {
2434                                 $contributors[] = $this->registry->create('Author', array($name, $uri, $email));
2435                         }
2436                 }
2437                 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor)
2438                 {
2439                         $name = null;
2440                         $url = null;
2441                         $email = null;
2442                         if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
2443                         {
2444                                 $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2445                         }
2446                         if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
2447                         {
2448                                 $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
2449                         }
2450                         if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
2451                         {
2452                                 $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2453                         }
2454                         if ($name !== null || $email !== null || $url !== null)
2455                         {
2456                                 $contributors[] = $this->registry->create('Author', array($name, $url, $email));
2457                         }
2458                 }
2460                 if (!empty($contributors))
2461                 {
2462                         return array_unique($contributors);
2463                 }
2464                 else
2465                 {
2466                         return null;
2467                 }
2468         }
2470         /**
2471          * Get a single link for the feed
2472          *
2473          * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8)
2474          * @param int $key The link that you want to return. Remember that arrays begin with 0, not 1
2475          * @param string $rel The relationship of the link to return
2476          * @return string|null Link URL
2477          */
2478         public function get_link($key = 0, $rel = 'alternate')
2479         {
2480                 $links = $this->get_links($rel);
2481                 if (isset($links[$key]))
2482                 {
2483                         return $links[$key];
2484                 }
2485                 else
2486                 {
2487                         return null;
2488                 }
2489         }
2491         /**
2492          * Get the permalink for the item
2493          *
2494          * Returns the first link available with a relationship of "alternate".
2495          * Identical to {@see get_link()} with key 0
2496          *
2497          * @see get_link
2498          * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8)
2499          * @internal Added for parity between the parent-level and the item/entry-level.
2500          * @return string|null Link URL
2501          */
2502         public function get_permalink()
2503         {
2504                 return $this->get_link(0);
2505         }
2507         /**
2508          * Get all links for the feed
2509          *
2510          * Uses `<atom:link>` or `<link>`
2511          *
2512          * @since Beta 2
2513          * @param string $rel The relationship of links to return
2514          * @return array|null Links found for the feed (strings)
2515          */
2516         public function get_links($rel = 'alternate')
2517         {
2518                 if (!isset($this->data['links']))
2519                 {
2520                         $this->data['links'] = array();
2521                         if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link'))
2522                         {
2523                                 foreach ($links as $link)
2524                                 {
2525                                         if (isset($link['attribs']['']['href']))
2526                                         {
2527                                                 $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
2528                                                 $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
2529                                         }
2530                                 }
2531                         }
2532                         if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link'))
2533                         {
2534                                 foreach ($links as $link)
2535                                 {
2536                                         if (isset($link['attribs']['']['href']))
2537                                         {
2538                                                 $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
2539                                                 $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
2541                                         }
2542                                 }
2543                         }
2544                         if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
2545                         {
2546                                 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
2547                         }
2548                         if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
2549                         {
2550                                 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
2551                         }
2552                         if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
2553                         {
2554                                 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
2555                         }
2557                         $keys = array_keys($this->data['links']);
2558                         foreach ($keys as $key)
2559                         {
2560                                 if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key)))
2561                                 {
2562                                         if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]))
2563                                         {
2564                                                 $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]);
2565                                                 $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key];
2566                                         }
2567                                         else
2568                                         {
2569                                                 $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key];
2570                                         }
2571                                 }
2572                                 elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY)
2573                                 {
2574                                         $this->data['links'][substr($key, 41)] =& $this->data['links'][$key];
2575                                 }
2576                                 $this->data['links'][$key] = array_unique($this->data['links'][$key]);
2577                         }
2578                 }
2580                 if (isset($this->data['links'][$rel]))
2581                 {
2582                         return $this->data['links'][$rel];
2583                 }
2584                 else if (isset($this->data['headers']['link']) &&
2585                          preg_match('/<([^>]+)>; rel='.preg_quote($rel).'/',
2586                                     $this->data['headers']['link'], $match))
2587                 {
2588                         return array($match[1]);
2589                 }
2590                 else
2591                 {
2592                         return null;
2593                 }
2594         }
2596         public function get_all_discovered_feeds()
2597         {
2598                 return $this->all_discovered_feeds;
2599         }
2601         /**
2602          * Get the content for the item
2603          *
2604          * Uses `<atom:subtitle>`, `<atom:tagline>`, `<description>`,
2605          * `<dc:description>`, `<itunes:summary>` or `<itunes:subtitle>`
2606          *
2607          * @since 1.0 (previously called `get_feed_description()` since 0.8)
2608          * @return string|null
2609          */
2610         public function get_description()
2611         {
2612                 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle'))
2613                 {
2614                         return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
2615                 }
2616                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline'))
2617                 {
2618                         return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
2619                 }
2620                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description'))
2621                 {
2622                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
2623                 }
2624                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description'))
2625                 {
2626                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
2627                 }
2628                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description'))
2629                 {
2630                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
2631                 }
2632                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description'))
2633                 {
2634                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2635                 }
2636                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description'))
2637                 {
2638                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2639                 }
2640                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary'))
2641                 {
2642                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
2643                 }
2644                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle'))
2645                 {
2646                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
2647                 }
2648                 else
2649                 {
2650                         return null;
2651                 }
2652         }
2654         /**
2655          * Get the copyright info for the feed
2656          *
2657          * Uses `<atom:rights>`, `<atom:copyright>` or `<dc:rights>`
2658          *
2659          * @since 1.0 (previously called `get_feed_copyright()` since 0.8)
2660          * @return string|null
2661          */
2662         public function get_copyright()
2663         {
2664                 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights'))
2665                 {
2666                         return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
2667                 }
2668                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright'))
2669                 {
2670                         return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
2671                 }
2672                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'copyright'))
2673                 {
2674                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2675                 }
2676                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights'))
2677                 {
2678                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2679                 }
2680                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights'))
2681                 {
2682                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2683                 }
2684                 else
2685                 {
2686                         return null;
2687                 }
2688         }
2690         /**
2691          * Get the language for the feed
2692          *
2693          * Uses `<language>`, `<dc:language>`, or @xml_lang
2694          *
2695          * @since 1.0 (previously called `get_feed_language()` since 0.8)
2696          * @return string|null
2697          */
2698         public function get_language()
2699         {
2700                 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'language'))
2701                 {
2702                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2703                 }
2704                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language'))
2705                 {
2706                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2707                 }
2708                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language'))
2709                 {
2710                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2711                 }
2712                 elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang']))
2713                 {
2714                         return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
2715                 }
2716                 elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang']))
2717                 {
2718                         return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
2719                 }
2720                 elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang']))
2721                 {
2722                         return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
2723                 }
2724                 elseif (isset($this->data['headers']['content-language']))
2725                 {
2726                         return $this->sanitize($this->data['headers']['content-language'], SIMPLEPIE_CONSTRUCT_TEXT);
2727                 }
2728                 else
2729                 {
2730                         return null;
2731                 }
2732         }
2734         /**
2735          * Get the latitude coordinates for the item
2736          *
2737          * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
2738          *
2739          * Uses `<geo:lat>` or `<georss:point>`
2740          *
2741          * @since 1.0
2742          * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
2743          * @link http://www.georss.org/ GeoRSS
2744          * @return string|null
2745          */
2746         public function get_latitude()
2747         {
2749                 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat'))
2750                 {
2751                         return (float) $return[0]['data'];
2752                 }
2753                 elseif (($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
2754                 {
2755                         return (float) $match[1];
2756                 }
2757                 else
2758                 {
2759                         return null;
2760                 }
2761         }
2763         /**
2764          * Get the longitude coordinates for the feed
2765          *
2766          * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
2767          *
2768          * Uses `<geo:long>`, `<geo:lon>` or `<georss:point>`
2769          *
2770          * @since 1.0
2771          * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
2772          * @link http://www.georss.org/ GeoRSS
2773          * @return string|null
2774          */
2775         public function get_longitude()
2776         {
2777                 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long'))
2778                 {
2779                         return (float) $return[0]['data'];
2780                 }
2781                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon'))
2782                 {
2783                         return (float) $return[0]['data'];
2784                 }
2785                 elseif (($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
2786                 {
2787                         return (float) $match[2];
2788                 }
2789                 else
2790                 {
2791                         return null;
2792                 }
2793         }
2795         /**
2796          * Get the feed logo's title
2797          *
2798          * RSS 0.9.0, 1.0 and 2.0 feeds are allowed to have a "feed logo" title.
2799          *
2800          * Uses `<image><title>` or `<image><dc:title>`
2801          *
2802          * @return string|null
2803          */
2804         public function get_image_title()
2805         {
2806                 if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
2807                 {
2808                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2809                 }
2810                 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
2811                 {
2812                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2813                 }
2814                 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
2815                 {
2816                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2817                 }
2818                 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
2819                 {
2820                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2821                 }
2822                 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
2823                 {
2824                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2825                 }
2826                 else
2827                 {
2828                         return null;
2829                 }
2830         }
2832         /**
2833          * Get the feed logo's URL
2834          *
2835          * RSS 0.9.0, 2.0, Atom 1.0, and feeds with iTunes RSS tags are allowed to
2836          * have a "feed logo" URL. This points directly to the image itself.
2837          *
2838          * Uses `<itunes:image>`, `<atom:logo>`, `<atom:icon>`,
2839          * `<image><title>` or `<image><dc:title>`
2840          *
2841          * @return string|null
2842          */
2843         public function get_image_url()
2844         {
2845                 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image'))
2846                 {
2847                         return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI);
2848                 }
2849                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo'))
2850                 {
2851                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2852                 }
2853                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon'))
2854                 {
2855                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2856                 }
2857                 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'url'))
2858                 {
2859                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2860                 }
2861                 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'url'))
2862                 {
2863                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2864                 }
2865                 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url'))
2866                 {
2867                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2868                 }
2869                 else
2870                 {
2871                         return null;
2872                 }
2873         }
2876         /**
2877          * Get the feed logo's link
2878          *
2879          * RSS 0.9.0, 1.0 and 2.0 feeds are allowed to have a "feed logo" link. This
2880          * points to a human-readable page that the image should link to.
2881          *
2882          * Uses `<itunes:image>`, `<atom:logo>`, `<atom:icon>`,
2883          * `<image><title>` or `<image><dc:title>`
2884          *
2885          * @return string|null
2886          */
2887         public function get_image_link()
2888         {
2889                 if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
2890                 {
2891                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2892                 }
2893                 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
2894                 {
2895                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2896                 }
2897                 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
2898                 {
2899                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2900                 }
2901                 else
2902                 {
2903                         return null;
2904                 }
2905         }
2907         /**
2908          * Get the feed logo's link
2909          *
2910          * RSS 2.0 feeds are allowed to have a "feed logo" width.
2911          *
2912          * Uses `<image><width>` or defaults to 88.0 if no width is specified and
2913          * the feed is an RSS 2.0 feed.
2914          *
2915          * @return int|float|null
2916          */
2917         public function get_image_width()
2918         {
2919                 if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'width'))
2920                 {
2921                         return round($return[0]['data']);
2922                 }
2923                 elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url'))
2924                 {
2925                         return 88.0;
2926                 }
2927                 else
2928                 {
2929                         return null;
2930                 }
2931         }
2933         /**
2934          * Get the feed logo's height
2935          *
2936          * RSS 2.0 feeds are allowed to have a "feed logo" height.
2937          *
2938          * Uses `<image><height>` or defaults to 31.0 if no height is specified and
2939          * the feed is an RSS 2.0 feed.
2940          *
2941          * @return int|float|null
2942          */
2943         public function get_image_height()
2944         {
2945                 if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'height'))
2946                 {
2947                         return round($return[0]['data']);
2948                 }
2949                 elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url'))
2950                 {
2951                         return 31.0;
2952                 }
2953                 else
2954                 {
2955                         return null;
2956                 }
2957         }
2959         /**
2960          * Get the number of items in the feed
2961          *
2962          * This is well-suited for {@link http://php.net/for for()} loops with
2963          * {@see get_item()}
2964          *
2965          * @param int $max Maximum value to return. 0 for no limit
2966          * @return int Number of items in the feed
2967          */
2968         public function get_item_quantity($max = 0)
2969         {