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