b33c635f15a0b1ea79bf9c19414b001a33205384
[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-2012, 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.3.1
37  * @copyright 2004-2012 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.3.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 object Instance of SimplePie_File to use as a feed
450          * @see SimplePie::set_file()
451          * @access private
452          */
453         public $file;
455         /**
456          * @var string Raw feed data
457          * @see SimplePie::set_raw_data()
458          * @access private
459          */
460         public $raw_data;
462         /**
463          * @var int Timeout for fetching remote files
464          * @see SimplePie::set_timeout()
465          * @access private
466          */
467         public $timeout = 10;
469         /**
470          * @var bool Forces fsockopen() to be used for remote files instead
471          * of cURL, even if a new enough version is installed
472          * @see SimplePie::force_fsockopen()
473          * @access private
474          */
475         public $force_fsockopen = false;
477         /**
478          * @var bool Force the given data/URL to be treated as a feed no matter what
479          * it appears like
480          * @see SimplePie::force_feed()
481          * @access private
482          */
483         public $force_feed = false;
485         /**
486          * @var bool Enable/Disable Caching
487          * @see SimplePie::enable_cache()
488          * @access private
489          */
490         public $cache = true;
492         /**
493          * @var int Cache duration (in seconds)
494          * @see SimplePie::set_cache_duration()
495          * @access private
496          */
497         public $cache_duration = 3600;
499         /**
500          * @var int Auto-discovery cache duration (in seconds)
501          * @see SimplePie::set_autodiscovery_cache_duration()
502          * @access private
503          */
504         public $autodiscovery_cache_duration = 604800; // 7 Days.
506         /**
507          * @var string Cache location (relative to executing script)
508          * @see SimplePie::set_cache_location()
509          * @access private
510          */
511         public $cache_location = './cache';
513         /**
514          * @var string Function that creates the cache filename
515          * @see SimplePie::set_cache_name_function()
516          * @access private
517          */
518         public $cache_name_function = 'md5';
520         /**
521          * @var bool Reorder feed by date descending
522          * @see SimplePie::enable_order_by_date()
523          * @access private
524          */
525         public $order_by_date = true;
527         /**
528          * @var mixed Force input encoding to be set to the follow value
529          * (false, or anything type-cast to false, disables this feature)
530          * @see SimplePie::set_input_encoding()
531          * @access private
532          */
533         public $input_encoding = false;
535         /**
536          * @var int Feed Autodiscovery Level
537          * @see SimplePie::set_autodiscovery_level()
538          * @access private
539          */
540         public $autodiscovery = SIMPLEPIE_LOCATOR_ALL;
542         /**
543          * Class registry object
544          *
545          * @var SimplePie_Registry
546          */
547         public $registry;
549         /**
550          * @var int Maximum number of feeds to check with autodiscovery
551          * @see SimplePie::set_max_checked_feeds()
552          * @access private
553          */
554         public $max_checked_feeds = 10;
556         /**
557          * @var array All the feeds found during the autodiscovery process
558          * @see SimplePie::get_all_discovered_feeds()
559          * @access private
560          */
561         public $all_discovered_feeds = array();
563         /**
564          * @var string Web-accessible path to the handler_image.php file.
565          * @see SimplePie::set_image_handler()
566          * @access private
567          */
568         public $image_handler = '';
570         /**
571          * @var array Stores the URLs when multiple feeds are being initialized.
572          * @see SimplePie::set_feed_url()
573          * @access private
574          */
575         public $multifeed_url = array();
577         /**
578          * @var array Stores SimplePie objects when multiple feeds initialized.
579          * @access private
580          */
581         public $multifeed_objects = array();
583         /**
584          * @var array Stores the get_object_vars() array for use with multifeeds.
585          * @see SimplePie::set_feed_url()
586          * @access private
587          */
588         public $config_settings = null;
590         /**
591          * @var integer Stores the number of items to return per-feed with multifeeds.
592          * @see SimplePie::set_item_limit()
593          * @access private
594          */
595         public $item_limit = 0;
597         /**
598          * @var array Stores the default attributes to be stripped by strip_attributes().
599          * @see SimplePie::strip_attributes()
600          * @access private
601          */
602         public $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc');
604         /**
605          * @var array Stores the default tags to be stripped by strip_htmltags().
606          * @see SimplePie::strip_htmltags()
607          * @access private
608          */
609         public $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style');
611         /**
612          * The SimplePie class contains feed level data and options
613          *
614          * To use SimplePie, create the SimplePie object with no parameters. You can
615          * then set configuration options using the provided methods. After setting
616          * them, you must initialise the feed using $feed->init(). At that point the
617          * object's methods and properties will be available to you.
618          *
619          * Previously, it was possible to pass in the feed URL along with cache
620          * options directly into the constructor. This has been removed as of 1.3 as
621          * it caused a lot of confusion.
622          *
623          * @since 1.0 Preview Release
624          */
625         public function __construct()
626         {
627                 if (version_compare(PHP_VERSION, '5.2', '<'))
628                 {
629                         trigger_error('PHP 4.x, 5.0 and 5.1 are no longer supported. Please upgrade to PHP 5.2 or newer.');
630                         die();
631                 }
633                 // Other objects, instances created here so we can set options on them
634                 $this->sanitize = new SimplePie_Sanitize();
635                 $this->registry = new SimplePie_Registry();
637                 if (func_num_args() > 0)
638                 {
639                         $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING;
640                         trigger_error('Passing parameters to the constructor is no longer supported. Please use set_feed_url(), set_cache_location(), and set_cache_location() directly.', $level);
642                         $args = func_get_args();
643                         switch (count($args)) {
644                                 case 3:
645                                         $this->set_cache_duration($args[2]);
646                                 case 2:
647                                         $this->set_cache_location($args[1]);
648                                 case 1:
649                                         $this->set_feed_url($args[0]);
650                                         $this->init();
651                         }
652                 }
653         }
655         /**
656          * Used for converting object to a string
657          */
658         public function __toString()
659         {
660                 return md5(serialize($this->data));
661         }
663         /**
664          * Remove items that link back to this before destroying this object
665          */
666         public function __destruct()
667         {
668                 if ((version_compare(PHP_VERSION, '5.3', '<') || !gc_enabled()) && !ini_get('zend.ze1_compatibility_mode'))
669                 {
670                         if (!empty($this->data['items']))
671                         {
672                                 foreach ($this->data['items'] as $item)
673                                 {
674                                         $item->__destruct();
675                                 }
676                                 unset($item, $this->data['items']);
677                         }
678                         if (!empty($this->data['ordered_items']))
679                         {
680                                 foreach ($this->data['ordered_items'] as $item)
681                                 {
682                                         $item->__destruct();
683                                 }
684                                 unset($item, $this->data['ordered_items']);
685                         }
686                 }
687         }
689         /**
690          * Force the given data/URL to be treated as a feed
691          *
692          * This tells SimplePie to ignore the content-type provided by the server.
693          * Be careful when using this option, as it will also disable autodiscovery.
694          *
695          * @since 1.1
696          * @param bool $enable Force the given data/URL to be treated as a feed
697          */
698         public function force_feed($enable = false)
699         {
700                 $this->force_feed = (bool) $enable;
701         }
703         /**
704          * Set the URL of the feed you want to parse
705          *
706          * This allows you to enter the URL of the feed you want to parse, or the
707          * website you want to try to use auto-discovery on. This takes priority
708          * over any set raw data.
709          *
710          * You can set multiple feeds to mash together by passing an array instead
711          * of a string for the $url. Remember that with each additional feed comes
712          * additional processing and resources.
713          *
714          * @since 1.0 Preview Release
715          * @see set_raw_data()
716          * @param string|array $url This is the URL (or array of URLs) that you want to parse.
717          */
718         public function set_feed_url($url)
719         {
720                 $this->multifeed_url = array();
721                 if (is_array($url))
722                 {
723                         foreach ($url as $value)
724                         {
725                                 $this->multifeed_url[] = $this->registry->call('Misc', 'fix_protocol', array($value, 1));
726                         }
727                 }
728                 else
729                 {
730                         $this->feed_url = $this->registry->call('Misc', 'fix_protocol', array($url, 1));
731                 }
732         }
734         /**
735          * Set an instance of {@see SimplePie_File} to use as a feed
736          *
737          * @param SimplePie_File &$file
738          * @return bool True on success, false on failure
739          */
740         public function set_file(&$file)
741         {
742                 if ($file instanceof SimplePie_File)
743                 {
744                         $this->feed_url = $file->url;
745                         $this->file =& $file;
746                         return true;
747                 }
748                 return false;
749         }
751         /**
752          * Set the raw XML data to parse
753          *
754          * Allows you to use a string of RSS/Atom data instead of a remote feed.
755          *
756          * If you have a feed available as a string in PHP, you can tell SimplePie
757          * to parse that data string instead of a remote feed. Any set feed URL
758          * takes precedence.
759          *
760          * @since 1.0 Beta 3
761          * @param string $data RSS or Atom data as a string.
762          * @see set_feed_url()
763          */
764         public function set_raw_data($data)
765         {
766                 $this->raw_data = $data;
767         }
769         /**
770          * Set the the default timeout for fetching remote feeds
771          *
772          * This allows you to change the maximum time the feed's server to respond
773          * and send the feed back.
774          *
775          * @since 1.0 Beta 3
776          * @param int $timeout The maximum number of seconds to spend waiting to retrieve a feed.
777          */
778         public function set_timeout($timeout = 10)
779         {
780                 $this->timeout = (int) $timeout;
781         }
783         /**
784          * Force SimplePie to use fsockopen() instead of cURL
785          *
786          * @since 1.0 Beta 3
787          * @param bool $enable Force fsockopen() to be used
788          */
789         public function force_fsockopen($enable = false)
790         {
791                 $this->force_fsockopen = (bool) $enable;
792         }
794         /**
795          * Enable/disable caching in SimplePie.
796          *
797          * This option allows you to disable caching all-together in SimplePie.
798          * However, disabling the cache can lead to longer load times.
799          *
800          * @since 1.0 Preview Release
801          * @param bool $enable Enable caching
802          */
803         public function enable_cache($enable = true)
804         {
805                 $this->cache = (bool) $enable;
806         }
808         /**
809          * Set the length of time (in seconds) that the contents of a feed will be
810          * cached
811          *
812          * @param int $seconds The feed content cache duration
813          */
814         public function set_cache_duration($seconds = 3600)
815         {
816                 $this->cache_duration = (int) $seconds;
817         }
819         /**
820          * Set the length of time (in seconds) that the autodiscovered feed URL will
821          * be cached
822          *
823          * @param int $seconds The autodiscovered feed URL cache duration.
824          */
825         public function set_autodiscovery_cache_duration($seconds = 604800)
826         {
827                 $this->autodiscovery_cache_duration = (int) $seconds;
828         }
830         /**
831          * Set the file system location where the cached files should be stored
832          *
833          * @param string $location The file system location.
834          */
835         public function set_cache_location($location = './cache')
836         {
837                 $this->cache_location = (string) $location;
838         }
840         /**
841          * Set whether feed items should be sorted into reverse chronological order
842          *
843          * @param bool $enable Sort as reverse chronological order.
844          */
845         public function enable_order_by_date($enable = true)
846         {
847                 $this->order_by_date = (bool) $enable;
848         }
850         /**
851          * Set the character encoding used to parse the feed
852          *
853          * This overrides the encoding reported by the feed, however it will fall
854          * back to the normal encoding detection if the override fails
855          *
856          * @param string $encoding Character encoding
857          */
858         public function set_input_encoding($encoding = false)
859         {
860                 if ($encoding)
861                 {
862                         $this->input_encoding = (string) $encoding;
863                 }
864                 else
865                 {
866                         $this->input_encoding = false;
867                 }
868         }
870         /**
871          * Set how much feed autodiscovery to do
872          *
873          * @see SIMPLEPIE_LOCATOR_NONE
874          * @see SIMPLEPIE_LOCATOR_AUTODISCOVERY
875          * @see SIMPLEPIE_LOCATOR_LOCAL_EXTENSION
876          * @see SIMPLEPIE_LOCATOR_LOCAL_BODY
877          * @see SIMPLEPIE_LOCATOR_REMOTE_EXTENSION
878          * @see SIMPLEPIE_LOCATOR_REMOTE_BODY
879          * @see SIMPLEPIE_LOCATOR_ALL
880          * @param int $level Feed Autodiscovery Level (level can be a combination of the above constants, see bitwise OR operator)
881          */
882         public function set_autodiscovery_level($level = SIMPLEPIE_LOCATOR_ALL)
883         {
884                 $this->autodiscovery = (int) $level;
885         }
887         /**
888          * Get the class registry
889          *
890          * Use this to override SimplePie's default classes
891          * @see SimplePie_Registry
892          * @return SimplePie_Registry
893          */
894         public function &get_registry()
895         {
896                 return $this->registry;
897         }
899         /**#@+
900          * Useful when you are overloading or extending SimplePie's default classes.
901          *
902          * @deprecated Use {@see get_registry()} instead
903          * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
904          * @param string $class Name of custom class
905          * @return boolean True on success, false otherwise
906          */
907         /**
908          * Set which class SimplePie uses for caching
909          */
910         public function set_cache_class($class = 'SimplePie_Cache')
911         {
912                 return $this->registry->register('Cache', $class, true);
913         }
915         /**
916          * Set which class SimplePie uses for auto-discovery
917          */
918         public function set_locator_class($class = 'SimplePie_Locator')
919         {
920                 return $this->registry->register('Locator', $class, true);
921         }
923         /**
924          * Set which class SimplePie uses for XML parsing
925          */
926         public function set_parser_class($class = 'SimplePie_Parser')
927         {
928                 return $this->registry->register('Parser', $class, true);
929         }
931         /**
932          * Set which class SimplePie uses for remote file fetching
933          */
934         public function set_file_class($class = 'SimplePie_File')
935         {
936                 return $this->registry->register('File', $class, true);
937         }
939         /**
940          * Set which class SimplePie uses for data sanitization
941          */
942         public function set_sanitize_class($class = 'SimplePie_Sanitize')
943         {
944                 return $this->registry->register('Sanitize', $class, true);
945         }
947         /**
948          * Set which class SimplePie uses for handling feed items
949          */
950         public function set_item_class($class = 'SimplePie_Item')
951         {
952                 return $this->registry->register('Item', $class, true);
953         }
955         /**
956          * Set which class SimplePie uses for handling author data
957          */
958         public function set_author_class($class = 'SimplePie_Author')
959         {
960                 return $this->registry->register('Author', $class, true);
961         }
963         /**
964          * Set which class SimplePie uses for handling category data
965          */
966         public function set_category_class($class = 'SimplePie_Category')
967         {
968                 return $this->registry->register('Category', $class, true);
969         }
971         /**
972          * Set which class SimplePie uses for feed enclosures
973          */
974         public function set_enclosure_class($class = 'SimplePie_Enclosure')
975         {
976                 return $this->registry->register('Enclosure', $class, true);
977         }
979         /**
980          * Set which class SimplePie uses for `<media:text>` captions
981          */
982         public function set_caption_class($class = 'SimplePie_Caption')
983         {
984                 return $this->registry->register('Caption', $class, true);
985         }
987         /**
988          * Set which class SimplePie uses for `<media:copyright>`
989          */
990         public function set_copyright_class($class = 'SimplePie_Copyright')
991         {
992                 return $this->registry->register('Copyright', $class, true);
993         }
995         /**
996          * Set which class SimplePie uses for `<media:credit>`
997          */
998         public function set_credit_class($class = 'SimplePie_Credit')
999         {
1000                 return $this->registry->register('Credit', $class, true);
1001         }
1003         /**
1004          * Set which class SimplePie uses for `<media:rating>`
1005          */
1006         public function set_rating_class($class = 'SimplePie_Rating')
1007         {
1008                 return $this->registry->register('Rating', $class, true);
1009         }
1011         /**
1012          * Set which class SimplePie uses for `<media:restriction>`
1013          */
1014         public function set_restriction_class($class = 'SimplePie_Restriction')
1015         {
1016                 return $this->registry->register('Restriction', $class, true);
1017         }
1019         /**
1020          * Set which class SimplePie uses for content-type sniffing
1021          */
1022         public function set_content_type_sniffer_class($class = 'SimplePie_Content_Type_Sniffer')
1023         {
1024                 return $this->registry->register('Content_Type_Sniffer', $class, true);
1025         }
1027         /**
1028          * Set which class SimplePie uses item sources
1029          */
1030         public function set_source_class($class = 'SimplePie_Source')
1031         {
1032                 return $this->registry->register('Source', $class, true);
1033         }
1034         /**#@-*/
1036         /**
1037          * Set the user agent string
1038          *
1039          * @param string $ua New user agent string.
1040          */
1041         public function set_useragent($ua = SIMPLEPIE_USERAGENT)
1042         {
1043                 $this->useragent = (string) $ua;
1044         }
1046         /**
1047          * Set callback function to create cache filename with
1048          *
1049          * @param mixed $function Callback function
1050          */
1051         public function set_cache_name_function($function = 'md5')
1052         {
1053                 if (is_callable($function))
1054                 {
1055                         $this->cache_name_function = $function;
1056                 }
1057         }
1059         /**
1060          * Set options to make SP as fast as possible
1061          *
1062          * Forgoes a substantial amount of data sanitization in favor of speed. This
1063          * turns SimplePie into a dumb parser of feeds.
1064          *
1065          * @param bool $set Whether to set them or not
1066          */
1067         public function set_stupidly_fast($set = false)
1068         {
1069                 if ($set)
1070                 {
1071                         $this->enable_order_by_date(false);
1072                         $this->remove_div(false);
1073                         $this->strip_comments(false);
1074                         $this->strip_htmltags(false);
1075                         $this->strip_attributes(false);
1076                         $this->set_image_handler(false);
1077                 }
1078         }
1080         /**
1081          * Set maximum number of feeds to check with autodiscovery
1082          *
1083          * @param int $max Maximum number of feeds to check
1084          */
1085         public function set_max_checked_feeds($max = 10)
1086         {
1087                 $this->max_checked_feeds = (int) $max;
1088         }
1090         public function remove_div($enable = true)
1091         {
1092                 $this->sanitize->remove_div($enable);
1093         }
1095         public function strip_htmltags($tags = '', $encode = null)
1096         {
1097                 if ($tags === '')
1098                 {
1099                         $tags = $this->strip_htmltags;
1100                 }
1101                 $this->sanitize->strip_htmltags($tags);
1102                 if ($encode !== null)
1103                 {
1104                         $this->sanitize->encode_instead_of_strip($tags);
1105                 }
1106         }
1108         public function encode_instead_of_strip($enable = true)
1109         {
1110                 $this->sanitize->encode_instead_of_strip($enable);
1111         }
1113         public function strip_attributes($attribs = '')
1114         {
1115                 if ($attribs === '')
1116                 {
1117                         $attribs = $this->strip_attributes;
1118                 }
1119                 $this->sanitize->strip_attributes($attribs);
1120         }
1122         /**
1123          * Set the output encoding
1124          *
1125          * Allows you to override SimplePie's output to match that of your webpage.
1126          * This is useful for times when your webpages are not being served as
1127          * UTF-8.  This setting will be obeyed by {@see handle_content_type()}, and
1128          * is similar to {@see set_input_encoding()}.
1129          *
1130          * It should be noted, however, that not all character encodings can support
1131          * all characters.  If your page is being served as ISO-8859-1 and you try
1132          * to display a Japanese feed, you'll likely see garbled characters.
1133          * Because of this, it is highly recommended to ensure that your webpages
1134          * are served as UTF-8.
1135          *
1136          * The number of supported character encodings depends on whether your web
1137          * host supports {@link http://php.net/mbstring mbstring},
1138          * {@link http://php.net/iconv iconv}, or both. See
1139          * {@link http://simplepie.org/wiki/faq/Supported_Character_Encodings} for
1140          * more information.
1141          *
1142          * @param string $encoding
1143          */
1144         public function set_output_encoding($encoding = 'UTF-8')
1145         {
1146                 $this->sanitize->set_output_encoding($encoding);
1147         }
1149         public function strip_comments($strip = false)
1150         {
1151                 $this->sanitize->strip_comments($strip);
1152         }
1154         /**
1155          * Set element/attribute key/value pairs of HTML attributes
1156          * containing URLs that need to be resolved relative to the feed
1157          *
1158          * Defaults to |a|@href, |area|@href, |blockquote|@cite, |del|@cite,
1159          * |form|@action, |img|@longdesc, |img|@src, |input|@src, |ins|@cite,
1160          * |q|@cite
1161          *
1162          * @since 1.0
1163          * @param array|null $element_attribute Element/attribute key/value pairs, null for default
1164          */
1165         public function set_url_replacements($element_attribute = null)
1166         {
1167                 $this->sanitize->set_url_replacements($element_attribute);
1168         }
1170         /**
1171          * Set the handler to enable the display of cached images.
1172          *
1173          * @param str $page Web-accessible path to the handler_image.php file.
1174          * @param str $qs The query string that the value should be passed to.
1175          */
1176         public function set_image_handler($page = false, $qs = 'i')
1177         {
1178                 if ($page !== false)
1179                 {
1180                         $this->sanitize->set_image_handler($page . '?' . $qs . '=');
1181                 }
1182                 else
1183                 {
1184                         $this->image_handler = '';
1185                 }
1186         }
1188         /**
1189          * Set the limit for items returned per-feed with multifeeds
1190          *
1191          * @param integer $limit The maximum number of items to return.
1192          */
1193         public function set_item_limit($limit = 0)
1194         {
1195                 $this->item_limit = (int) $limit;
1196         }
1198         /**
1199          * Initialize the feed object
1200          *
1201          * This is what makes everything happen.  Period.  This is where all of the
1202          * configuration options get processed, feeds are fetched, cached, and
1203          * parsed, and all of that other good stuff.
1204          *
1205          * @return boolean True if successful, false otherwise
1206          */
1207         public function init()
1208         {
1209                 // Check absolute bare minimum requirements.
1210                 if (!extension_loaded('xml') || !extension_loaded('pcre'))
1211                 {
1212                         return false;
1213                 }
1214                 // 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.
1215                 elseif (!extension_loaded('xmlreader'))
1216                 {
1217                         static $xml_is_sane = null;
1218                         if ($xml_is_sane === null)
1219                         {
1220                                 $parser_check = xml_parser_create();
1221                                 xml_parse_into_struct($parser_check, '<foo>&amp;</foo>', $values);
1222                                 xml_parser_free($parser_check);
1223                                 $xml_is_sane = isset($values[0]['value']);
1224                         }
1225                         if (!$xml_is_sane)
1226                         {
1227                                 return false;
1228                         }
1229                 }
1231                 if (method_exists($this->sanitize, 'set_registry'))
1232                 {
1233                         $this->sanitize->set_registry($this->registry);
1234                 }
1236                 // Pass whatever was set with config options over to the sanitizer.
1237                 // Pass the classes in for legacy support; new classes should use the registry instead
1238                 $this->sanitize->pass_cache_data($this->cache, $this->cache_location, $this->cache_name_function, $this->registry->get_class('Cache'));
1239                 $this->sanitize->pass_file_data($this->registry->get_class('File'), $this->timeout, $this->useragent, $this->force_fsockopen);
1241                 if (!empty($this->multifeed_url))
1242                 {
1243                         $i = 0;
1244                         $success = 0;
1245                         $this->multifeed_objects = array();
1246                         $this->error = array();
1247                         foreach ($this->multifeed_url as $url)
1248                         {
1249                                 $this->multifeed_objects[$i] = clone $this;
1250                                 $this->multifeed_objects[$i]->set_feed_url($url);
1251                                 $single_success = $this->multifeed_objects[$i]->init();
1252                                 $success |= $single_success;
1253                                 if (!$single_success)
1254                                 {
1255                                         $this->error[$i] = $this->multifeed_objects[$i]->error();
1256                                 }
1257                                 $i++;
1258                         }
1259                         return (bool) $success;
1260                 }
1261                 elseif ($this->feed_url === null && $this->raw_data === null)
1262                 {
1263                         return false;
1264                 }
1266                 $this->error = null;
1267                 $this->data = array();
1268                 $this->multifeed_objects = array();
1269                 $cache = false;
1271                 if ($this->feed_url !== null)
1272                 {
1273                         $parsed_feed_url = $this->registry->call('Misc', 'parse_url', array($this->feed_url));
1275                         // Decide whether to enable caching
1276                         if ($this->cache && $parsed_feed_url['scheme'] !== '')
1277                         {
1278                                 $cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $this->feed_url), 'spc'));
1279                         }
1281                         // Fetch the data via SimplePie_File into $this->raw_data
1282                         if (($fetched = $this->fetch_data($cache)) === true)
1283                         {
1284                                 return true;
1285                         }
1286                         elseif ($fetched === false) {
1287                                 return false;
1288                         }
1290                         list($headers, $sniffed) = $fetched;
1291                 }
1293                 // Set up array of possible encodings
1294                 $encodings = array();
1296                 // First check to see if input has been overridden.
1297                 if ($this->input_encoding !== false)
1298                 {
1299                         $encodings[] = $this->input_encoding;
1300                 }
1302                 $application_types = array('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity');
1303                 $text_types = array('text/xml', 'text/xml-external-parsed-entity');
1305                 // RFC 3023 (only applies to sniffed content)
1306                 if (isset($sniffed))
1307                 {
1308                         if (in_array($sniffed, $application_types) || substr($sniffed, 0, 12) === 'application/' && substr($sniffed, -4) === '+xml')
1309                         {
1310                                 if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset))
1311                                 {
1312                                         $encodings[] = strtoupper($charset[1]);
1313                                 }
1314                                 $encodings = array_merge($encodings, $this->registry->call('Misc', 'xml_encoding', array($this->raw_data, &$this->registry)));
1315                                 $encodings[] = 'UTF-8';
1316                         }
1317                         elseif (in_array($sniffed, $text_types) || substr($sniffed, 0, 5) === 'text/' && substr($sniffed, -4) === '+xml')
1318                         {
1319                                 if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset))
1320                                 {
1321                                         $encodings[] = $charset[1];
1322                                 }
1323                                 $encodings[] = 'US-ASCII';
1324                         }
1325                         // Text MIME-type default
1326                         elseif (substr($sniffed, 0, 5) === 'text/')
1327                         {
1328                                 $encodings[] = 'US-ASCII';
1329                         }
1330                 }
1332                 // Fallback to XML 1.0 Appendix F.1/UTF-8/ISO-8859-1
1333                 $encodings = array_merge($encodings, $this->registry->call('Misc', 'xml_encoding', array($this->raw_data, &$this->registry)));
1334                 $encodings[] = 'UTF-8';
1335                 $encodings[] = 'ISO-8859-1';
1337                 // There's no point in trying an encoding twice
1338                 $encodings = array_unique($encodings);
1340                 // Loop through each possible encoding, till we return something, or run out of possibilities
1341                 foreach ($encodings as $encoding)
1342                 {
1343                         // Change the encoding to UTF-8 (as we always use UTF-8 internally)
1344                         if ($utf8_data = $this->registry->call('Misc', 'change_encoding', array($this->raw_data, $encoding, 'UTF-8')))
1345                         {
1346                                 // Create new parser
1347                                 $parser = $this->registry->create('Parser');
1349                                 // If it's parsed fine
1350                                 if ($parser->parse($utf8_data, 'UTF-8'))
1351                                 {
1352                                         $this->data = $parser->get_data();
1353                                         if (!($this->get_type() & ~SIMPLEPIE_TYPE_NONE))
1354                                         {
1355                                                 $this->error = "A feed could not be found at $this->feed_url. This does not appear to be a valid RSS or Atom feed.";
1356                                                 $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__));
1357                                                 return false;
1358                                         }
1360                                         if (isset($headers))
1361                                         {
1362                                                 $this->data['headers'] = $headers;
1363                                         }
1364                                         $this->data['build'] = SIMPLEPIE_BUILD;
1366                                         // Cache the file if caching is enabled
1367                                         if ($cache && !$cache->save($this))
1368                                         {
1369                                                 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);
1370                                         }
1371                                         return true;
1372                                 }
1373                         }
1374                 }
1376                 if (isset($parser))
1377                 {
1378                         // We have an error, just set SimplePie_Misc::error to it and quit
1379                         $this->error = sprintf('This XML document is invalid, 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());
1380                 }
1381                 else
1382                 {
1383                         $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.';
1384                 }
1386                 $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__));
1388                 return false;
1389         }
1391         /**
1392          * Fetch the data via SimplePie_File
1393          *
1394          * If the data is already cached, attempt to fetch it from there instead
1395          * @param SimplePie_Cache|false $cache Cache handler, or false to not load from the cache
1396          * @return array|true Returns true if the data was loaded from the cache, or an array of HTTP headers and sniffed type
1397          */
1398         protected function fetch_data(&$cache)
1399         {
1400                 // If it's enabled, use the cache
1401                 if ($cache)
1402                 {
1403                         // Load the Cache
1404                         $this->data = $cache->load();
1405                         if (!empty($this->data))
1406                         {
1407                                 // If the cache is for an outdated build of SimplePie
1408                                 if (!isset($this->data['build']) || $this->data['build'] !== SIMPLEPIE_BUILD)
1409                                 {
1410                                         $cache->unlink();
1411                                         $this->data = array();
1412                                 }
1413                                 // If we've hit a collision just rerun it with caching disabled
1414                                 elseif (isset($this->data['url']) && $this->data['url'] !== $this->feed_url)
1415                                 {
1416                                         $cache = false;
1417                                         $this->data = array();
1418                                 }
1419                                 // If we've got a non feed_url stored (if the page isn't actually a feed, or is a redirect) use that URL.
1420                                 elseif (isset($this->data['feed_url']))
1421                                 {
1422                                         // If the autodiscovery cache is still valid use it.
1423                                         if ($cache->mtime() + $this->autodiscovery_cache_duration > time())
1424                                         {
1425                                                 // Do not need to do feed autodiscovery yet.
1426                                                 if ($this->data['feed_url'] !== $this->data['url'])
1427                                                 {
1428                                                         $this->set_feed_url($this->data['feed_url']);
1429                                                         return $this->init();
1430                                                 }
1432                                                 $cache->unlink();
1433                                                 $this->data = array();
1434                                         }
1435                                 }
1436                                 // Check if the cache has been updated
1437                                 elseif ($cache->mtime() + $this->cache_duration < time())
1438                                 {
1439                                         // If we have last-modified and/or etag set
1440                                         if (isset($this->data['headers']['last-modified']) || isset($this->data['headers']['etag']))
1441                                         {
1442                                                 $headers = array(
1443                                                         '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',
1444                                                 );
1445                                                 if (isset($this->data['headers']['last-modified']))
1446                                                 {
1447                                                         $headers['if-modified-since'] = $this->data['headers']['last-modified'];
1448                                                 }
1449                                                 if (isset($this->data['headers']['etag']))
1450                                                 {
1451                                                         $headers['if-none-match'] = $this->data['headers']['etag'];
1452                                                 }
1454                                                 $file = $this->registry->create('File', array($this->feed_url, $this->timeout/10, 5, $headers, $this->useragent, $this->force_fsockopen));
1456                                                 if ($file->success)
1457                                                 {
1458                                                         if ($file->status_code === 304)
1459                                                         {
1460                                                                 $cache->touch();
1461                                                                 return true;
1462                                                         }
1463                                                 }
1464                                                 else
1465                                                 {
1466                                                         unset($file);
1467                                                 }
1468                                         }
1469                                 }
1470                                 // If the cache is still valid, just return true
1471                                 else
1472                                 {
1473                                         $this->raw_data = false;
1474                                         return true;
1475                                 }
1476                         }
1477                         // If the cache is empty, delete it
1478                         else
1479                         {
1480                                 $cache->unlink();
1481                                 $this->data = array();
1482                         }
1483                 }
1484                 // 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.
1485                 if (!isset($file))
1486                 {
1487                         if ($this->file instanceof SimplePie_File && $this->file->url === $this->feed_url)
1488                         {
1489                                 $file =& $this->file;
1490                         }
1491                         else
1492                         {
1493                                 $headers = array(
1494                                         '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',
1495                                 );
1496                                 $file = $this->registry->create('File', array($this->feed_url, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen));
1497                         }
1498                 }
1499                 // If the file connection has an error, set SimplePie::error to that and quit
1500                 if (!$file->success && !($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300)))
1501                 {
1502                         $this->error = $file->error;
1503                         return !empty($this->data);
1504                 }
1506                 if (!$this->force_feed)
1507                 {
1508                         // Check if the supplied URL is a feed, if it isn't, look for it.
1509                         $locate = $this->registry->create('Locator', array(&$file, $this->timeout, $this->useragent, $this->max_checked_feeds));
1511                         if (!$locate->is_feed($file))
1512                         {
1513                                 // We need to unset this so that if SimplePie::set_file() has been called that object is untouched
1514                                 unset($file);
1515                                 try
1516                                 {
1517                                         if (!($file = $locate->find($this->autodiscovery, $this->all_discovered_feeds)))
1518                                         {
1519                                                 $this->error = "A feed could not be found at $this->feed_url. A feed with an invalid mime type may fall victim to this error, or " . SIMPLEPIE_NAME . " was unable to auto-discover it.. Use force_feed() if you are certain this URL is a real feed.";
1520                                                 $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__));
1521                                                 return false;
1522                                         }
1523                                 }
1524                                 catch (SimplePie_Exception $e)
1525                                 {
1526                                         // This is usually because DOMDocument doesn't exist
1527                                         $this->error = $e->getMessage();
1528                                         $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, $e->getFile(), $e->getLine()));
1529                                         return false;
1530                                 }
1531                                 if ($cache)
1532                                 {
1533                                         $this->data = array('url' => $this->feed_url, 'feed_url' => $file->url, 'build' => SIMPLEPIE_BUILD);
1534                                         if (!$cache->save($this))
1535                                         {
1536                                                 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);
1537                                         }
1538                                         $cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $file->url), 'spc'));
1539                                 }
1540                                 $this->feed_url = $file->url;
1541                         }
1542                         $locate = null;
1543                 }
1545                 $this->raw_data = $file->body;
1547                 $headers = $file->headers;
1548                 $sniffer = $this->registry->create('Content_Type_Sniffer', array(&$file));
1549                 $sniffed = $sniffer->get_type();
1551                 return array($headers, $sniffed);
1552         }
1554         /**
1555          * Get the error message for the occured error
1556          *
1557          * @return string|array Error message, or array of messages for multifeeds
1558          */
1559         public function error()
1560         {
1561                 return $this->error;
1562         }
1564         /**
1565          * Get the raw XML
1566          *
1567          * This is the same as the old `$feed->enable_xml_dump(true)`, but returns
1568          * the data instead of printing it.
1569          *
1570          * @return string|boolean Raw XML data, false if the cache is used
1571          */
1572         public function get_raw_data()
1573         {
1574                 return $this->raw_data;
1575         }
1577         /**
1578          * Get the character encoding used for output
1579          *
1580          * @since Preview Release
1581          * @return string
1582          */
1583         public function get_encoding()
1584         {
1585                 return $this->sanitize->output_encoding;
1586         }
1588         /**
1589          * Send the content-type header with correct encoding
1590          *
1591          * This method ensures that the SimplePie-enabled page is being served with
1592          * the correct {@link http://www.iana.org/assignments/media-types/ mime-type}
1593          * and character encoding HTTP headers (character encoding determined by the
1594          * {@see set_output_encoding} config option).
1595          *
1596          * This won't work properly if any content or whitespace has already been
1597          * sent to the browser, because it relies on PHP's
1598          * {@link http://php.net/header header()} function, and these are the
1599          * circumstances under which the function works.
1600          *
1601          * Because it's setting these settings for the entire page (as is the nature
1602          * of HTTP headers), this should only be used once per page (again, at the
1603          * top).
1604          *
1605          * @param string $mime MIME type to serve the page as
1606          */
1607         public function handle_content_type($mime = 'text/html')
1608         {
1609                 if (!headers_sent())
1610                 {
1611                         $header = "Content-type: $mime;";
1612                         if ($this->get_encoding())
1613                         {
1614                                 $header .= ' charset=' . $this->get_encoding();
1615                         }
1616                         else
1617                         {
1618                                 $header .= ' charset=UTF-8';
1619                         }
1620                         header($header);
1621                 }
1622         }
1624         /**
1625          * Get the type of the feed
1626          *
1627          * This returns a SIMPLEPIE_TYPE_* constant, which can be tested against
1628          * using {@link http://php.net/language.operators.bitwise bitwise operators}
1629          *
1630          * @since 0.8 (usage changed to using constants in 1.0)
1631          * @see SIMPLEPIE_TYPE_NONE Unknown.
1632          * @see SIMPLEPIE_TYPE_RSS_090 RSS 0.90.
1633          * @see SIMPLEPIE_TYPE_RSS_091_NETSCAPE RSS 0.91 (Netscape).
1634          * @see SIMPLEPIE_TYPE_RSS_091_USERLAND RSS 0.91 (Userland).
1635          * @see SIMPLEPIE_TYPE_RSS_091 RSS 0.91.
1636          * @see SIMPLEPIE_TYPE_RSS_092 RSS 0.92.
1637          * @see SIMPLEPIE_TYPE_RSS_093 RSS 0.93.
1638          * @see SIMPLEPIE_TYPE_RSS_094 RSS 0.94.
1639          * @see SIMPLEPIE_TYPE_RSS_10 RSS 1.0.
1640          * @see SIMPLEPIE_TYPE_RSS_20 RSS 2.0.x.
1641          * @see SIMPLEPIE_TYPE_RSS_RDF RDF-based RSS.
1642          * @see SIMPLEPIE_TYPE_RSS_SYNDICATION Non-RDF-based RSS (truly intended as syndication format).
1643          * @see SIMPLEPIE_TYPE_RSS_ALL Any version of RSS.
1644          * @see SIMPLEPIE_TYPE_ATOM_03 Atom 0.3.
1645          * @see SIMPLEPIE_TYPE_ATOM_10 Atom 1.0.
1646          * @see SIMPLEPIE_TYPE_ATOM_ALL Any version of Atom.
1647          * @see SIMPLEPIE_TYPE_ALL Any known/supported feed type.
1648          * @return int SIMPLEPIE_TYPE_* constant
1649          */
1650         public function get_type()
1651         {
1652                 if (!isset($this->data['type']))
1653                 {
1654                         $this->data['type'] = SIMPLEPIE_TYPE_ALL;
1655                         if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed']))
1656                         {
1657                                 $this->data['type'] &= SIMPLEPIE_TYPE_ATOM_10;
1658                         }
1659                         elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed']))
1660                         {
1661                                 $this->data['type'] &= SIMPLEPIE_TYPE_ATOM_03;
1662                         }
1663                         elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF']))
1664                         {
1665                                 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['channel'])
1666                                 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['image'])
1667                                 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item'])
1668                                 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['textinput']))
1669                                 {
1670                                         $this->data['type'] &= SIMPLEPIE_TYPE_RSS_10;
1671                                 }
1672                                 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['channel'])
1673                                 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['image'])
1674                                 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item'])
1675                                 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['textinput']))
1676                                 {
1677                                         $this->data['type'] &= SIMPLEPIE_TYPE_RSS_090;
1678                                 }
1679                         }
1680                         elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss']))
1681                         {
1682                                 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_ALL;
1683                                 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version']))
1684                                 {
1685                                         switch (trim($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version']))
1686                                         {
1687                                                 case '0.91':
1688                                                         $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091;
1689                                                         if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['skiphours']['hour'][0]['data']))
1690                                                         {
1691                                                                 switch (trim($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['skiphours']['hour'][0]['data']))
1692                                                                 {
1693                                                                         case '0':
1694                                                                                 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_NETSCAPE;
1695                                                                                 break;
1697                                                                         case '24':
1698                                                                                 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_USERLAND;
1699                                                                                 break;
1700                                                                 }
1701                                                         }
1702                                                         break;
1704                                                 case '0.92':
1705                                                         $this->data['type'] &= SIMPLEPIE_TYPE_RSS_092;
1706                                                         break;
1708                                                 case '0.93':
1709                                                         $this->data['type'] &= SIMPLEPIE_TYPE_RSS_093;
1710                                                         break;
1712                                                 case '0.94':
1713                                                         $this->data['type'] &= SIMPLEPIE_TYPE_RSS_094;
1714                                                         break;
1716                                                 case '2.0':
1717                                                         $this->data['type'] &= SIMPLEPIE_TYPE_RSS_20;
1718                                                         break;
1719                                         }
1720                                 }
1721                         }
1722                         else
1723                         {
1724                                 $this->data['type'] = SIMPLEPIE_TYPE_NONE;
1725                         }
1726                 }
1727                 return $this->data['type'];
1728         }
1730         /**
1731          * Get the URL for the feed
1732          *
1733          * May or may not be different from the URL passed to {@see set_feed_url()},
1734          * depending on whether auto-discovery was used.
1735          *
1736          * @since Preview Release (previously called `get_feed_url()` since SimplePie 0.8.)
1737          * @todo If we have a perm redirect we should return the new URL
1738          * @todo When we make the above change, let's support <itunes:new-feed-url> as well
1739          * @todo Also, |atom:link|@rel=self
1740          * @return string|null
1741          */
1742         public function subscribe_url()
1743         {
1744                 if ($this->feed_url !== null)
1745                 {
1746                         return $this->sanitize($this->feed_url, SIMPLEPIE_CONSTRUCT_IRI);
1747                 }
1748                 else
1749                 {
1750                         return null;
1751                 }
1752         }
1754         /**
1755          * Get data for an feed-level element
1756          *
1757          * This method allows you to get access to ANY element/attribute that is a
1758          * sub-element of the opening feed tag.
1759          *
1760          * The return value is an indexed array of elements matching the given
1761          * namespace and tag name. Each element has `attribs`, `data` and `child`
1762          * subkeys. For `attribs` and `child`, these contain namespace subkeys.
1763          * `attribs` then has one level of associative name => value data (where
1764          * `value` is a string) after the namespace. `child` has tag-indexed keys
1765          * after the namespace, each member of which is an indexed array matching
1766          * this same format.
1767          *
1768          * For example:
1769          * <pre>
1770          * // This is probably a bad example because we already support
1771          * // <media:content> natively, but it shows you how to parse through
1772          * // the nodes.
1773          * $group = $item->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'group');
1774          * $content = $group[0]['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'];
1775          * $file = $content[0]['attribs']['']['url'];
1776          * echo $file;
1777          * </pre>
1778          *
1779          * @since 1.0
1780          * @see http://simplepie.org/wiki/faq/supported_xml_namespaces
1781          * @param string $namespace The URL of the XML namespace of the elements you're trying to access
1782          * @param string $tag Tag name
1783          * @return array
1784          */
1785         public function get_feed_tags($namespace, $tag)
1786         {
1787                 $type = $this->get_type();
1788                 if ($type & SIMPLEPIE_TYPE_ATOM_10)
1789                 {
1790                         if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag]))
1791                         {
1792                                 return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag];
1793                         }
1794                 }
1795                 if ($type & SIMPLEPIE_TYPE_ATOM_03)
1796                 {
1797                         if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag]))
1798                         {
1799                                 return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag];
1800                         }
1801                 }
1802                 if ($type & SIMPLEPIE_TYPE_RSS_RDF)
1803                 {
1804                         if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag]))
1805                         {
1806                                 return $this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag];
1807                         }
1808                 }
1809                 if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION)
1810                 {
1811                         if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag]))
1812                         {
1813                                 return $this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag];
1814                         }
1815                 }
1816                 return null;
1817         }
1819         /**
1820          * Get data for an channel-level element
1821          *
1822          * This method allows you to get access to ANY element/attribute in the
1823          * channel/header section of the feed.
1824          *
1825          * See {@see SimplePie::get_feed_tags()} for a description of the return value
1826          *
1827          * @since 1.0
1828          * @see http://simplepie.org/wiki/faq/supported_xml_namespaces
1829          * @param string $namespace The URL of the XML namespace of the elements you're trying to access
1830          * @param string $tag Tag name
1831          * @return array
1832          */
1833         public function get_channel_tags($namespace, $tag)
1834         {
1835                 $type = $this->get_type();
1836                 if ($type & SIMPLEPIE_TYPE_ATOM_ALL)
1837                 {
1838                         if ($return = $this->get_feed_tags($namespace, $tag))
1839                         {
1840                                 return $return;
1841                         }
1842                 }
1843                 if ($type & SIMPLEPIE_TYPE_RSS_10)
1844                 {
1845                         if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'channel'))
1846                         {
1847                                 if (isset($channel[0]['child'][$namespace][$tag]))
1848                                 {
1849                                         return $channel[0]['child'][$namespace][$tag];
1850                                 }
1851                         }
1852                 }
1853                 if ($type & SIMPLEPIE_TYPE_RSS_090)
1854                 {
1855                         if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'channel'))
1856                         {
1857                                 if (isset($channel[0]['child'][$namespace][$tag]))
1858                                 {
1859                                         return $channel[0]['child'][$namespace][$tag];
1860                                 }
1861                         }
1862                 }
1863                 if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION)
1864                 {
1865                         if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'channel'))
1866                         {
1867                                 if (isset($channel[0]['child'][$namespace][$tag]))
1868                                 {
1869                                         return $channel[0]['child'][$namespace][$tag];
1870                                 }
1871                         }
1872                 }
1873                 return null;
1874         }
1876         /**
1877          * Get data for an channel-level element
1878          *
1879          * This method allows you to get access to ANY element/attribute in the
1880          * image/logo section of the feed.
1881          *
1882          * See {@see SimplePie::get_feed_tags()} for a description of the return value
1883          *
1884          * @since 1.0
1885          * @see http://simplepie.org/wiki/faq/supported_xml_namespaces
1886          * @param string $namespace The URL of the XML namespace of the elements you're trying to access
1887          * @param string $tag Tag name
1888          * @return array
1889          */
1890         public function get_image_tags($namespace, $tag)
1891         {
1892                 $type = $this->get_type();
1893                 if ($type & SIMPLEPIE_TYPE_RSS_10)
1894                 {
1895                         if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'image'))
1896                         {
1897                                 if (isset($image[0]['child'][$namespace][$tag]))
1898                                 {
1899                                         return $image[0]['child'][$namespace][$tag];
1900                                 }
1901                         }
1902                 }
1903                 if ($type & SIMPLEPIE_TYPE_RSS_090)
1904                 {
1905                         if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'image'))
1906                         {
1907                                 if (isset($image[0]['child'][$namespace][$tag]))
1908                                 {
1909                                         return $image[0]['child'][$namespace][$tag];
1910                                 }
1911                         }
1912                 }
1913                 if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION)
1914                 {
1915                         if ($image = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'image'))
1916                         {
1917                                 if (isset($image[0]['child'][$namespace][$tag]))
1918                                 {
1919                                         return $image[0]['child'][$namespace][$tag];
1920                                 }
1921                         }
1922                 }
1923                 return null;
1924         }
1926         /**
1927          * Get the base URL value from the feed
1928          *
1929          * Uses `<xml:base>` if available, otherwise uses the first link in the
1930          * feed, or failing that, the URL of the feed itself.
1931          *
1932          * @see get_link
1933          * @see subscribe_url
1934          *
1935          * @param array $element
1936          * @return string
1937          */
1938         public function get_base($element = array())
1939         {
1940                 if (!($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION) && !empty($element['xml_base_explicit']) && isset($element['xml_base']))
1941                 {
1942                         return $element['xml_base'];
1943                 }
1944                 elseif ($this->get_link() !== null)
1945                 {
1946                         return $this->get_link();
1947                 }
1948                 else
1949                 {
1950                         return $this->subscribe_url();
1951                 }
1952         }
1954         /**
1955          * Sanitize feed data
1956          *
1957          * @access private
1958          * @see SimplePie_Sanitize::sanitize()
1959          * @param string $data Data to sanitize
1960          * @param int $type One of the SIMPLEPIE_CONSTRUCT_* constants
1961          * @param string $base Base URL to resolve URLs against
1962          * @return string Sanitized data
1963          */
1964         public function sanitize($data, $type, $base = '')
1965         {
1966                 return $this->sanitize->sanitize($data, $type, $base);
1967         }
1969         /**
1970          * Get the title of the feed
1971          *
1972          * Uses `<atom:title>`, `<title>` or `<dc:title>`
1973          *
1974          * @since 1.0 (previously called `get_feed_title` since 0.8)
1975          * @return string|null
1976          */
1977         public function get_title()
1978         {
1979                 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title'))
1980                 {
1981                         return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
1982                 }
1983                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title'))
1984                 {
1985                         return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
1986                 }
1987                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
1988                 {
1989                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
1990                 }
1991                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
1992                 {
1993                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
1994                 }
1995                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
1996                 {
1997                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
1998                 }
1999                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
2000                 {
2001                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2002                 }
2003                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
2004                 {
2005                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2006                 }
2007                 else
2008                 {
2009                         return null;
2010                 }
2011         }
2013         /**
2014          * Get a category for the feed
2015          *
2016          * @since Unknown
2017          * @param int $key The category that you want to return.  Remember that arrays begin with 0, not 1
2018          * @return SimplePie_Category|null
2019          */
2020         public function get_category($key = 0)
2021         {
2022                 $categories = $this->get_categories();
2023                 if (isset($categories[$key]))
2024                 {
2025                         return $categories[$key];
2026                 }
2027                 else
2028                 {
2029                         return null;
2030                 }
2031         }
2033         /**
2034          * Get all categories for the feed
2035          *
2036          * Uses `<atom:category>`, `<category>` or `<dc:subject>`
2037          *
2038          * @since Unknown
2039          * @return array|null List of {@see SimplePie_Category} objects
2040          */
2041         public function get_categories()
2042         {
2043                 $categories = array();
2045                 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category)
2046                 {
2047                         $term = null;
2048                         $scheme = null;
2049                         $label = null;
2050                         if (isset($category['attribs']['']['term']))
2051                         {
2052                                 $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT);
2053                         }
2054                         if (isset($category['attribs']['']['scheme']))
2055                         {
2056                                 $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
2057                         }
2058                         if (isset($category['attribs']['']['label']))
2059                         {
2060                                 $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
2061                         }
2062                         $categories[] = $this->registry->create('Category', array($term, $scheme, $label));
2063                 }
2064                 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category)
2065                 {
2066                         // This is really the label, but keep this as the term also for BC.
2067                         // Label will also work on retrieving because that falls back to term.
2068                         $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2069                         if (isset($category['attribs']['']['domain']))
2070                         {
2071                                 $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT);
2072                         }
2073                         else
2074                         {
2075                                 $scheme = null;
2076                         }
2077                         $categories[] = $this->registry->create('Category', array($term, $scheme, null));
2078                 }
2079                 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category)
2080                 {
2081                         $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2082                 }
2083                 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category)
2084                 {
2085                         $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2086                 }
2088                 if (!empty($categories))
2089                 {
2090                         return array_unique($categories);
2091                 }
2092                 else
2093                 {
2094                         return null;
2095                 }
2096         }
2098         /**
2099          * Get an author for the feed
2100          *
2101          * @since 1.1
2102          * @param int $key The author that you want to return.  Remember that arrays begin with 0, not 1
2103          * @return SimplePie_Author|null
2104          */
2105         public function get_author($key = 0)
2106         {
2107                 $authors = $this->get_authors();
2108                 if (isset($authors[$key]))
2109                 {
2110                         return $authors[$key];
2111                 }
2112                 else
2113                 {
2114                         return null;
2115                 }
2116         }
2118         /**
2119          * Get all authors for the feed
2120          *
2121          * Uses `<atom:author>`, `<author>`, `<dc:creator>` or `<itunes:author>`
2122          *
2123          * @since 1.1
2124          * @return array|null List of {@see SimplePie_Author} objects
2125          */
2126         public function get_authors()
2127         {
2128                 $authors = array();
2129                 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author)
2130                 {
2131                         $name = null;
2132                         $uri = null;
2133                         $email = null;
2134                         if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
2135                         {
2136                                 $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2137                         }
2138                         if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
2139                         {
2140                                 $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]));
2141                         }
2142                         if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
2143                         {
2144                                 $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2145                         }
2146                         if ($name !== null || $email !== null || $uri !== null)
2147                         {
2148                                 $authors[] = $this->registry->create('Author', array($name, $uri, $email));
2149                         }
2150                 }
2151                 if ($author = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author'))
2152                 {
2153                         $name = null;
2154                         $url = null;
2155                         $email = null;
2156                         if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
2157                         {
2158                                 $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2159                         }
2160                         if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
2161                         {
2162                                 $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]));
2163                         }
2164                         if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
2165                         {
2166                                 $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2167                         }
2168                         if ($name !== null || $email !== null || $url !== null)
2169                         {
2170                                 $authors[] = $this->registry->create('Author', array($name, $url, $email));
2171                         }
2172                 }
2173                 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author)
2174                 {
2175                         $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2176                 }
2177                 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author)
2178                 {
2179                         $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2180                 }
2181                 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author)
2182                 {
2183                         $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
2184                 }
2186                 if (!empty($authors))
2187                 {
2188                         return array_unique($authors);
2189                 }
2190                 else
2191                 {
2192                         return null;
2193                 }
2194         }
2196         /**
2197          * Get a contributor for the feed
2198          *
2199          * @since 1.1
2200          * @param int $key The contrbutor that you want to return.  Remember that arrays begin with 0, not 1
2201          * @return SimplePie_Author|null
2202          */
2203         public function get_contributor($key = 0)
2204         {
2205                 $contributors = $this->get_contributors();
2206                 if (isset($contributors[$key]))
2207                 {
2208                         return $contributors[$key];
2209                 }
2210                 else
2211                 {
2212                         return null;
2213                 }
2214         }
2216         /**
2217          * Get all contributors for the feed
2218          *
2219          * Uses `<atom:contributor>`
2220          *
2221          * @since 1.1
2222          * @return array|null List of {@see SimplePie_Author} objects
2223          */
2224         public function get_contributors()
2225         {
2226                 $contributors = array();
2227                 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor)
2228                 {
2229                         $name = null;
2230                         $uri = null;
2231                         $email = null;
2232                         if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
2233                         {
2234                                 $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2235                         }
2236                         if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
2237                         {
2238                                 $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]));
2239                         }
2240                         if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
2241                         {
2242                                 $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2243                         }
2244                         if ($name !== null || $email !== null || $uri !== null)
2245                         {
2246                                 $contributors[] = $this->registry->create('Author', array($name, $uri, $email));
2247                         }
2248                 }
2249                 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor)
2250                 {
2251                         $name = null;
2252                         $url = null;
2253                         $email = null;
2254                         if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
2255                         {
2256                                 $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2257                         }
2258                         if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
2259                         {
2260                                 $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]));
2261                         }
2262                         if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
2263                         {
2264                                 $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2265                         }
2266                         if ($name !== null || $email !== null || $url !== null)
2267                         {
2268                                 $contributors[] = $this->registry->create('Author', array($name, $url, $email));
2269                         }
2270                 }
2272                 if (!empty($contributors))
2273                 {
2274                         return array_unique($contributors);
2275                 }
2276                 else
2277                 {
2278                         return null;
2279                 }
2280         }
2282         /**
2283          * Get a single link for the feed
2284          *
2285          * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8)
2286          * @param int $key The link that you want to return.  Remember that arrays begin with 0, not 1
2287          * @param string $rel The relationship of the link to return
2288          * @return string|null Link URL
2289          */
2290         public function get_link($key = 0, $rel = 'alternate')
2291         {
2292                 $links = $this->get_links($rel);
2293                 if (isset($links[$key]))
2294                 {
2295                         return $links[$key];
2296                 }
2297                 else
2298                 {
2299                         return null;
2300                 }
2301         }
2303         /**
2304          * Get the permalink for the item
2305          *
2306          * Returns the first link available with a relationship of "alternate".
2307          * Identical to {@see get_link()} with key 0
2308          *
2309          * @see get_link
2310          * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8)
2311          * @internal Added for parity between the parent-level and the item/entry-level.
2312          * @return string|null Link URL
2313          */
2314         public function get_permalink()
2315         {
2316                 return $this->get_link(0);
2317         }
2319         /**
2320          * Get all links for the feed
2321          *
2322          * Uses `<atom:link>` or `<link>`
2323          *
2324          * @since Beta 2
2325          * @param string $rel The relationship of links to return
2326          * @return array|null Links found for the feed (strings)
2327          */
2328         public function get_links($rel = 'alternate')
2329         {
2330                 if (!isset($this->data['links']))
2331                 {
2332                         $this->data['links'] = array();
2333                         if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link'))
2334                         {
2335                                 foreach ($links as $link)
2336                                 {
2337                                         if (isset($link['attribs']['']['href']))
2338                                         {
2339                                                 $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
2340                                                 $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
2341                                         }
2342                                 }
2343                         }
2344                         if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link'))
2345                         {
2346                                 foreach ($links as $link)
2347                                 {
2348                                         if (isset($link['attribs']['']['href']))
2349                                         {
2350                                                 $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
2351                                                 $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
2353                                         }
2354                                 }
2355                         }
2356                         if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
2357                         {
2358                                 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
2359                         }
2360                         if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
2361                         {
2362                                 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
2363                         }
2364                         if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
2365                         {
2366                                 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
2367                         }
2369                         $keys = array_keys($this->data['links']);
2370                         foreach ($keys as $key)
2371                         {
2372                                 if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key)))
2373                                 {
2374                                         if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]))
2375                                         {
2376                                                 $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]);
2377                                                 $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key];
2378                                         }
2379                                         else
2380                                         {
2381                                                 $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key];
2382                                         }
2383                                 }
2384                                 elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY)
2385                                 {
2386                                         $this->data['links'][substr($key, 41)] =& $this->data['links'][$key];
2387                                 }
2388                                 $this->data['links'][$key] = array_unique($this->data['links'][$key]);
2389                         }
2390                 }
2392                 if (isset($this->data['links'][$rel]))
2393                 {
2394                         return $this->data['links'][$rel];
2395                 }
2396                 else
2397                 {
2398                         return null;
2399                 }
2400         }
2402         public function get_all_discovered_feeds()
2403         {
2404                 return $this->all_discovered_feeds;
2405         }
2407         /**
2408          * Get the content for the item
2409          *
2410          * Uses `<atom:subtitle>`, `<atom:tagline>`, `<description>`,
2411          * `<dc:description>`, `<itunes:summary>` or `<itunes:subtitle>`
2412          *
2413          * @since 1.0 (previously called `get_feed_description()` since 0.8)
2414          * @return string|null
2415          */
2416         public function get_description()
2417         {
2418                 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle'))
2419                 {
2420                         return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
2421                 }
2422                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline'))
2423                 {
2424                         return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
2425                 }
2426                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description'))
2427                 {
2428                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
2429                 }
2430                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description'))
2431                 {
2432                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
2433                 }
2434                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description'))
2435                 {
2436                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
2437                 }
2438                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description'))
2439                 {
2440                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2441                 }
2442                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description'))
2443                 {
2444                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2445                 }
2446                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary'))
2447                 {
2448                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
2449                 }
2450                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle'))
2451                 {
2452                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
2453                 }
2454                 else
2455                 {
2456                         return null;
2457                 }
2458         }
2460         /**
2461          * Get the copyright info for the feed
2462          *
2463          * Uses `<atom:rights>`, `<atom:copyright>` or `<dc:rights>`
2464          *
2465          * @since 1.0 (previously called `get_feed_copyright()` since 0.8)
2466          * @return string|null
2467          */
2468         public function get_copyright()
2469         {
2470                 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights'))
2471                 {
2472                         return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
2473                 }
2474                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright'))
2475                 {
2476                         return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
2477                 }
2478                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'copyright'))
2479                 {
2480                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2481                 }
2482                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights'))
2483                 {
2484                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2485                 }
2486                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights'))
2487                 {
2488                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2489                 }
2490                 else
2491                 {
2492                         return null;
2493                 }
2494         }
2496         /**
2497          * Get the language for the feed
2498          *
2499          * Uses `<language>`, `<dc:language>`, or @xml_lang
2500          *
2501          * @since 1.0 (previously called `get_feed_language()` since 0.8)
2502          * @return string|null
2503          */
2504         public function get_language()
2505         {
2506                 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'language'))
2507                 {
2508                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2509                 }
2510                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language'))
2511                 {
2512                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2513                 }
2514                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language'))
2515                 {
2516                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2517                 }
2518                 elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang']))
2519                 {
2520                         return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
2521                 }
2522                 elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang']))
2523                 {
2524                         return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
2525                 }
2526                 elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang']))
2527                 {
2528                         return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
2529                 }
2530                 elseif (isset($this->data['headers']['content-language']))
2531                 {
2532                         return $this->sanitize($this->data['headers']['content-language'], SIMPLEPIE_CONSTRUCT_TEXT);
2533                 }
2534                 else
2535                 {
2536                         return null;
2537                 }
2538         }
2540         /**
2541          * Get the latitude coordinates for the item
2542          *
2543          * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
2544          *
2545          * Uses `<geo:lat>` or `<georss:point>`
2546          *
2547          * @since 1.0
2548          * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
2549          * @link http://www.georss.org/ GeoRSS
2550          * @return string|null
2551          */
2552         public function get_latitude()
2553         {
2555                 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat'))
2556                 {
2557                         return (float) $return[0]['data'];
2558                 }
2559                 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))
2560                 {
2561                         return (float) $match[1];
2562                 }
2563                 else
2564                 {
2565                         return null;
2566                 }
2567         }
2569         /**
2570          * Get the longitude coordinates for the feed
2571          *
2572          * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
2573          *
2574          * Uses `<geo:long>`, `<geo:lon>` or `<georss:point>`
2575          *
2576          * @since 1.0
2577          * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
2578          * @link http://www.georss.org/ GeoRSS
2579          * @return string|null
2580          */
2581         public function get_longitude()
2582         {
2583                 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long'))
2584                 {
2585                         return (float) $return[0]['data'];
2586                 }
2587                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon'))
2588                 {
2589                         return (float) $return[0]['data'];
2590                 }
2591                 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))
2592                 {
2593                         return (float) $match[2];
2594                 }
2595                 else
2596                 {
2597                         return null;
2598                 }
2599         }
2601         /**
2602          * Get the feed logo's title
2603          *
2604          * RSS 0.9.0, 1.0 and 2.0 feeds are allowed to have a "feed logo" title.
2605          *
2606          * Uses `<image><title>` or `<image><dc:title>`
2607          *
2608          * @return string|null
2609          */
2610         public function get_image_title()
2611         {
2612                 if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
2613                 {
2614                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2615                 }
2616                 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
2617                 {
2618                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2619                 }
2620                 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
2621                 {
2622                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2623                 }
2624                 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
2625                 {
2626                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2627                 }
2628                 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
2629                 {
2630                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2631                 }
2632                 else
2633                 {
2634                         return null;
2635                 }
2636         }
2638         /**
2639          * Get the feed logo's URL
2640          *
2641          * RSS 0.9.0, 2.0, Atom 1.0, and feeds with iTunes RSS tags are allowed to
2642          * have a "feed logo" URL. This points directly to the image itself.
2643          *
2644          * Uses `<itunes:image>`, `<atom:logo>`, `<atom:icon>`,
2645          * `<image><title>` or `<image><dc:title>`
2646          *
2647          * @return string|null
2648          */
2649         public function get_image_url()
2650         {
2651                 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image'))
2652                 {
2653                         return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI);
2654                 }
2655                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo'))
2656                 {
2657                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2658                 }
2659                 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon'))
2660                 {
2661                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2662                 }
2663                 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'url'))
2664                 {
2665                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2666                 }
2667                 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'url'))
2668                 {
2669                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2670                 }
2671                 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url'))
2672                 {
2673                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2674                 }
2675                 else
2676                 {
2677                         return null;
2678                 }
2679         }
2682         /**
2683          * Get the feed logo's link
2684          *
2685          * RSS 0.9.0, 1.0 and 2.0 feeds are allowed to have a "feed logo" link. This
2686          * points to a human-readable page that the image should link to.
2687          *
2688          * Uses `<itunes:image>`, `<atom:logo>`, `<atom:icon>`,
2689          * `<image><title>` or `<image><dc:title>`
2690          *
2691          * @return string|null
2692          */
2693         public function get_image_link()
2694         {
2695                 if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
2696                 {
2697                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2698                 }
2699                 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
2700                 {
2701                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2702                 }
2703                 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
2704                 {
2705                         return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2706                 }
2707                 else
2708                 {
2709                         return null;
2710                 }
2711         }
2713         /**
2714          * Get the feed logo's link
2715          *
2716          * RSS 2.0 feeds are allowed to have a "feed logo" width.
2717          *
2718          * Uses `<image><width>` or defaults to 88.0 if no width is specified and
2719          * the feed is an RSS 2.0 feed.
2720          *
2721          * @return int|float|null
2722          */
2723         public function get_image_width()
2724         {
2725                 if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'width'))
2726                 {
2727                         return round($return[0]['data']);
2728                 }
2729                 elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url'))
2730                 {
2731                         return 88.0;
2732                 }
2733                 else
2734                 {
2735                         return null;
2736                 }
2737         }
2739         /**
2740          * Get the feed logo's height
2741          *
2742          * RSS 2.0 feeds are allowed to have a "feed logo" height.
2743          *
2744          * Uses `<image><height>` or defaults to 31.0 if no height is specified and
2745          * the feed is an RSS 2.0 feed.
2746          *
2747          * @return int|float|null
2748          */
2749         public function get_image_height()
2750         {
2751                 if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'height'))
2752                 {
2753                         return round($return[0]['data']);
2754                 }
2755                 elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url'))
2756                 {
2757                         return 31.0;
2758                 }
2759                 else
2760                 {
2761                         return null;
2762                 }
2763         }
2765         /**
2766          * Get the number of items in the feed
2767          *
2768          * This is well-suited for {@link http://php.net/for for()} loops with
2769          * {@see get_item()}
2770          *
2771          * @param int $max Maximum value to return. 0 for no limit
2772          * @return int Number of items in the feed
2773          */
2774         public function get_item_quantity($max = 0)
2775         {
2776                 $max = (int) $max;
2777                 $qty = count($this->get_items());
2778                 if ($max === 0)
2779                 {
2780                         return $qty;
2781                 }
2782                 else
2783                 {
2784                         return ($qty > $max) ? $max : $qty;
2785                 }
2786         }
2788         /**
2789          * Get a single item from the feed
2790          *
2791          * This is better suited for {@link http://php.net/for for()} loops, whereas
2792          * {@see get_items()} is better suited for
2793          * {@link http://php.net/foreach foreach()} loops.
2794          *
2795          * @see get_item_quantity()
2796          * @since Beta 2
2797          * @param int $key The item that you want to return.  Remember that arrays begin with 0, not 1
2798          * @return SimplePie_Item|null
2799          */
2800         public function get_item($key = 0)
2801         {
2802                 $items = $this->get_items();
2803                 if (isset($items[$key]))
2804                 {
2805                         return $items[$key];
2806                 }
2807                 else
2808                 {
2809                         return null;
2810                 }
2811         }
2813         /**
2814          * Get all items from the feed
2815          *
2816          * This is better suited for {@link http://php.net/for for()} loops, whereas
2817          * {@see get_items()} is better suited for
2818          * {@link http://php.net/foreach foreach()} loops.
2819          *
2820          * @see get_item_quantity
2821          * @since Beta 2
2822          * @param int $start Index to start at
2823          * @param int $end Number of items to return. 0 for all items after `$start`
2824          * @return array|null List of {@see SimplePie_Item} objects
2825          */
2826         public function get_items($start = 0, $end = 0)
2827         {
2828                 if (!isset($this->data['items']))
2829                 {
2830                         if (!empty($this->multifeed_objects))
2831                         {
2832                                 $this->data['items'] = SimplePie::merge_items($this->multifeed_objects, $start, $end, $this->item_limit);
2833                         }
2834                         else
2835                         {
2836                                 $this->data['items'] = array();
2837                                 if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'entry'))
2838                                 {
2839                                         $keys = array_keys($items);
2840                                         foreach ($keys as $key)
2841                                         {
2842                                                 $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key]));
2843                                         }
2844                                 }
2845                                 if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'entry'))
2846                                 {
2847                                         $keys = array_keys($items);
2848                                         foreach ($keys as $key)
2849                                         {
2850                                                 $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key]));
2851                                         }
2852                                 }
2853                                 if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'item'))
2854                                 {
2855                                         $keys = array_keys($items);
2856                                         foreach ($keys as $key)
2857                                         {
2858                                                 $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key]));
2859                                         }
2860                                 }
2861                                 if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'item'))
2862                                 {
2863                                         $keys = array_keys($items);
2864                                         foreach ($keys as $key)
2865                                         {
2866                                                 $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key]));
2867                                         }
2868                                 }
2869                                 if ($items = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'item'))
2870                                 {
2871                                         $keys = array_keys($items);
2872                                         foreach ($keys as $key)
2873                                         {
2874                                                 $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key]));
2875                                         }
2876                                 }
2877                         }
2878                 }
2880                 if (!empty($this->data['items']))
2881                 {
2882                         // If we want to order it by date, check if all items have a date, and then sort it
2883                         if ($this->order_by_date && empty($this->multifeed_objects))
2884                         {
2885                                 if (!isset($this->data['ordered_items']))
2886                                 {
2887                                         $do_sort = true;
2888                                         foreach ($this->data['items'] as $item)
2889                                         {
2890                                                 if (!$item->get_date('U'))
2891                                                 {
2892                                                         $do_sort = false;
2893                                                         break;
2894                                                 }
2895                                         }
2896                                         $item = null;
2897                                         $this->data['ordered_items'] = $this->data['items'];
2898                                         if ($do_sort)
2899                                         {
2900                                                 usort($this->data['ordered_items'], array(get_class($this), 'sort_items'));
2901                                         }
2902                                 }
2903                                 $items = $this->data['ordered_items'];
2904                         }
2905                         else
2906                         {
2907                                 $items = $this->data['items'];
2908                         }
2910                         // Slice the data as desired
2911                         if ($end === 0)
2912                         {
2913                                 return array_slice($items, $start);
2914                         }
2915                         else
2916                         {
2917                                 return array_slice($items, $start, $end);
2918                         }
2919                 }
2920                 else
2921                 {
2922                         return array();
2923                 }
2924         }
2926         /**
2927          * Set the favicon handler
2928          *
2929          * @deprecated Use your own favicon handling instead
2930          */
2931         public function set_favicon_handler($page = false, $qs = 'i')
2932         {
2933                 $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING;
2934                 trigger_error('Favicon handling has been removed, please use your own handling', $level);
2935                 return false;
2936         }
2938         /**
2939          * Get the favicon for the current feed
2940          *
2941          * @deprecated Use your own favicon handling instead
2942          */
2943         public function get_favicon()
2944         {
2945                 $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING;
2946                 trigger_error('Favicon handling has been removed, please use your own handling', $level);
2948                 if (($url = $this->get_link()) !== null)
2949                 {
2950                         return 'http://g.etfv.co/' . urlencode($url);
2951                 }
2953                 return false;
2954         }
2956         /**
2957          * Magic method handler
2958          *
2959          * @param string $method Method name
2960          * @param array $args Arguments to the method
2961          * @return mixed
2962          */
2963         public function __call($method, $args)
2964         {
2965                 if (strpos($method, 'subscribe_') === 0)
2966                 {
2967                         $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING;
2968                         trigger_error('subscribe_*() has been deprecated, implement the callback yourself', $level);
2969                         return '';
2970                 }
2971                 if ($method === 'enable_xml_dump')
2972                 {
2973                         $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING;
2974                         trigger_error('enable_xml_dump() has been deprecated, use get_raw_data() instead', $level);
2975                         return false;
2976                 }
2978                 $class = get_class($this);
2979                 $trace = debug_backtrace();
2980                 $file = $trace[0]['file'];
2981                 $line = $trace[0]['line'];
2982                 trigger_error("Call to undefined method $class::$method() in $file on line $line", E_USER_ERROR);
2983         }
2985         /**
2986          * Sorting callback for items
2987          *
2988          * @access private
2989          * @param SimplePie $a
2990          * @param SimplePie $b
2991          * @return boolean
2992          */
2993         public static function sort_items($a, $b)
2994         {
2995                 return $a->get_date('U') <= $b->get_date('U');
2996         }
2998         /**
2999          * Merge items from several feeds into one
3000          *
3001          * If you're merging multiple feeds together, they need to all have dates
3002          * for the items or else SimplePie will refuse to sort them.
3003          *
3004          * @link http://simplepie.org/wiki/tutorial/sort_multiple_feeds_by_time_and_date#if_feeds_require_separate_per-feed_settings
3005          * @param array $urls List of SimplePie feed objects to merge
3006          * @param int $start Starting item
3007          * @param int $end Number of items to return
3008          * @param int $limit Maximum number of items per feed
3009          * @return array
3010          */
3011         public static function merge_items($urls, $start = 0, $end = 0, $limit = 0)
3012         {
3013                 if (is_array($urls) && sizeof($urls) > 0)
3014                 {
3015                         $items = array();
3016                         foreach ($urls as $arg)
3017                         {
3018                                 if ($arg instanceof SimplePie)
3019                                 {
3020                                         $items = array_merge($items, $arg->get_items(0, $limit));
3021                                 }
3022                                 else
3023                                 {
3024                                         trigger_error('Arguments must be SimplePie objects', E_USER_WARNING);
3025                                 }
3026                         }
3028                         $do_sort = true;
3029                         foreach ($items as $item)
3030                         {
3031                                 if (!$item->get_date('U'))
3032                                 {
3033                                         $do_sort = false;
3034                                         break;
3035                                 }
3036                         }
3037                         $item = null;
3038                         if ($do_sort)
3039                         {
3040                                 usort($items, array(get_class($urls[0]), 'sort_items'));
3041                         }
3043                         if ($end === 0)
3044                         {
3045                                 return array_slice($items, $start);
3046                         }
3047                         else
3048                         {
3049                                 return array_slice($items, $start, $end);
3050                         }
3051                 }
3052                 else
3053                 {
3054                         trigger_error('Cannot merge zero SimplePie objects', E_USER_WARNING);
3055                         return array();
3056                 }
3057         }