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