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