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