Make WordPress Core


Ignore:
Timestamp:
09/30/2024 10:48:16 PM (8 months ago)
Author:
SergeyBiryukov
Message:

External Libraries: Update the SimplePie library to version 1.8.0.

The most notable change in this update is that all code is now namespaced and uses PSR-4 classes, though there is a compatibility layer available for extenders using the older class names, so plugin or theme authors directly using SimplePie can decide for themselves when they want to change to using the namespaced names for SimplePie classes.

Note: This commit includes additional fixes for PHP 8.4 compatibility (PR 875, PR 888) from the one-dot-eight branch of SimplePie, which is expected to be released as SimplePie 1.8.1 soon.

References:

Follow-up to [47733], [49176], [52393], [52413].

Props jrf, peterwilsoncc, chaion07, cu121, markparnell, audrasjb, costdev, Presskopp, desrosj, faisal03, mukesh27, SergeyBiryukov.
See #55604.

Location:
trunk/src/wp-includes/SimplePie/src
Files:
1 added
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/SimplePie/src/SimplePie.php

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