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