Make WordPress Core

Changeset 59141


Ignore:
Timestamp:
09/30/2024 10:48:16 PM (7 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
Files:
55 added
19 edited
1 copied
28 moved

Legend:

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

    r59140 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie;
     46
    4447/**
    4548 * Manages all author-related data
    4649 *
    47  * Used by {@see SimplePie_Item::get_author()} and {@see SimplePie::get_authors()}
     50 * Used by {@see Item::get_author()} and {@see SimplePie::get_authors()}
    4851 *
    4952 * This class can be overloaded with {@see SimplePie::set_author_class()}
     
    5255 * @subpackage API
    5356 */
    54 class SimplePie_Author
     57class Author
    5558{
    56     /**
    57     * Author's name
    58     *
    59     * @var string
    60     * @see get_name()
    61     */
    62     var $name;
     59    /**
     60    * Author's name
     61    *
     62    * @var string
     63    * @see get_name()
     64    */
     65    public $name;
    6366
    64     /**
    65     * Author's link
    66     *
    67     * @var string
    68     * @see get_link()
    69     */
    70     var $link;
     67    /**
     68    * Author's link
     69    *
     70    * @var string
     71    * @see get_link()
     72    */
     73    public $link;
    7174
    72     /**
    73     * Author's email address
    74     *
    75     * @var string
    76     * @see get_email()
    77     */
    78     var $email;
     75    /**
     76    * Author's email address
     77    *
     78    * @var string
     79    * @see get_email()
     80    */
     81    public $email;
    7982
    80     /**
    81     * Constructor, used to input the data
    82     *
    83     * @param string $name
    84     * @param string $link
    85     * @param string $email
    86     */
    87     public function __construct($name = null, $link = null, $email = null)
    88     {
    89         $this->name = $name;
    90         $this->link = $link;
    91         $this->email = $email;
    92     }
     83    /**
     84    * Constructor, used to input the data
     85    *
     86    * @param string $name
     87    * @param string $link
     88    * @param string $email
     89    */
     90    public function __construct($name = null, $link = null, $email = null)
     91    {
     92        $this->name = $name;
     93        $this->link = $link;
     94        $this->email = $email;
     95    }
    9396
    94     /**
    95     * String-ified version
    96     *
    97     * @return string
    98     */
    99     public function __toString()
    100     {
    101         // There is no $this->data here
    102         return md5(serialize($this));
    103     }
     97    /**
     98    * String-ified version
     99    *
     100    * @return string
     101    */
     102    public function __toString()
     103    {
     104        // There is no $this->data here
     105        return md5(serialize($this));
     106    }
    104107
    105     /**
    106      * Author's name
    107      *
    108      * @return string|null
    109      */
    110     public function get_name()
    111     {
    112         if ($this->name !== null)
    113         {
    114             return $this->name;
    115         }
     108    /**
     109     * Author's name
     110     *
     111     * @return string|null
     112     */
     113    public function get_name()
     114    {
     115        if ($this->name !== null) {
     116            return $this->name;
     117        }
    116118
    117         return null;
    118     }
     119        return null;
     120    }
    119121
    120     /**
    121      * Author's link
    122      *
    123      * @return string|null
    124      */
    125     public function get_link()
    126     {
    127         if ($this->link !== null)
    128         {
    129             return $this->link;
    130         }
     122    /**
     123     * Author's link
     124     *
     125     * @return string|null
     126     */
     127    public function get_link()
     128    {
     129        if ($this->link !== null) {
     130            return $this->link;
     131        }
    131132
    132         return null;
    133     }
     133        return null;
     134    }
    134135
    135     /**
    136      * Author's email address
    137      *
    138      * @return string|null
    139      */
    140     public function get_email()
    141     {
    142         if ($this->email !== null)
    143         {
    144             return $this->email;
    145         }
     136    /**
     137     * Author's email address
     138     *
     139     * @return string|null
     140     */
     141    public function get_email()
     142    {
     143        if ($this->email !== null) {
     144            return $this->email;
     145        }
    146146
    147         return null;
    148     }
     147        return null;
     148    }
    149149}
     150
     151class_alias('SimplePie\Author', 'SimplePie_Author');
  • trunk/src/wp-includes/SimplePie/src/Cache.php

    r59140 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie;
     46
     47use SimplePie\Cache\Base;
     48
    4449/**
    4550 * Used to create cache objects
     
    5156 * @package SimplePie
    5257 * @subpackage Caching
     58 * @deprecated since SimplePie 1.8.0, use "SimplePie\SimplePie::set_cache()" instead
    5359 */
    54 class SimplePie_Cache
     60class Cache
    5561{
    56     /**
    57     * Cache handler classes
    58     *
    59     * These receive 3 parameters to their constructor, as documented in
    60     * {@see register()}
    61     * @var array
    62     */
    63     protected static $handlers = array(
    64         'mysql'     => 'SimplePie_Cache_MySQL',
    65         'memcache'  => 'SimplePie_Cache_Memcache',
    66         'memcached' => 'SimplePie_Cache_Memcached',
    67         'redis'     => 'SimplePie_Cache_Redis'
    68     );
     62    /**
     63    * Cache handler classes
     64    *
     65    * These receive 3 parameters to their constructor, as documented in
     66    * {@see register()}
     67    * @var array
     68    */
     69    protected static $handlers = [
     70        'mysql'     => 'SimplePie\Cache\MySQL',
     71        'memcache'  => 'SimplePie\Cache\Memcache',
     72        'memcached' => 'SimplePie\Cache\Memcached',
     73        'redis'     => 'SimplePie\Cache\Redis'
     74    ];
    6975
    70     /**
    71      * Don't call the constructor. Please.
    72      */
    73     private function __construct() { }
     76    /**
     77     * Don't call the constructor. Please.
     78     */
     79    private function __construct()
     80    {
     81    }
    7482
    75     /**
    76      * Create a new SimplePie_Cache object
    77      *
    78      * @param string $location URL location (scheme is used to determine handler)
    79      * @param string $filename Unique identifier for cache object
    80      * @param string $extension 'spi' or 'spc'
    81      * @return SimplePie_Cache_Base Type of object depends on scheme of `$location`
    82      */
    83     public static function get_handler($location, $filename, $extension)
    84     {
    85         $type = explode(':', $location, 2);
    86         $type = $type[0];
    87         if (!empty(self::$handlers[$type]))
    88         {
    89             $class = self::$handlers[$type];
    90             return new $class($location, $filename, $extension);
    91         }
     83    /**
     84     * Create a new SimplePie\Cache object
     85     *
     86     * @param string $location URL location (scheme is used to determine handler)
     87     * @param string $filename Unique identifier for cache object
     88     * @param Base::TYPE_FEED|Base::TYPE_IMAGE $extension 'spi' or 'spc'
     89     * @return Base Type of object depends on scheme of `$location`
     90     */
     91    public static function get_handler($location, $filename, $extension)
     92    {
     93        $type = explode(':', $location, 2);
     94        $type = $type[0];
     95        if (!empty(self::$handlers[$type])) {
     96            $class = self::$handlers[$type];
     97            return new $class($location, $filename, $extension);
     98        }
    9299
    93         return new SimplePie_Cache_File($location, $filename, $extension);
    94     }
     100        return new \SimplePie\Cache\File($location, $filename, $extension);
     101    }
    95102
    96     /**
    97      * Create a new SimplePie_Cache object
    98      *
    99      * @deprecated Use {@see get_handler} instead
    100      */
    101     public function create($location, $filename, $extension)
    102     {
    103         trigger_error('Cache::create() has been replaced with Cache::get_handler(). Switch to the registry system to use this.', E_USER_DEPRECATED);
    104         return self::get_handler($location, $filename, $extension);
    105     }
     103    /**
     104     * Create a new SimplePie\Cache object
     105     *
     106     * @deprecated since SimplePie 1.3.1, use {@see get_handler()} instead
     107     */
     108    public function create($location, $filename, $extension)
     109    {
     110        trigger_error('Cache::create() has been replaced with Cache::get_handler() since SimplePie 1.3.1, use the registry system instead.', \E_USER_DEPRECATED);
    106111
    107     /**
    108      * Register a handler
    109      *
    110      * @param string $type DSN type to register for
    111      * @param string $class Name of handler class. Must implement SimplePie_Cache_Base
    112      */
    113     public static function register($type, $class)
    114     {
    115         self::$handlers[$type] = $class;
    116     }
     112        return self::get_handler($location, $filename, $extension);
     113    }
    117114
    118     /**
    119      * Parse a URL into an array
    120      *
    121      * @param string $url
    122      * @return array
    123      */
    124     public static function parse_URL($url)
    125     {
    126         $params = parse_url($url);
    127         $params['extras'] = array();
    128         if (isset($params['query']))
    129         {
    130             parse_str($params['query'], $params['extras']);
    131         }
    132         return $params;
    133     }
     115    /**
     116     * Register a handler
     117     *
     118     * @param string $type DSN type to register for
     119     * @param class-string<Base> $class Name of handler class. Must implement Base
     120     */
     121    public static function register($type, $class)
     122    {
     123        self::$handlers[$type] = $class;
     124    }
     125
     126    /**
     127     * Parse a URL into an array
     128     *
     129     * @param string $url
     130     * @return array
     131     */
     132    public static function parse_URL($url)
     133    {
     134        $params = parse_url($url);
     135        $params['extras'] = [];
     136        if (isset($params['query'])) {
     137            parse_str($params['query'], $params['extras']);
     138        }
     139        return $params;
     140    }
    134141}
     142
     143class_alias('SimplePie\Cache', 'SimplePie_Cache');
  • trunk/src/wp-includes/SimplePie/src/Cache/Base.php

    r47733 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie\Cache;
     46
    4447/**
    4548 * Base for cache objects
    4649 *
    47  * Classes to be used with {@see SimplePie_Cache::register()} are expected
     50 * Classes to be used with {@see \SimplePie\Cache::register()} are expected
    4851 * to implement this interface.
    4952 *
    5053 * @package SimplePie
    5154 * @subpackage Caching
     55 * @deprecated since SimplePie 1.8.0, use "Psr\SimpleCache\CacheInterface" instead
    5256 */
    53 interface SimplePie_Cache_Base
     57interface Base
    5458{
    55     /**
    56     * Feed cache type
    57     *
    58     * @var string
    59     */
    60     const TYPE_FEED = 'spc';
     59    /**
     60    * Feed cache type
     61    *
     62    * @var string
     63    */
     64    public const TYPE_FEED = 'spc';
    6165
    62     /**
    63     * Image cache type
    64     *
    65     * @var string
    66     */
    67     const TYPE_IMAGE = 'spi';
     66    /**
     67    * Image cache type
     68    *
     69    * @var string
     70    */
     71    public const TYPE_IMAGE = 'spi';
    6872
    69     /**
    70     * Create a new cache object
    71     *
    72     * @param string $location Location string (from SimplePie::$cache_location)
    73     * @param string $name Unique ID for the cache
    74      * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
    75     */
    76     public function __construct($location, $name, $type);
     73    /**
     74    * Create a new cache object
     75    *
     76    * @param string $location Location string (from SimplePie::$cache_location)
     77    * @param string $name Unique ID for the cache
     78     * @param Base::TYPE_FEED|Base::TYPE_IMAGE $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
     79    */
     80    public function __construct($location, $name, $type);
    7781
    78     /**
    79     * Save data to the cache
    80     *
    81      * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
    82     * @return bool Successfulness
    83     */
    84     public function save($data);
     82    /**
     83    * Save data to the cache
     84    *
     85     * @param array|\SimplePie\SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
     86    * @return bool Successfulness
     87    */
     88    public function save($data);
    8589
    86     /**
    87     * Retrieve the data saved to the cache
    88     *
    89     * @return array Data for SimplePie::$data
    90     */
    91     public function load();
     90    /**
     91    * Retrieve the data saved to the cache
     92    *
     93    * @return array Data for SimplePie::$data
     94    */
     95    public function load();
    9296
    93     /**
    94     * Retrieve the last modified time for the cache
    95     *
    96     * @return int Timestamp
    97     */
    98     public function mtime();
     97    /**
     98    * Retrieve the last modified time for the cache
     99    *
     100    * @return int Timestamp
     101    */
     102    public function mtime();
    99103
    100     /**
    101     * Set the last modified time to the current time
    102     *
    103     * @return bool Success status
    104     */
    105     public function touch();
     104    /**
     105    * Set the last modified time to the current time
     106    *
     107    * @return bool Success status
     108    */
     109    public function touch();
    106110
    107     /**
    108     * Remove the cache
    109     *
    110     * @return bool Success status
    111     */
    112     public function unlink();
     111    /**
     112    * Remove the cache
     113    *
     114    * @return bool Success status
     115    */
     116    public function unlink();
    113117}
     118
     119class_alias('SimplePie\Cache\Base', 'SimplePie_Cache_Base');
  • trunk/src/wp-includes/SimplePie/src/Cache/DB.php

    r47733 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie\Cache;
     46
    4447/**
    4548 * Base class for database-based caches
     
    4750 * @package SimplePie
    4851 * @subpackage Caching
     52 * @deprecated since SimplePie 1.8.0, use implementation of "Psr\SimpleCache\CacheInterface" instead
    4953 */
    50 abstract class SimplePie_Cache_DB implements SimplePie_Cache_Base
     54abstract class DB implements Base
    5155{
    52     /**
    53     * Helper for database conversion
    54     *
    55     * Converts a given {@see SimplePie} object into data to be stored
    56     *
    57      * @param SimplePie $data
    58     * @return array First item is the serialized data for storage, second item is the unique ID for this item
    59     */
    60     protected static function prepare_simplepie_object_for_cache($data)
    61     {
    62         $items = $data->get_items();
    63         $items_by_id = array();
     56    /**
     57    * Helper for database conversion
     58    *
     59    * Converts a given {@see SimplePie} object into data to be stored
     60    *
     61     * @param \SimplePie\SimplePie $data
     62    * @return array First item is the serialized data for storage, second item is the unique ID for this item
     63    */
     64    protected static function prepare_simplepie_object_for_cache($data)
     65    {
     66        $items = $data->get_items();
     67        $items_by_id = [];
    6468
    65         if (!empty($items))
    66         {
    67             foreach ($items as $item)
    68             {
    69                 $items_by_id[$item->get_id()] = $item;
    70             }
     69        if (!empty($items)) {
     70            foreach ($items as $item) {
     71                $items_by_id[$item->get_id()] = $item;
     72            }
    7173
    72             if (count($items_by_id) !== count($items))
    73             {
    74                 $items_by_id = array();
    75                 foreach ($items as $item)
    76                 {
    77                     $items_by_id[$item->get_id(true)] = $item;
    78                 }
    79             }
     74            if (count($items_by_id) !== count($items)) {
     75                $items_by_id = [];
     76                foreach ($items as $item) {
     77                    $items_by_id[$item->get_id(true)] = $item;
     78                }
     79            }
    8080
    81             if (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]))
    82             {
    83                 $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0];
    84             }
    85             elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]))
    86             {
    87                 $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0];
    88             }
    89             elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]))
    90             {
    91                 $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0];
    92             }
    93             elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0]))
    94             {
    95                 $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0];
    96             }
    97             else
    98             {
    99                 $channel = null;
    100             }
     81            if (isset($data->data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['feed'][0])) {
     82                $channel = &$data->data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['feed'][0];
     83            } elseif (isset($data->data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['feed'][0])) {
     84                $channel = &$data->data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['feed'][0];
     85            } elseif (isset($data->data['child'][\SimplePie\SimplePie::NAMESPACE_RDF]['RDF'][0])) {
     86                $channel = &$data->data['child'][\SimplePie\SimplePie::NAMESPACE_RDF]['RDF'][0];
     87            } elseif (isset($data->data['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['rss'][0]['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['channel'][0])) {
     88                $channel = &$data->data['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['rss'][0]['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['channel'][0];
     89            } else {
     90                $channel = null;
     91            }
    10192
    102             if ($channel !== null)
    103             {
    104                 if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry']))
    105                 {
    106                     unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry']);
    107                 }
    108                 if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry']))
    109                 {
    110                     unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry']);
    111                 }
    112                 if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']))
    113                 {
    114                     unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']);
    115                 }
    116                 if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']))
    117                 {
    118                     unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']);
    119                 }
    120                 if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item']))
    121                 {
    122                     unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item']);
    123                 }
    124             }
    125             if (isset($data->data['items']))
    126             {
    127                 unset($data->data['items']);
    128             }
    129             if (isset($data->data['ordered_items']))
    130             {
    131                 unset($data->data['ordered_items']);
    132             }
    133         }
    134         return array(serialize($data->data), $items_by_id);
    135     }
     93            if ($channel !== null) {
     94                if (isset($channel['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['entry'])) {
     95                    unset($channel['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['entry']);
     96                }
     97                if (isset($channel['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['entry'])) {
     98                    unset($channel['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['entry']);
     99                }
     100                if (isset($channel['child'][\SimplePie\SimplePie::NAMESPACE_RSS_10]['item'])) {
     101                    unset($channel['child'][\SimplePie\SimplePie::NAMESPACE_RSS_10]['item']);
     102                }
     103                if (isset($channel['child'][\SimplePie\SimplePie::NAMESPACE_RSS_090]['item'])) {
     104                    unset($channel['child'][\SimplePie\SimplePie::NAMESPACE_RSS_090]['item']);
     105                }
     106                if (isset($channel['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['item'])) {
     107                    unset($channel['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['item']);
     108                }
     109            }
     110            if (isset($data->data['items'])) {
     111                unset($data->data['items']);
     112            }
     113            if (isset($data->data['ordered_items'])) {
     114                unset($data->data['ordered_items']);
     115            }
     116        }
     117        return [serialize($data->data), $items_by_id];
     118    }
    136119}
     120
     121class_alias('SimplePie\Cache\DB', 'SimplePie_Cache_DB');
  • trunk/src/wp-includes/SimplePie/src/Cache/File.php

    r47733 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie\Cache;
     46
    4447/**
    4548 * Caches data to the filesystem
     
    4750 * @package SimplePie
    4851 * @subpackage Caching
     52 * @deprecated since SimplePie 1.8.0, use implementation of "Psr\SimpleCache\CacheInterface" instead
    4953 */
    50 class SimplePie_Cache_File implements SimplePie_Cache_Base
     54class File implements Base
    5155{
    52     /**
    53     * Location string
    54     *
    55     * @see SimplePie::$cache_location
    56     * @var string
    57     */
    58     protected $location;
     56    /**
     57    * Location string
     58    *
     59    * @see SimplePie::$cache_location
     60    * @var string
     61    */
     62    protected $location;
    5963
    60     /**
    61     * Filename
    62     *
    63     * @var string
    64     */
    65     protected $filename;
     64    /**
     65    * Filename
     66    *
     67    * @var string
     68    */
     69    protected $filename;
    6670
    67     /**
    68     * File extension
    69     *
    70     * @var string
    71     */
    72     protected $extension;
     71    /**
     72    * File extension
     73    *
     74    * @var string
     75    */
     76    protected $extension;
    7377
    74     /**
    75     * File path
    76     *
    77     * @var string
    78     */
    79     protected $name;
     78    /**
     79    * File path
     80    *
     81    * @var string
     82    */
     83    protected $name;
    8084
    81     /**
    82     * Create a new cache object
    83     *
    84     * @param string $location Location string (from SimplePie::$cache_location)
    85     * @param string $name Unique ID for the cache
    86      * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
    87     */
    88     public function __construct($location, $name, $type)
    89     {
    90         $this->location = $location;
    91         $this->filename = $name;
    92         $this->extension = $type;
    93         $this->name = "$this->location/$this->filename.$this->extension";
    94     }
     85    /**
     86    * Create a new cache object
     87    *
     88    * @param string $location Location string (from SimplePie::$cache_location)
     89    * @param string $name Unique ID for the cache
     90     * @param Base::TYPE_FEED|Base::TYPE_IMAGE $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
     91    */
     92    public function __construct($location, $name, $type)
     93    {
     94        $this->location = $location;
     95        $this->filename = $name;
     96        $this->extension = $type;
     97        $this->name = "$this->location/$this->filename.$this->extension";
     98    }
    9599
    96     /**
    97      * Save data to the cache
    98      *
    99      * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
    100      * @return bool Successfulness
    101      */
    102     public function save($data)
    103     {
    104         if (file_exists($this->name) && is_writable($this->name) || file_exists($this->location) && is_writable($this->location))
    105         {
    106             if ($data instanceof SimplePie)
    107             {
    108                 $data = $data->data;
    109             }
     100    /**
     101     * Save data to the cache
     102     *
     103     * @param array|\SimplePie\SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
     104     * @return bool Successfulness
     105     */
     106    public function save($data)
     107    {
     108        if (file_exists($this->name) && is_writable($this->name) || file_exists($this->location) && is_writable($this->location)) {
     109            if ($data instanceof \SimplePie\SimplePie) {
     110                $data = $data->data;
     111            }
    110112
    111             $data = serialize($data);
    112             return (bool) file_put_contents($this->name, $data);
    113         }
    114         return false;
    115     }
     113            $data = serialize($data);
     114            return (bool) file_put_contents($this->name, $data);
     115        }
     116        return false;
     117    }
    116118
    117     /**
    118      * Retrieve the data saved to the cache
    119      *
    120      * @return array Data for SimplePie::$data
    121      */
    122     public function load()
    123     {
    124         if (file_exists($this->name) && is_readable($this->name))
    125         {
    126             return unserialize(file_get_contents($this->name));
    127         }
    128         return false;
    129     }
     119    /**
     120     * Retrieve the data saved to the cache
     121     *
     122     * @return array Data for SimplePie::$data
     123     */
     124    public function load()
     125    {
     126        if (file_exists($this->name) && is_readable($this->name)) {
     127            return unserialize(file_get_contents($this->name));
     128        }
     129        return false;
     130    }
    130131
    131     /**
    132     * Retrieve the last modified time for the cache
    133     *
    134     * @return int Timestamp
    135     */
    136     public function mtime()
    137     {
    138         return @filemtime($this->name);
    139     }
     132    /**
     133    * Retrieve the last modified time for the cache
     134    *
     135    * @return int Timestamp
     136    */
     137    public function mtime()
     138    {
     139        return @filemtime($this->name);
     140    }
    140141
    141     /**
    142     * Set the last modified time to the current time
    143     *
    144     * @return bool Success status
    145     */
    146     public function touch()
    147     {
    148         return @touch($this->name);
    149     }
     142    /**
     143    * Set the last modified time to the current time
     144    *
     145    * @return bool Success status
     146    */
     147    public function touch()
     148    {
     149        return @touch($this->name);
     150    }
    150151
    151     /**
    152      * Remove the cache
    153      *
    154      * @return bool Success status
    155      */
    156     public function unlink()
    157     {
    158         if (file_exists($this->name))
    159         {
    160             return unlink($this->name);
    161         }
    162         return false;
    163     }
     152    /**
     153     * Remove the cache
     154     *
     155     * @return bool Success status
     156     */
     157    public function unlink()
     158    {
     159        if (file_exists($this->name)) {
     160            return unlink($this->name);
     161        }
     162        return false;
     163    }
    164164}
     165
     166class_alias('SimplePie\Cache\File', 'SimplePie_Cache_File');
  • trunk/src/wp-includes/SimplePie/src/Cache/Memcache.php

    r47733 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie\Cache;
     46
     47use Memcache as NativeMemcache;
     48
    4449/**
    4550 * Caches data to memcache
     
    5459 * @subpackage Caching
    5560 * @uses Memcache
     61 * @deprecated since SimplePie 1.8.0, use implementation of "Psr\SimpleCache\CacheInterface" instead
    5662 */
    57 class SimplePie_Cache_Memcache implements SimplePie_Cache_Base
     63class Memcache implements Base
    5864{
    59     /**
    60     * Memcache instance
    61     *
    62     * @var Memcache
    63     */
    64     protected $cache;
     65    /**
     66    * Memcache instance
     67    *
     68    * @var Memcache
     69    */
     70    protected $cache;
    6571
    66     /**
    67     * Options
    68     *
    69     * @var array
    70     */
    71     protected $options;
     72    /**
     73    * Options
     74    *
     75    * @var array
     76    */
     77    protected $options;
    7278
    73     /**
    74     * Cache name
    75     *
    76     * @var string
    77     */
    78     protected $name;
     79    /**
     80    * Cache name
     81    *
     82    * @var string
     83    */
     84    protected $name;
    7985
    80     /**
    81     * Create a new cache object
    82     *
    83     * @param string $location Location string (from SimplePie::$cache_location)
    84     * @param string $name Unique ID for the cache
    85      * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
    86     */
    87     public function __construct($location, $name, $type)
    88     {
    89         $this->options = array(
    90             'host' => '127.0.0.1',
    91             'port' => 11211,
    92             'extras' => array(
    93                 'timeout' => 3600, // one hour
    94                 'prefix' => 'simplepie_',
    95             ),
    96         );
    97         $this->options = SimplePie_Misc::array_merge_recursive($this->options, SimplePie_Cache::parse_URL($location));
     86    /**
     87    * Create a new cache object
     88    *
     89    * @param string $location Location string (from SimplePie::$cache_location)
     90    * @param string $name Unique ID for the cache
     91     * @param Base::TYPE_FEED|Base::TYPE_IMAGE $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
     92    */
     93    public function __construct($location, $name, $type)
     94    {
     95        $this->options = [
     96            'host' => '127.0.0.1',
     97            'port' => 11211,
     98            'extras' => [
     99                'timeout' => 3600, // one hour
     100                'prefix' => 'simplepie_',
     101            ],
     102        ];
     103        $this->options = array_replace_recursive($this->options, \SimplePie\Cache::parse_URL($location));
    98104
    99         $this->name = $this->options['extras']['prefix'] . md5("$name:$type");
     105        $this->name = $this->options['extras']['prefix'] . md5("$name:$type");
    100106
    101         $this->cache = new Memcache();
    102         $this->cache->addServer($this->options['host'], (int) $this->options['port']);
    103     }
     107        $this->cache = new NativeMemcache();
     108        $this->cache->addServer($this->options['host'], (int) $this->options['port']);
     109    }
    104110
    105     /**
    106      * Save data to the cache
    107      *
    108      * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
    109      * @return bool Successfulness
    110      */
    111     public function save($data)
    112     {
    113         if ($data instanceof SimplePie)
    114         {
    115             $data = $data->data;
    116         }
    117         return $this->cache->set($this->name, serialize($data), MEMCACHE_COMPRESSED, (int) $this->options['extras']['timeout']);
    118     }
     111    /**
     112     * Save data to the cache
     113     *
     114     * @param array|\SimplePie\SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
     115     * @return bool Successfulness
     116     */
     117    public function save($data)
     118    {
     119        if ($data instanceof \SimplePie\SimplePie) {
     120            $data = $data->data;
     121        }
     122        return $this->cache->set($this->name, serialize($data), MEMCACHE_COMPRESSED, (int) $this->options['extras']['timeout']);
     123    }
    119124
    120     /**
    121     * Retrieve the data saved to the cache
    122     *
    123     * @return array Data for SimplePie::$data
    124     */
    125     public function load()
    126     {
    127         $data = $this->cache->get($this->name);
     125    /**
     126    * Retrieve the data saved to the cache
     127    *
     128    * @return array Data for SimplePie::$data
     129    */
     130    public function load()
     131    {
     132        $data = $this->cache->get($this->name);
    128133
    129         if ($data !== false)
    130         {
    131             return unserialize($data);
    132         }
    133         return false;
    134     }
     134        if ($data !== false) {
     135            return unserialize($data);
     136        }
     137        return false;
     138    }
    135139
    136     /**
    137     * Retrieve the last modified time for the cache
    138     *
    139     * @return int Timestamp
    140     */
    141     public function mtime()
    142     {
    143         $data = $this->cache->get($this->name);
     140    /**
     141    * Retrieve the last modified time for the cache
     142    *
     143    * @return int Timestamp
     144    */
     145    public function mtime()
     146    {
     147        $data = $this->cache->get($this->name);
    144148
    145         if ($data !== false)
    146         {
    147             // essentially ignore the mtime because Memcache expires on its own
    148             return time();
    149         }
     149        if ($data !== false) {
     150            // essentially ignore the mtime because Memcache expires on its own
     151            return time();
     152        }
    150153
    151         return false;
    152     }
     154        return false;
     155    }
    153156
    154     /**
    155     * Set the last modified time to the current time
    156     *
    157     * @return bool Success status
    158     */
    159     public function touch()
    160     {
    161         $data = $this->cache->get($this->name);
     157    /**
     158    * Set the last modified time to the current time
     159    *
     160    * @return bool Success status
     161    */
     162    public function touch()
     163    {
     164        $data = $this->cache->get($this->name);
    162165
    163         if ($data !== false)
    164         {
    165             return $this->cache->set($this->name, $data, MEMCACHE_COMPRESSED, (int) $this->options['extras']['timeout']);
    166         }
     166        if ($data !== false) {
     167            return $this->cache->set($this->name, $data, MEMCACHE_COMPRESSED, (int) $this->options['extras']['timeout']);
     168        }
    167169
    168         return false;
    169     }
     170        return false;
     171    }
    170172
    171     /**
    172     * Remove the cache
    173     *
    174     * @return bool Success status
    175     */
    176     public function unlink()
    177     {
    178         return $this->cache->delete($this->name, 0);
    179     }
     173    /**
     174    * Remove the cache
     175    *
     176    * @return bool Success status
     177    */
     178    public function unlink()
     179    {
     180        return $this->cache->delete($this->name, 0);
     181    }
    180182}
     183
     184class_alias('SimplePie\Cache\Memcache', 'SimplePie_Cache_Memcache');
  • trunk/src/wp-includes/SimplePie/src/Cache/Memcached.php

    r47733 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie\Cache;
     46
     47use Memcached as NativeMemcached;
     48
    4449/**
    4550 * Caches data to memcached
     
    5560 * @author     Paul L. McNeely
    5661 * @uses       Memcached
     62 * @deprecated since SimplePie 1.8.0, use implementation of "Psr\SimpleCache\CacheInterface" instead
    5763 */
    58 class SimplePie_Cache_Memcached implements SimplePie_Cache_Base
     64class Memcached implements Base
    5965{
    6066    /**
    61      * Memcached instance
    62      * @var Memcached
     67     * NativeMemcached instance
     68     * @var NativeMemcached
    6369     */
    6470    protected $cache;
     
    7985     * Create a new cache object
    8086     * @param string $location Location string (from SimplePie::$cache_location)
    81      * @param string $name     Unique ID for the cache
    82      * @param string $type    Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
     87     * @param string $name Unique ID for the cache
     88     * @param Base::TYPE_FEED|Base::TYPE_IMAGE $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
    8389     */
    84     public function __construct($location, $name, $type) {
    85         $this->options = array(
     90    public function __construct($location, $name, $type)
     91    {
     92        $this->options = [
    8693            'host'   => '127.0.0.1',
    8794            'port'   => 11211,
    88             'extras' => array(
     95            'extras' => [
    8996                'timeout' => 3600, // one hour
    9097                'prefix'  => 'simplepie_',
    91             ),
    92         );
    93         $this->options = SimplePie_Misc::array_merge_recursive($this->options, SimplePie_Cache::parse_URL($location));
     98            ],
     99        ];
     100        $this->options = array_replace_recursive($this->options, \SimplePie\Cache::parse_URL($location));
    94101
    95102        $this->name = $this->options['extras']['prefix'] . md5("$name:$type");
    96103
    97         $this->cache = new Memcached();
     104        $this->cache = new NativeMemcached();
    98105        $this->cache->addServer($this->options['host'], (int)$this->options['port']);
    99106    }
     
    101108    /**
    102109     * Save data to the cache
    103      * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
     110     * @param array|\SimplePie\SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
    104111     * @return bool Successfulness
    105112     */
    106     public function save($data) {
    107         if ($data instanceof SimplePie) {
     113    public function save($data)
     114    {
     115        if ($data instanceof \SimplePie\SimplePie) {
    108116            $data = $data->data;
    109117        }
     
    116124     * @return array Data for SimplePie::$data
    117125     */
    118     public function load() {
     126    public function load()
     127    {
    119128        $data = $this->cache->get($this->name);
    120129
     
    129138     * @return int Timestamp
    130139     */
    131     public function mtime() {
     140    public function mtime()
     141    {
    132142        $data = $this->cache->get($this->name . '_mtime');
    133143        return (int) $data;
     
    138148     * @return bool Success status
    139149     */
    140     public function touch() {
     150    public function touch()
     151    {
    141152        $data = $this->cache->get($this->name);
    142153        return $this->setData($data);
     
    147158     * @return bool Success status
    148159     */
    149     public function unlink() {
     160    public function unlink()
     161    {
    150162        return $this->cache->delete($this->name, 0);
    151163    }
    152164
    153165    /**
    154      * Set the last modified time and data to Memcached
     166     * Set the last modified time and data to NativeMemcached
    155167     * @return bool Success status
    156168     */
    157     private function setData($data) {
    158 
     169    private function setData($data)
     170    {
    159171        if ($data !== false) {
    160172            $this->cache->set($this->name . '_mtime', time(), (int)$this->options['extras']['timeout']);
     
    165177    }
    166178}
     179
     180class_alias('SimplePie\Cache\Memcached', 'SimplePie_Cache_Memcached');
  • trunk/src/wp-includes/SimplePie/src/Cache/MySQL.php

    r47733 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie\Cache;
     46
    4447/**
    4548 * Caches data to a MySQL database
     
    5356 * @package SimplePie
    5457 * @subpackage Caching
     58 * @deprecated since SimplePie 1.8.0, use implementation of "Psr\SimpleCache\CacheInterface" instead
    5559 */
    56 class SimplePie_Cache_MySQL extends SimplePie_Cache_DB
     60class MySQL extends DB
    5761{
    58     /**
    59      * PDO instance
    60      *
    61      * @var PDO
    62      */
    63     protected $mysql;
    64 
    65     /**
    66      * Options
    67      *
    68      * @var array
    69      */
    70     protected $options;
    71 
    72     /**
    73      * Cache ID
    74      *
    75      * @var string
    76      */
    77     protected $id;
    78 
    79     /**
    80      * Create a new cache object
    81      *
    82      * @param string $location Location string (from SimplePie::$cache_location)
    83      * @param string $name Unique ID for the cache
    84      * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
    85      */
    86     public function __construct($location, $name, $type)
    87     {
    88         $this->options = array(
    89             'user' => null,
    90             'pass' => null,
    91             'host' => '127.0.0.1',
    92             'port' => '3306',
    93             'path' => '',
    94             'extras' => array(
    95                 'prefix' => '',
    96                 'cache_purge_time' => 2592000
    97             ),
    98         );
    99 
    100         $this->options = SimplePie_Misc::array_merge_recursive($this->options, SimplePie_Cache::parse_URL($location));
    101 
    102         // Path is prefixed with a "/"
    103         $this->options['dbname'] = substr($this->options['path'], 1);
    104 
    105         try
    106         {
    107             $this->mysql = new PDO("mysql:dbname={$this->options['dbname']};host={$this->options['host']};port={$this->options['port']}", $this->options['user'], $this->options['pass'], array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));
    108         }
    109         catch (PDOException $e)
    110         {
    111             $this->mysql = null;
    112             return;
    113         }
    114 
    115         $this->id = $name . $type;
    116 
    117         if (!$query = $this->mysql->query('SHOW TABLES'))
    118         {
    119             $this->mysql = null;
    120             return;
    121         }
    122 
    123         $db = array();
    124         while ($row = $query->fetchColumn())
    125         {
    126             $db[] = $row;
    127         }
    128 
    129         if (!in_array($this->options['extras']['prefix'] . 'cache_data', $db))
    130         {
    131             $query = $this->mysql->exec('CREATE TABLE `' . $this->options['extras']['prefix'] . 'cache_data` (`id` TEXT CHARACTER SET utf8 NOT NULL, `items` SMALLINT NOT NULL DEFAULT 0, `data` BLOB NOT NULL, `mtime` INT UNSIGNED NOT NULL, UNIQUE (`id`(125)))');
    132             if ($query === false)
    133             {
    134                 trigger_error("Can't create " . $this->options['extras']['prefix'] . "cache_data table, check permissions", E_USER_WARNING);
    135                 $this->mysql = null;
    136                 return;
    137             }
    138         }
    139 
    140         if (!in_array($this->options['extras']['prefix'] . 'items', $db))
    141         {
    142             $query = $this->mysql->exec('CREATE TABLE `' . $this->options['extras']['prefix'] . 'items` (`feed_id` TEXT CHARACTER SET utf8 NOT NULL, `id` TEXT CHARACTER SET utf8 NOT NULL, `data` MEDIUMBLOB NOT NULL, `posted` INT UNSIGNED NOT NULL, INDEX `feed_id` (`feed_id`(125)))');
    143             if ($query === false)
    144             {
    145                 trigger_error("Can't create " . $this->options['extras']['prefix'] . "items table, check permissions", E_USER_WARNING);
    146                 $this->mysql = null;
    147                 return;
    148             }
    149         }
    150     }
    151 
    152     /**
    153      * Save data to the cache
    154      *
    155      * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
    156      * @return bool Successfulness
    157      */
    158     public function save($data)
    159     {
    160         if ($this->mysql === null)
    161         {
    162             return false;
    163         }
    164 
    165         $query = $this->mysql->prepare('DELETE i, cd FROM `' . $this->options['extras']['prefix'] . 'cache_data` cd, ' .
    166             '`' . $this->options['extras']['prefix'] . 'items` i ' .
    167             'WHERE cd.id = i.feed_id ' .
    168             'AND cd.mtime < (unix_timestamp() - :purge_time)');
    169         $query->bindValue(':purge_time', $this->options['extras']['cache_purge_time']);
    170 
    171         if (!$query->execute())
    172         {
    173             return false;
    174         }
    175 
    176         if ($data instanceof SimplePie)
    177         {
    178             $data = clone $data;
    179 
    180             $prepared = self::prepare_simplepie_object_for_cache($data);
    181 
    182             $query = $this->mysql->prepare('SELECT COUNT(*) FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed');
    183             $query->bindValue(':feed', $this->id);
    184             if ($query->execute())
    185             {
    186                 if ($query->fetchColumn() > 0)
    187                 {
    188                     $items = count($prepared[1]);
    189                     if ($items)
    190                     {
    191                         $sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = :items, `data` = :data, `mtime` = :time WHERE `id` = :feed';
    192                         $query = $this->mysql->prepare($sql);
    193                         $query->bindValue(':items', $items);
    194                     }
    195                     else
    196                     {
    197                         $sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `data` = :data, `mtime` = :time WHERE `id` = :feed';
    198                         $query = $this->mysql->prepare($sql);
    199                     }
    200 
    201                     $query->bindValue(':data', $prepared[0]);
    202                     $query->bindValue(':time', time());
    203                     $query->bindValue(':feed', $this->id);
    204                     if (!$query->execute())
    205                     {
    206                         return false;
    207                     }
    208                 }
    209                 else
    210                 {
    211                     $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:feed, :count, :data, :time)');
    212                     $query->bindValue(':feed', $this->id);
    213                     $query->bindValue(':count', count($prepared[1]));
    214                     $query->bindValue(':data', $prepared[0]);
    215                     $query->bindValue(':time', time());
    216                     if (!$query->execute())
    217                     {
    218                         return false;
    219                     }
    220                 }
    221 
    222                 $ids = array_keys($prepared[1]);
    223                 if (!empty($ids))
    224                 {
    225                     foreach ($ids as $id)
    226                     {
    227                         $database_ids[] = $this->mysql->quote($id);
    228                     }
    229 
    230                     $query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `id` = ' . implode(' OR `id` = ', $database_ids) . ' AND `feed_id` = :feed');
    231                     $query->bindValue(':feed', $this->id);
    232 
    233                     if ($query->execute())
    234                     {
    235                         $existing_ids = array();
    236                         while ($row = $query->fetchColumn())
    237                         {
    238                             $existing_ids[] = $row;
    239                         }
    240 
    241                         $new_ids = array_diff($ids, $existing_ids);
    242 
    243                         foreach ($new_ids as $new_id)
    244                         {
    245                             if (!($date = $prepared[1][$new_id]->get_date('U')))
    246                             {
    247                                 $date = time();
    248                             }
    249 
    250                             $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'items` (`feed_id`, `id`, `data`, `posted`) VALUES(:feed, :id, :data, :date)');
    251                             $query->bindValue(':feed', $this->id);
    252                             $query->bindValue(':id', $new_id);
    253                             $query->bindValue(':data', serialize($prepared[1][$new_id]->data));
    254                             $query->bindValue(':date', $date);
    255                             if (!$query->execute())
    256                             {
    257                                 return false;
    258                             }
    259                         }
    260                         return true;
    261                     }
    262                 }
    263                 else
    264                 {
    265                     return true;
    266                 }
    267             }
    268         }
    269         else
    270         {
    271             $query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed');
    272             $query->bindValue(':feed', $this->id);
    273             if ($query->execute())
    274             {
    275                 if ($query->rowCount() > 0)
    276                 {
    277                     $query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = 0, `data` = :data, `mtime` = :time WHERE `id` = :feed');
    278                     $query->bindValue(':data', serialize($data));
    279                     $query->bindValue(':time', time());
    280                     $query->bindValue(':feed', $this->id);
    281                     if ($this->execute())
    282                     {
    283                         return true;
    284                     }
    285                 }
    286                 else
    287                 {
    288                     $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:id, 0, :data, :time)');
    289                     $query->bindValue(':id', $this->id);
    290                     $query->bindValue(':data', serialize($data));
    291                     $query->bindValue(':time', time());
    292                     if ($query->execute())
    293                     {
    294                         return true;
    295                     }
    296                 }
    297             }
    298         }
    299         return false;
    300     }
    301 
    302     /**
    303      * Retrieve the data saved to the cache
    304      *
    305      * @return array Data for SimplePie::$data
    306      */
    307     public function load()
    308     {
    309         if ($this->mysql === null)
    310         {
    311             return false;
    312         }
    313 
    314         $query = $this->mysql->prepare('SELECT `items`, `data` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
    315         $query->bindValue(':id', $this->id);
    316         if ($query->execute() && ($row = $query->fetch()))
    317         {
    318             $data = unserialize($row[1]);
    319 
    320             if (isset($this->options['items'][0]))
    321             {
    322                 $items = (int) $this->options['items'][0];
    323             }
    324             else
    325             {
    326                 $items = (int) $row[0];
    327             }
    328 
    329             if ($items !== 0)
    330             {
    331                 if (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]))
    332                 {
    333                     $feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0];
    334                 }
    335                 elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]))
    336                 {
    337                     $feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0];
    338                 }
    339                 elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]))
    340                 {
    341                     $feed =& $data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0];
    342                 }
    343                 elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]))
    344                 {
    345                     $feed =& $data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0];
    346                 }
    347                 else
    348                 {
    349                     $feed = null;
    350                 }
    351 
    352                 if ($feed !== null)
    353                 {
    354                     $sql = 'SELECT `data` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :feed ORDER BY `posted` DESC';
    355                     if ($items > 0)
    356                     {
    357                         $sql .= ' LIMIT ' . $items;
    358                     }
    359 
    360                     $query = $this->mysql->prepare($sql);
    361                     $query->bindValue(':feed', $this->id);
    362                     if ($query->execute())
    363                     {
    364                         while ($row = $query->fetchColumn())
    365                         {
    366                             $feed['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry'][] = unserialize($row);
    367                         }
    368                     }
    369                     else
    370                     {
    371                         return false;
    372                     }
    373                 }
    374             }
    375             return $data;
    376         }
    377         return false;
    378     }
    379 
    380     /**
    381      * Retrieve the last modified time for the cache
    382      *
    383      * @return int Timestamp
    384      */
    385     public function mtime()
    386     {
    387         if ($this->mysql === null)
    388         {
    389             return false;
    390         }
    391 
    392         $query = $this->mysql->prepare('SELECT `mtime` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
    393         $query->bindValue(':id', $this->id);
    394         if ($query->execute() && ($time = $query->fetchColumn()))
    395         {
    396             return $time;
    397         }
    398 
    399         return false;
    400     }
    401 
    402     /**
    403      * Set the last modified time to the current time
    404      *
    405      * @return bool Success status
    406      */
    407     public function touch()
    408     {
    409         if ($this->mysql === null)
    410         {
    411             return false;
    412         }
    413 
    414         $query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `mtime` = :time WHERE `id` = :id');
    415         $query->bindValue(':time', time());
    416         $query->bindValue(':id', $this->id);
    417 
    418         return $query->execute() && $query->rowCount() > 0;
    419     }
    420 
    421     /**
    422      * Remove the cache
    423      *
    424      * @return bool Success status
    425      */
    426     public function unlink()
    427     {
    428         if ($this->mysql === null)
    429         {
    430             return false;
    431         }
    432 
    433         $query = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
    434         $query->bindValue(':id', $this->id);
    435         $query2 = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :id');
    436         $query2->bindValue(':id', $this->id);
    437 
    438         return $query->execute() && $query2->execute();
    439     }
     62    /**
     63     * PDO instance
     64     *
     65     * @var \PDO
     66     */
     67    protected $mysql;
     68
     69    /**
     70     * Options
     71     *
     72     * @var array
     73     */
     74    protected $options;
     75
     76    /**
     77     * Cache ID
     78     *
     79     * @var string
     80     */
     81    protected $id;
     82
     83    /**
     84     * Create a new cache object
     85     *
     86     * @param string $location Location string (from SimplePie::$cache_location)
     87     * @param string $name Unique ID for the cache
     88     * @param Base::TYPE_FEED|Base::TYPE_IMAGE $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
     89     */
     90    public function __construct($location, $name, $type)
     91    {
     92        $this->options = [
     93            'user' => null,
     94            'pass' => null,
     95            'host' => '127.0.0.1',
     96            'port' => '3306',
     97            'path' => '',
     98            'extras' => [
     99                'prefix' => '',
     100                'cache_purge_time' => 2592000
     101            ],
     102        ];
     103
     104        $this->options = array_replace_recursive($this->options, \SimplePie\Cache::parse_URL($location));
     105
     106        // Path is prefixed with a "/"
     107        $this->options['dbname'] = substr($this->options['path'], 1);
     108
     109        try {
     110            $this->mysql = new \PDO("mysql:dbname={$this->options['dbname']};host={$this->options['host']};port={$this->options['port']}", $this->options['user'], $this->options['pass'], [\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8']);
     111        } catch (\PDOException $e) {
     112            $this->mysql = null;
     113            return;
     114        }
     115
     116        $this->id = $name . $type;
     117
     118        if (!$query = $this->mysql->query('SHOW TABLES')) {
     119            $this->mysql = null;
     120            return;
     121        }
     122
     123        $db = [];
     124        while ($row = $query->fetchColumn()) {
     125            $db[] = $row;
     126        }
     127
     128        if (!in_array($this->options['extras']['prefix'] . 'cache_data', $db)) {
     129            $query = $this->mysql->exec('CREATE TABLE `' . $this->options['extras']['prefix'] . 'cache_data` (`id` TEXT CHARACTER SET utf8 NOT NULL, `items` SMALLINT NOT NULL DEFAULT 0, `data` BLOB NOT NULL, `mtime` INT UNSIGNED NOT NULL, UNIQUE (`id`(125)))');
     130            if ($query === false) {
     131                trigger_error("Can't create " . $this->options['extras']['prefix'] . "cache_data table, check permissions", \E_USER_WARNING);
     132                $this->mysql = null;
     133                return;
     134            }
     135        }
     136
     137        if (!in_array($this->options['extras']['prefix'] . 'items', $db)) {
     138            $query = $this->mysql->exec('CREATE TABLE `' . $this->options['extras']['prefix'] . 'items` (`feed_id` TEXT CHARACTER SET utf8 NOT NULL, `id` TEXT CHARACTER SET utf8 NOT NULL, `data` MEDIUMBLOB NOT NULL, `posted` INT UNSIGNED NOT NULL, INDEX `feed_id` (`feed_id`(125)))');
     139            if ($query === false) {
     140                trigger_error("Can't create " . $this->options['extras']['prefix'] . "items table, check permissions", \E_USER_WARNING);
     141                $this->mysql = null;
     142                return;
     143            }
     144        }
     145    }
     146
     147    /**
     148     * Save data to the cache
     149     *
     150     * @param array|\SimplePie\SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
     151     * @return bool Successfulness
     152     */
     153    public function save($data)
     154    {
     155        if ($this->mysql === null) {
     156            return false;
     157        }
     158
     159        $query = $this->mysql->prepare('DELETE i, cd FROM `' . $this->options['extras']['prefix'] . 'cache_data` cd, ' .
     160            '`' . $this->options['extras']['prefix'] . 'items` i ' .
     161            'WHERE cd.id = i.feed_id ' .
     162            'AND cd.mtime < (unix_timestamp() - :purge_time)');
     163        $query->bindValue(':purge_time', $this->options['extras']['cache_purge_time']);
     164
     165        if (!$query->execute()) {
     166            return false;
     167        }
     168
     169        if ($data instanceof \SimplePie\SimplePie) {
     170            $data = clone $data;
     171
     172            $prepared = self::prepare_simplepie_object_for_cache($data);
     173
     174            $query = $this->mysql->prepare('SELECT COUNT(*) FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed');
     175            $query->bindValue(':feed', $this->id);
     176            if ($query->execute()) {
     177                if ($query->fetchColumn() > 0) {
     178                    $items = count($prepared[1]);
     179                    if ($items) {
     180                        $sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = :items, `data` = :data, `mtime` = :time WHERE `id` = :feed';
     181                        $query = $this->mysql->prepare($sql);
     182                        $query->bindValue(':items', $items);
     183                    } else {
     184                        $sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `data` = :data, `mtime` = :time WHERE `id` = :feed';
     185                        $query = $this->mysql->prepare($sql);
     186                    }
     187
     188                    $query->bindValue(':data', $prepared[0]);
     189                    $query->bindValue(':time', time());
     190                    $query->bindValue(':feed', $this->id);
     191                    if (!$query->execute()) {
     192                        return false;
     193                    }
     194                } else {
     195                    $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:feed, :count, :data, :time)');
     196                    $query->bindValue(':feed', $this->id);
     197                    $query->bindValue(':count', count($prepared[1]));
     198                    $query->bindValue(':data', $prepared[0]);
     199                    $query->bindValue(':time', time());
     200                    if (!$query->execute()) {
     201                        return false;
     202                    }
     203                }
     204
     205                $ids = array_keys($prepared[1]);
     206                if (!empty($ids)) {
     207                    foreach ($ids as $id) {
     208                        $database_ids[] = $this->mysql->quote($id);
     209                    }
     210
     211                    $query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `id` = ' . implode(' OR `id` = ', $database_ids) . ' AND `feed_id` = :feed');
     212                    $query->bindValue(':feed', $this->id);
     213
     214                    if ($query->execute()) {
     215                        $existing_ids = [];
     216                        while ($row = $query->fetchColumn()) {
     217                            $existing_ids[] = $row;
     218                        }
     219
     220                        $new_ids = array_diff($ids, $existing_ids);
     221
     222                        foreach ($new_ids as $new_id) {
     223                            if (!($date = $prepared[1][$new_id]->get_date('U'))) {
     224                                $date = time();
     225                            }
     226
     227                            $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'items` (`feed_id`, `id`, `data`, `posted`) VALUES(:feed, :id, :data, :date)');
     228                            $query->bindValue(':feed', $this->id);
     229                            $query->bindValue(':id', $new_id);
     230                            $query->bindValue(':data', serialize($prepared[1][$new_id]->data));
     231                            $query->bindValue(':date', $date);
     232                            if (!$query->execute()) {
     233                                return false;
     234                            }
     235                        }
     236                        return true;
     237                    }
     238                } else {
     239                    return true;
     240                }
     241            }
     242        } else {
     243            $query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed');
     244            $query->bindValue(':feed', $this->id);
     245            if ($query->execute()) {
     246                if ($query->rowCount() > 0) {
     247                    $query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = 0, `data` = :data, `mtime` = :time WHERE `id` = :feed');
     248                    $query->bindValue(':data', serialize($data));
     249                    $query->bindValue(':time', time());
     250                    $query->bindValue(':feed', $this->id);
     251                    if ($query->execute()) {
     252                        return true;
     253                    }
     254                } else {
     255                    $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:id, 0, :data, :time)');
     256                    $query->bindValue(':id', $this->id);
     257                    $query->bindValue(':data', serialize($data));
     258                    $query->bindValue(':time', time());
     259                    if ($query->execute()) {
     260                        return true;
     261                    }
     262                }
     263            }
     264        }
     265        return false;
     266    }
     267
     268    /**
     269     * Retrieve the data saved to the cache
     270     *
     271     * @return array Data for SimplePie::$data
     272     */
     273    public function load()
     274    {
     275        if ($this->mysql === null) {
     276            return false;
     277        }
     278
     279        $query = $this->mysql->prepare('SELECT `items`, `data` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
     280        $query->bindValue(':id', $this->id);
     281        if ($query->execute() && ($row = $query->fetch())) {
     282            $data = unserialize($row[1]);
     283
     284            if (isset($this->options['items'][0])) {
     285                $items = (int) $this->options['items'][0];
     286            } else {
     287                $items = (int) $row[0];
     288            }
     289
     290            if ($items !== 0) {
     291                if (isset($data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['feed'][0])) {
     292                    $feed = &$data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['feed'][0];
     293                } elseif (isset($data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['feed'][0])) {
     294                    $feed = &$data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['feed'][0];
     295                } elseif (isset($data['child'][\SimplePie\SimplePie::NAMESPACE_RDF]['RDF'][0])) {
     296                    $feed = &$data['child'][\SimplePie\SimplePie::NAMESPACE_RDF]['RDF'][0];
     297                } elseif (isset($data['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['rss'][0])) {
     298                    $feed = &$data['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['rss'][0];
     299                } else {
     300                    $feed = null;
     301                }
     302
     303                if ($feed !== null) {
     304                    $sql = 'SELECT `data` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :feed ORDER BY `posted` DESC';
     305                    if ($items > 0) {
     306                        $sql .= ' LIMIT ' . $items;
     307                    }
     308
     309                    $query = $this->mysql->prepare($sql);
     310                    $query->bindValue(':feed', $this->id);
     311                    if ($query->execute()) {
     312                        while ($row = $query->fetchColumn()) {
     313                            $feed['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['entry'][] = unserialize($row);
     314                        }
     315                    } else {
     316                        return false;
     317                    }
     318                }
     319            }
     320            return $data;
     321        }
     322        return false;
     323    }
     324
     325    /**
     326     * Retrieve the last modified time for the cache
     327     *
     328     * @return int Timestamp
     329     */
     330    public function mtime()
     331    {
     332        if ($this->mysql === null) {
     333            return false;
     334        }
     335
     336        $query = $this->mysql->prepare('SELECT `mtime` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
     337        $query->bindValue(':id', $this->id);
     338        if ($query->execute() && ($time = $query->fetchColumn())) {
     339            return $time;
     340        }
     341
     342        return false;
     343    }
     344
     345    /**
     346     * Set the last modified time to the current time
     347     *
     348     * @return bool Success status
     349     */
     350    public function touch()
     351    {
     352        if ($this->mysql === null) {
     353            return false;
     354        }
     355
     356        $query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `mtime` = :time WHERE `id` = :id');
     357        $query->bindValue(':time', time());
     358        $query->bindValue(':id', $this->id);
     359
     360        return $query->execute() && $query->rowCount() > 0;
     361    }
     362
     363    /**
     364     * Remove the cache
     365     *
     366     * @return bool Success status
     367     */
     368    public function unlink()
     369    {
     370        if ($this->mysql === null) {
     371            return false;
     372        }
     373
     374        $query = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
     375        $query->bindValue(':id', $this->id);
     376        $query2 = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :id');
     377        $query2->bindValue(':id', $this->id);
     378
     379        return $query->execute() && $query2->execute();
     380    }
    440381}
     382
     383class_alias('SimplePie\Cache\MySQL', 'SimplePie_Cache_MySQL');
  • trunk/src/wp-includes/SimplePie/src/Cache/Redis.php

    r52393 r59141  
    22
    33/**
    4  * SimplePie Redis Cache Extension
     4 * SimplePie
     5 *
     6 * A PHP-Based RSS and Atom Feed Framework.
     7 * Takes the hard work out of managing a complete RSS/Atom solution.
     8 *
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     10 * All rights reserved.
     11 *
     12 * Redistribution and use in source and binary forms, with or without modification, are
     13 * permitted provided that the following conditions are met:
     14 *
     15 *  * Redistributions of source code must retain the above copyright notice, this list of
     16 *    conditions and the following disclaimer.
     17 *
     18 *  * Redistributions in binary form must reproduce the above copyright notice, this list
     19 *    of conditions and the following disclaimer in the documentation and/or other materials
     20 *    provided with the distribution.
     21 *
     22 *  * Neither the name of the SimplePie Team nor the names of its contributors may be used
     23 *    to endorse or promote products derived from this software without specific prior
     24 *    written permission.
     25 *
     26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
     27 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
     28 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
     29 * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     33 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     34 * POSSIBILITY OF SUCH DAMAGE.
    535 *
    636 * @package SimplePie
    7  * @author Jan Kozak <galvani78@gmail.com>
    8  * @link http://galvani.cz/
     37 * @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
     38 * @author Ryan Parman
     39 * @author Sam Sneddon
     40 * @author Ryan McCue
     41 * @link http://simplepie.org/ SimplePie
    942 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
    10  * @version 0.2.9
    1143 */
    1244
     45namespace SimplePie\Cache;
     46
     47use Redis as NativeRedis;
    1348
    1449/**
     
    2459 * @subpackage Caching
    2560 * @uses Redis
     61 * @deprecated since SimplePie 1.8.0, use implementation of "Psr\SimpleCache\CacheInterface" instead
    2662 */
    27 class SimplePie_Cache_Redis implements SimplePie_Cache_Base {
     63class Redis implements Base
     64{
    2865    /**
    2966     * Redis instance
    3067     *
    31      * @var \Redis
     68     * @var NativeRedis
    3269     */
    3370    protected $cache;
     
    4683     */
    4784    protected $name;
    48 
    49     /**
    50      * Cache Data
    51      *
    52      * @var type
    53      */
    54     protected $data;
    5585
    5686    /**
     
    5989     * @param string $location Location string (from SimplePie::$cache_location)
    6090     * @param string $name Unique ID for the cache
    61      * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
    62      */
    63     public function __construct($location, $name, $options = null) {
     91     * @param Base::TYPE_FEED|Base::TYPE_IMAGE $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
     92     */
     93    public function __construct($location, $name, $options = null)
     94    {
    6495        //$this->cache = \flow\simple\cache\Redis::getRedisClientInstance();
    65         $parsed = SimplePie_Cache::parse_URL($location);
    66         $redis = new Redis();
     96        $parsed = \SimplePie\Cache::parse_URL($location);
     97        $redis = new NativeRedis();
    6798        $redis->connect($parsed['host'], $parsed['port']);
    6899        if (isset($parsed['pass'])) {
     
    77108            $this->options = $options;
    78109        } else {
    79             $this->options = array (
     110            $this->options = [
    80111                'prefix' => 'rss:simple_primary:',
    81112                'expire' => 0,
    82             );
     113            ];
    83114        }
    84115
     
    87118
    88119    /**
    89      * @param \Redis $cache
    90      */
    91     public function setRedisClient(\Redis $cache) {
     120     * @param NativeRedis $cache
     121     */
     122    public function setRedisClient(NativeRedis $cache)
     123    {
    92124        $this->cache = $cache;
    93125    }
     
    96128     * Save data to the cache
    97129     *
    98      * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
     130     * @param array|\SimplePie\SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
    99131     * @return bool Successfulness
    100132     */
    101     public function save($data) {
    102         if ($data instanceof SimplePie) {
     133    public function save($data)
     134    {
     135        if ($data instanceof \SimplePie\SimplePie) {
    103136            $data = $data->data;
    104137        }
     
    116149     * @return array Data for SimplePie::$data
    117150     */
    118     public function load() {
     151    public function load()
     152    {
    119153        $data = $this->cache->get($this->name);
    120154
     
    130164     * @return int Timestamp
    131165     */
    132     public function mtime() {
    133 
     166    public function mtime()
     167    {
    134168        $data = $this->cache->get($this->name);
    135169
     
    146180     * @return bool Success status
    147181     */
    148     public function touch() {
    149 
     182    public function touch()
     183    {
    150184        $data = $this->cache->get($this->name);
    151185
     
    166200     * @return bool Success status
    167201     */
    168     public function unlink() {
     202    public function unlink()
     203    {
    169204        return $this->cache->set($this->name, null);
    170205    }
    171 
    172206}
     207
     208class_alias('SimplePie\Cache\Redis', 'SimplePie_Cache_Redis');
  • trunk/src/wp-includes/SimplePie/src/Caption.php

    r59140 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie;
    4446
    4547/**
    4648 * Handles `<media:text>` captions as defined in Media RSS.
    4749 *
    48  * Used by {@see SimplePie_Enclosure::get_caption()} and {@see SimplePie_Enclosure::get_captions()}
     50 * Used by {@see \SimplePie\Enclosure::get_caption()} and {@see \SimplePie\Enclosure::get_captions()}
    4951 *
    50  * This class can be overloaded with {@see SimplePie::set_caption_class()}
     52 * This class can be overloaded with {@see \SimplePie\SimplePie::set_caption_class()}
    5153 *
    5254 * @package SimplePie
    5355 * @subpackage API
    5456 */
    55 class SimplePie_Caption
     57class Caption
    5658{
    57     /**
    58     * Content type
    59     *
    60     * @var string
    61     * @see get_type()
    62     */
    63     var $type;
     59    /**
     60    * Content type
     61    *
     62    * @var string
     63    * @see get_type()
     64    */
     65    public $type;
    6466
    65     /**
    66     * Language
    67     *
    68     * @var string
    69     * @see get_language()
    70     */
    71     var $lang;
     67    /**
     68    * Language
     69    *
     70    * @var string
     71    * @see get_language()
     72    */
     73    public $lang;
    7274
    73     /**
    74     * Start time
    75     *
    76     * @var string
    77     * @see get_starttime()
    78     */
    79     var $startTime;
     75    /**
     76    * Start time
     77    *
     78    * @var string
     79    * @see get_starttime()
     80    */
     81    public $startTime;
    8082
    81     /**
    82     * End time
    83     *
    84     * @var string
    85     * @see get_endtime()
    86     */
    87     var $endTime;
     83    /**
     84    * End time
     85    *
     86    * @var string
     87    * @see get_endtime()
     88    */
     89    public $endTime;
    8890
    89     /**
    90     * Caption text
    91     *
    92     * @var string
    93     * @see get_text()
    94     */
    95     var $text;
     91    /**
     92    * Caption text
     93    *
     94    * @var string
     95    * @see get_text()
     96    */
     97    public $text;
    9698
    97     /**
    98     * Constructor, used to input the data
    99     *
    100     * For documentation on all the parameters, see the corresponding
    101     * properties and their accessors
    102     */
    103     public function __construct($type = null, $lang = null, $startTime = null, $endTime = null, $text = null)
    104     {
    105         $this->type = $type;
    106         $this->lang = $lang;
    107         $this->startTime = $startTime;
    108         $this->endTime = $endTime;
    109         $this->text = $text;
    110     }
     99    /**
     100    * Constructor, used to input the data
     101    *
     102    * For documentation on all the parameters, see the corresponding
     103    * properties and their accessors
     104    */
     105    public function __construct($type = null, $lang = null, $startTime = null, $endTime = null, $text = null)
     106    {
     107        $this->type = $type;
     108        $this->lang = $lang;
     109        $this->startTime = $startTime;
     110        $this->endTime = $endTime;
     111        $this->text = $text;
     112    }
    111113
    112     /**
    113     * String-ified version
    114     *
    115     * @return string
    116     */
    117     public function __toString()
    118     {
    119         // There is no $this->data here
    120         return md5(serialize($this));
    121     }
     114    /**
     115    * String-ified version
     116    *
     117    * @return string
     118    */
     119    public function __toString()
     120    {
     121        // There is no $this->data here
     122        return md5(serialize($this));
     123    }
    122124
    123     /**
    124      * Get the end time
    125      *
    126      * @return string|null Time in the format 'hh:mm:ss.SSS'
    127      */
    128     public function get_endtime()
    129     {
    130         if ($this->endTime !== null)
    131         {
    132             return $this->endTime;
    133         }
     125    /**
     126     * Get the end time
     127     *
     128     * @return string|null Time in the format 'hh:mm:ss.SSS'
     129     */
     130    public function get_endtime()
     131    {
     132        if ($this->endTime !== null) {
     133            return $this->endTime;
     134        }
    134135
    135         return null;
    136     }
     136        return null;
     137    }
    137138
    138     /**
    139      * Get the language
    140      *
    141      * @link http://tools.ietf.org/html/rfc3066
    142      * @return string|null Language code as per RFC 3066
    143      */
    144     public function get_language()
    145     {
    146         if ($this->lang !== null)
    147         {
    148             return $this->lang;
    149         }
     139    /**
     140     * Get the language
     141     *
     142     * @link http://tools.ietf.org/html/rfc3066
     143     * @return string|null Language code as per RFC 3066
     144     */
     145    public function get_language()
     146    {
     147        if ($this->lang !== null) {
     148            return $this->lang;
     149        }
    150150
    151         return null;
    152     }
     151        return null;
     152    }
    153153
    154     /**
    155      * Get the start time
    156      *
    157      * @return string|null Time in the format 'hh:mm:ss.SSS'
    158      */
    159     public function get_starttime()
    160     {
    161         if ($this->startTime !== null)
    162         {
    163             return $this->startTime;
    164         }
     154    /**
     155     * Get the start time
     156     *
     157     * @return string|null Time in the format 'hh:mm:ss.SSS'
     158     */
     159    public function get_starttime()
     160    {
     161        if ($this->startTime !== null) {
     162            return $this->startTime;
     163        }
    165164
    166         return null;
    167     }
     165        return null;
     166    }
    168167
    169     /**
    170      * Get the text of the caption
    171      *
    172      * @return string|null
    173      */
    174     public function get_text()
    175     {
    176         if ($this->text !== null)
    177         {
    178             return $this->text;
    179         }
     168    /**
     169     * Get the text of the caption
     170     *
     171     * @return string|null
     172     */
     173    public function get_text()
     174    {
     175        if ($this->text !== null) {
     176            return $this->text;
     177        }
    180178
    181         return null;
    182     }
     179        return null;
     180    }
    183181
    184     /**
    185      * Get the content type (not MIME type)
    186      *
    187      * @return string|null Either 'text' or 'html'
    188      */
    189     public function get_type()
    190     {
    191         if ($this->type !== null)
    192         {
    193             return $this->type;
    194         }
     182    /**
     183     * Get the content type (not MIME type)
     184     *
     185     * @return string|null Either 'text' or 'html'
     186     */
     187    public function get_type()
     188    {
     189        if ($this->type !== null) {
     190            return $this->type;
     191        }
    195192
    196         return null;
    197     }
     193        return null;
     194    }
    198195}
     196
     197class_alias('SimplePie\Caption', 'SimplePie_Caption');
  • trunk/src/wp-includes/SimplePie/src/Category.php

    r59140 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie;
     46
    4447/**
    4548 * Manages all category-related data
    4649 *
    47  * Used by {@see SimplePie_Item::get_category()} and {@see SimplePie_Item::get_categories()}
     50 * Used by {@see \SimplePie\Item::get_category()} and {@see \SimplePie\Item::get_categories()}
    4851 *
    49  * This class can be overloaded with {@see SimplePie::set_category_class()}
     52 * This class can be overloaded with {@see \SimplePie\SimplePie::set_category_class()}
    5053 *
    5154 * @package SimplePie
    5255 * @subpackage API
    5356 */
    54 class SimplePie_Category
     57class Category
    5558{
    56     /**
    57     * Category identifier
    58     *
    59     * @var string|null
    60     * @see get_term
    61     */
    62     var $term;
     59    /**
     60    * Category identifier
     61    *
     62    * @var string|null
     63    * @see get_term
     64    */
     65    public $term;
    6366
    64     /**
    65     * Categorization scheme identifier
    66     *
    67     * @var string|null
    68     * @see get_scheme()
    69     */
    70     var $scheme;
     67    /**
     68    * Categorization scheme identifier
     69    *
     70    * @var string|null
     71    * @see get_scheme()
     72    */
     73    public $scheme;
    7174
    72     /**
    73     * Human readable label
    74     *
    75     * @var string|null
    76     * @see get_label()
    77     */
    78     var $label;
     75    /**
     76    * Human readable label
     77    *
     78    * @var string|null
     79    * @see get_label()
     80    */
     81    public $label;
    7982
    80     /**
    81     * Category type
    82      *
    83     * category for <category>
    84     * subject for <dc:subject>
    85     *
    86     * @var string|null
    87     * @see get_type()
    88     */
    89     var $type;
     83    /**
     84    * Category type
     85     *
     86    * category for <category>
     87    * subject for <dc:subject>
     88    *
     89    * @var string|null
     90    * @see get_type()
     91    */
     92    public $type;
    9093
    91     /**
    92     * Constructor, used to input the data
    93     *
    94     * @param string|null $term
    95     * @param string|null $scheme
    96     * @param string|null $label
    97     * @param string|null $type
    98     */
    99     public function __construct($term = null, $scheme = null, $label = null, $type = null)
    100     {
    101         $this->term = $term;
    102         $this->scheme = $scheme;
    103         $this->label = $label;
    104         $this->type = $type;
    105     }
     94    /**
     95    * Constructor, used to input the data
     96    *
     97    * @param string|null $term
     98    * @param string|null $scheme
     99    * @param string|null $label
     100    * @param string|null $type
     101    */
     102    public function __construct($term = null, $scheme = null, $label = null, $type = null)
     103    {
     104        $this->term = $term;
     105        $this->scheme = $scheme;
     106        $this->label = $label;
     107        $this->type = $type;
     108    }
    106109
    107     /**
    108     * String-ified version
    109     *
    110     * @return string
    111     */
    112     public function __toString()
    113     {
    114         // There is no $this->data here
    115         return md5(serialize($this));
    116     }
     110    /**
     111    * String-ified version
     112    *
     113    * @return string
     114    */
     115    public function __toString()
     116    {
     117        // There is no $this->data here
     118        return md5(serialize($this));
     119    }
    117120
    118     /**
    119     * Get the category identifier
    120     *
    121     * @return string|null
    122     */
    123     public function get_term()
    124     {
    125         return $this->term;
    126     }
     121    /**
     122    * Get the category identifier
     123    *
     124    * @return string|null
     125    */
     126    public function get_term()
     127    {
     128        return $this->term;
     129    }
    127130
    128     /**
    129     * Get the categorization scheme identifier
    130     *
    131     * @return string|null
    132     */
    133     public function get_scheme()
    134     {
    135         return $this->scheme;
    136     }
     131    /**
     132    * Get the categorization scheme identifier
     133    *
     134    * @return string|null
     135    */
     136    public function get_scheme()
     137    {
     138        return $this->scheme;
     139    }
    137140
    138     /**
    139      * Get the human readable label
    140      *
    141      * @param bool $strict
    142      * @return string|null
    143      */
    144     public function get_label($strict = false)
    145     {
    146         if ($this->label === null && $strict !== true)
    147         {
    148             return $this->get_term();
    149         }
    150         return $this->label;
    151     }
     141    /**
     142     * Get the human readable label
     143     *
     144     * @param bool $strict
     145     * @return string|null
     146     */
     147    public function get_label($strict = false)
     148    {
     149        if ($this->label === null && $strict !== true) {
     150            return $this->get_term();
     151        }
     152        return $this->label;
     153    }
    152154
    153     /**
    154     * Get the category type
    155     *
    156     * @return string|null
    157     */
    158     public function get_type()
    159     {
    160         return $this->type;
    161     }
     155    /**
     156    * Get the category type
     157    *
     158    * @return string|null
     159    */
     160    public function get_type()
     161    {
     162        return $this->type;
     163    }
    162164}
    163165
     166class_alias('SimplePie\Category', 'SimplePie_Category');
  • trunk/src/wp-includes/SimplePie/src/Content/Type/Sniffer.php

    r49176 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie\Content\Type;
    4446
    4547/**
     
    5254 *
    5355 *
    54  * This class can be overloaded with {@see SimplePie::set_content_type_sniffer_class()}
     56 * This class can be overloaded with {@see \SimplePie\SimplePie::set_content_type_sniffer_class()}
    5557 *
    5658 * @package SimplePie
    5759 * @subpackage HTTP
    5860 */
    59 class SimplePie_Content_Type_Sniffer
     61class Sniffer
    6062{
    61     /**
    62      * File object
    63      *
    64      * @var SimplePie_File
    65      */
    66     var $file;
    67 
    68     /**
    69      * Create an instance of the class with the input file
    70      *
    71      * @param SimplePie_Content_Type_Sniffer $file Input file
    72      */
    73     public function __construct($file)
    74     {
    75         $this->file = $file;
    76     }
    77 
    78     /**
    79      * Get the Content-Type of the specified file
    80      *
    81      * @return string Actual Content-Type
    82      */
    83     public function get_type()
    84     {
    85         if (isset($this->file->headers['content-type']))
    86         {
    87             if (!isset($this->file->headers['content-encoding'])
    88                 && ($this->file->headers['content-type'] === 'text/plain'
    89                     || $this->file->headers['content-type'] === 'text/plain; charset=ISO-8859-1'
    90                     || $this->file->headers['content-type'] === 'text/plain; charset=iso-8859-1'
    91                     || $this->file->headers['content-type'] === 'text/plain; charset=UTF-8'))
    92             {
    93                 return $this->text_or_binary();
    94             }
    95 
    96             if (($pos = strpos($this->file->headers['content-type'], ';')) !== false)
    97             {
    98                 $official = substr($this->file->headers['content-type'], 0, $pos);
    99             }
    100             else
    101             {
    102                 $official = $this->file->headers['content-type'];
    103             }
    104             $official = trim(strtolower($official));
    105 
    106             if ($official === 'unknown/unknown'
    107                 || $official === 'application/unknown')
    108             {
    109                 return $this->unknown();
    110             }
    111             elseif (substr($official, -4) === '+xml'
    112                 || $official === 'text/xml'
    113                 || $official === 'application/xml')
    114             {
    115                 return $official;
    116             }
    117             elseif (substr($official, 0, 6) === 'image/')
    118             {
    119                 if ($return = $this->image())
    120                 {
    121                     return $return;
    122                 }
    123 
    124                 return $official;
    125             }
    126             elseif ($official === 'text/html')
    127             {
    128                 return $this->feed_or_html();
    129             }
    130 
    131             return $official;
    132         }
    133 
    134         return $this->unknown();
    135     }
    136 
    137     /**
    138      * Sniff text or binary
    139      *
    140      * @return string Actual Content-Type
    141      */
    142     public function text_or_binary()
    143     {
    144         if (substr($this->file->body, 0, 2) === "\xFE\xFF"
    145             || substr($this->file->body, 0, 2) === "\xFF\xFE"
    146             || substr($this->file->body, 0, 4) === "\x00\x00\xFE\xFF"
    147             || substr($this->file->body, 0, 3) === "\xEF\xBB\xBF")
    148         {
    149             return 'text/plain';
    150         }
    151         elseif (preg_match('/[\x00-\x08\x0E-\x1A\x1C-\x1F]/', $this->file->body))
    152         {
    153             return 'application/octet-stream';
    154         }
    155 
    156         return 'text/plain';
    157     }
    158 
    159     /**
    160      * Sniff unknown
    161      *
    162      * @return string Actual Content-Type
    163      */
    164     public function unknown()
    165     {
    166         $ws = strspn($this->file->body, "\x09\x0A\x0B\x0C\x0D\x20");
    167         if (strtolower(substr($this->file->body, $ws, 14)) === '<!doctype html'
    168             || strtolower(substr($this->file->body, $ws, 5)) === '<html'
    169             || strtolower(substr($this->file->body, $ws, 7)) === '<script')
    170         {
    171             return 'text/html';
    172         }
    173         elseif (substr($this->file->body, 0, 5) === '%PDF-')
    174         {
    175             return 'application/pdf';
    176         }
    177         elseif (substr($this->file->body, 0, 11) === '%!PS-Adobe-')
    178         {
    179             return 'application/postscript';
    180         }
    181         elseif (substr($this->file->body, 0, 6) === 'GIF87a'
    182             || substr($this->file->body, 0, 6) === 'GIF89a')
    183         {
    184             return 'image/gif';
    185         }
    186         elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A")
    187         {
    188             return 'image/png';
    189         }
    190         elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF")
    191         {
    192             return 'image/jpeg';
    193         }
    194         elseif (substr($this->file->body, 0, 2) === "\x42\x4D")
    195         {
    196             return 'image/bmp';
    197         }
    198         elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00")
    199         {
    200             return 'image/vnd.microsoft.icon';
    201         }
    202 
    203         return $this->text_or_binary();
    204     }
    205 
    206     /**
    207      * Sniff images
    208      *
    209      * @return string Actual Content-Type
    210      */
    211     public function image()
    212     {
    213         if (substr($this->file->body, 0, 6) === 'GIF87a'
    214             || substr($this->file->body, 0, 6) === 'GIF89a')
    215         {
    216             return 'image/gif';
    217         }
    218         elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A")
    219         {
    220             return 'image/png';
    221         }
    222         elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF")
    223         {
    224             return 'image/jpeg';
    225         }
    226         elseif (substr($this->file->body, 0, 2) === "\x42\x4D")
    227         {
    228             return 'image/bmp';
    229         }
    230         elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00")
    231         {
    232             return 'image/vnd.microsoft.icon';
    233         }
    234 
    235         return false;
    236     }
    237 
    238     /**
    239      * Sniff HTML
    240      *
    241      * @return string Actual Content-Type
    242      */
    243     public function feed_or_html()
    244     {
    245         $len = strlen($this->file->body);
    246         $pos = strspn($this->file->body, "\x09\x0A\x0D\x20\xEF\xBB\xBF");
    247 
    248         while ($pos < $len)
    249         {
    250             switch ($this->file->body[$pos])
    251             {
    252                 case "\x09":
    253                 case "\x0A":
    254                 case "\x0D":
    255                 case "\x20":
    256                     $pos += strspn($this->file->body, "\x09\x0A\x0D\x20", $pos);
    257                     continue 2;
    258 
    259                 case '<':
    260                     $pos++;
    261                     break;
    262 
    263                 default:
    264                     return 'text/html';
    265             }
    266 
    267             if (substr($this->file->body, $pos, 3) === '!--')
    268             {
    269                 $pos += 3;
    270                 if ($pos < $len && ($pos = strpos($this->file->body, '-->', $pos)) !== false)
    271                 {
    272                     $pos += 3;
    273                 }
    274                 else
    275                 {
    276                     return 'text/html';
    277                 }
    278             }
    279             elseif (substr($this->file->body, $pos, 1) === '!')
    280             {
    281                 if ($pos < $len && ($pos = strpos($this->file->body, '>', $pos)) !== false)
    282                 {
    283                     $pos++;
    284                 }
    285                 else
    286                 {
    287                     return 'text/html';
    288                 }
    289             }
    290             elseif (substr($this->file->body, $pos, 1) === '?')
    291             {
    292                 if ($pos < $len && ($pos = strpos($this->file->body, '?>', $pos)) !== false)
    293                 {
    294                     $pos += 2;
    295                 }
    296                 else
    297                 {
    298                     return 'text/html';
    299                 }
    300             }
    301             elseif (substr($this->file->body, $pos, 3) === 'rss'
    302                 || substr($this->file->body, $pos, 7) === 'rdf:RDF')
    303             {
    304                 return 'application/rss+xml';
    305             }
    306             elseif (substr($this->file->body, $pos, 4) === 'feed')
    307             {
    308                 return 'application/atom+xml';
    309             }
    310             else
    311             {
    312                 return 'text/html';
    313             }
    314         }
    315 
    316         return 'text/html';
    317     }
     63    /**
     64     * File object
     65     *
     66     * @var \SimplePie\File
     67     */
     68    public $file;
     69
     70    /**
     71     * Create an instance of the class with the input file
     72     *
     73     * @param Sniffer $file Input file
     74     */
     75    public function __construct($file)
     76    {
     77        $this->file = $file;
     78    }
     79
     80    /**
     81     * Get the Content-Type of the specified file
     82     *
     83     * @return string Actual Content-Type
     84     */
     85    public function get_type()
     86    {
     87        if (isset($this->file->headers['content-type'])) {
     88            if (!isset($this->file->headers['content-encoding'])
     89                && ($this->file->headers['content-type'] === 'text/plain'
     90                    || $this->file->headers['content-type'] === 'text/plain; charset=ISO-8859-1'
     91                    || $this->file->headers['content-type'] === 'text/plain; charset=iso-8859-1'
     92                    || $this->file->headers['content-type'] === 'text/plain; charset=UTF-8')) {
     93                return $this->text_or_binary();
     94            }
     95
     96            if (($pos = strpos($this->file->headers['content-type'], ';')) !== false) {
     97                $official = substr($this->file->headers['content-type'], 0, $pos);
     98            } else {
     99                $official = $this->file->headers['content-type'];
     100            }
     101            $official = trim(strtolower($official));
     102
     103            if ($official === 'unknown/unknown'
     104                || $official === 'application/unknown') {
     105                return $this->unknown();
     106            } elseif (substr($official, -4) === '+xml'
     107                || $official === 'text/xml'
     108                || $official === 'application/xml') {
     109                return $official;
     110            } elseif (substr($official, 0, 6) === 'image/') {
     111                if ($return = $this->image()) {
     112                    return $return;
     113                }
     114
     115                return $official;
     116            } elseif ($official === 'text/html') {
     117                return $this->feed_or_html();
     118            }
     119
     120            return $official;
     121        }
     122
     123        return $this->unknown();
     124    }
     125
     126    /**
     127     * Sniff text or binary
     128     *
     129     * @return string Actual Content-Type
     130     */
     131    public function text_or_binary()
     132    {
     133        if (substr($this->file->body, 0, 2) === "\xFE\xFF"
     134            || substr($this->file->body, 0, 2) === "\xFF\xFE"
     135            || substr($this->file->body, 0, 4) === "\x00\x00\xFE\xFF"
     136            || substr($this->file->body, 0, 3) === "\xEF\xBB\xBF") {
     137            return 'text/plain';
     138        } elseif (preg_match('/[\x00-\x08\x0E-\x1A\x1C-\x1F]/', $this->file->body)) {
     139            return 'application/octet-stream';
     140        }
     141
     142        return 'text/plain';
     143    }
     144
     145    /**
     146     * Sniff unknown
     147     *
     148     * @return string Actual Content-Type
     149     */
     150    public function unknown()
     151    {
     152        $ws = strspn($this->file->body, "\x09\x0A\x0B\x0C\x0D\x20");
     153        if (strtolower(substr($this->file->body, $ws, 14)) === '<!doctype html'
     154            || strtolower(substr($this->file->body, $ws, 5)) === '<html'
     155            || strtolower(substr($this->file->body, $ws, 7)) === '<script') {
     156            return 'text/html';
     157        } elseif (substr($this->file->body, 0, 5) === '%PDF-') {
     158            return 'application/pdf';
     159        } elseif (substr($this->file->body, 0, 11) === '%!PS-Adobe-') {
     160            return 'application/postscript';
     161        } elseif (substr($this->file->body, 0, 6) === 'GIF87a'
     162            || substr($this->file->body, 0, 6) === 'GIF89a') {
     163            return 'image/gif';
     164        } elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") {
     165            return 'image/png';
     166        } elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF") {
     167            return 'image/jpeg';
     168        } elseif (substr($this->file->body, 0, 2) === "\x42\x4D") {
     169            return 'image/bmp';
     170        } elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00") {
     171            return 'image/vnd.microsoft.icon';
     172        }
     173
     174        return $this->text_or_binary();
     175    }
     176
     177    /**
     178     * Sniff images
     179     *
     180     * @return string Actual Content-Type
     181     */
     182    public function image()
     183    {
     184        if (substr($this->file->body, 0, 6) === 'GIF87a'
     185            || substr($this->file->body, 0, 6) === 'GIF89a') {
     186            return 'image/gif';
     187        } elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") {
     188            return 'image/png';
     189        } elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF") {
     190            return 'image/jpeg';
     191        } elseif (substr($this->file->body, 0, 2) === "\x42\x4D") {
     192            return 'image/bmp';
     193        } elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00") {
     194            return 'image/vnd.microsoft.icon';
     195        }
     196
     197        return false;
     198    }
     199
     200    /**
     201     * Sniff HTML
     202     *
     203     * @return string Actual Content-Type
     204     */
     205    public function feed_or_html()
     206    {
     207        $len = strlen($this->file->body);
     208        $pos = strspn($this->file->body, "\x09\x0A\x0D\x20\xEF\xBB\xBF");
     209
     210        while ($pos < $len) {
     211            switch ($this->file->body[$pos]) {
     212                case "\x09":
     213                case "\x0A":
     214                case "\x0D":
     215                case "\x20":
     216                    $pos += strspn($this->file->body, "\x09\x0A\x0D\x20", $pos);
     217                    continue 2;
     218
     219                case '<':
     220                    $pos++;
     221                    break;
     222
     223                default:
     224                    return 'text/html';
     225            }
     226
     227            if (substr($this->file->body, $pos, 3) === '!--') {
     228                $pos += 3;
     229                if ($pos < $len && ($pos = strpos($this->file->body, '-->', $pos)) !== false) {
     230                    $pos += 3;
     231                } else {
     232                    return 'text/html';
     233                }
     234            } elseif (substr($this->file->body, $pos, 1) === '!') {
     235                if ($pos < $len && ($pos = strpos($this->file->body, '>', $pos)) !== false) {
     236                    $pos++;
     237                } else {
     238                    return 'text/html';
     239                }
     240            } elseif (substr($this->file->body, $pos, 1) === '?') {
     241                if ($pos < $len && ($pos = strpos($this->file->body, '?>', $pos)) !== false) {
     242                    $pos += 2;
     243                } else {
     244                    return 'text/html';
     245                }
     246            } elseif (substr($this->file->body, $pos, 3) === 'rss'
     247                || substr($this->file->body, $pos, 7) === 'rdf:RDF') {
     248                return 'application/rss+xml';
     249            } elseif (substr($this->file->body, $pos, 4) === 'feed') {
     250                return 'application/atom+xml';
     251            } else {
     252                return 'text/html';
     253            }
     254        }
     255
     256        return 'text/html';
     257    }
    318258}
     259
     260class_alias('SimplePie\Content\Type\Sniffer', 'SimplePie_Content_Type_Sniffer');
  • trunk/src/wp-includes/SimplePie/src/Copyright.php

    r59140 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie;
     46
    4447/**
    4548 * Manages `<media:copyright>` copyright tags as defined in Media RSS
    4649 *
    47  * Used by {@see SimplePie_Enclosure::get_copyright()}
     50 * Used by {@see \SimplePie\Enclosure::get_copyright()}
    4851 *
    49  * This class can be overloaded with {@see SimplePie::set_copyright_class()}
     52 * This class can be overloaded with {@see \SimplePie\SimplePie::set_copyright_class()}
    5053 *
    5154 * @package SimplePie
    5255 * @subpackage API
    5356 */
    54 class SimplePie_Copyright
     57class Copyright
    5558{
    56     /**
    57     * Copyright URL
    58     *
    59     * @var string
    60     * @see get_url()
    61     */
    62     var $url;
     59    /**
     60    * Copyright URL
     61    *
     62    * @var string
     63    * @see get_url()
     64    */
     65    public $url;
    6366
    64     /**
    65     * Attribution
    66     *
    67     * @var string
    68     * @see get_attribution()
    69     */
    70     var $label;
     67    /**
     68    * Attribution
     69    *
     70    * @var string
     71    * @see get_attribution()
     72    */
     73    public $label;
    7174
    72     /**
    73     * Constructor, used to input the data
    74     *
    75     * For documentation on all the parameters, see the corresponding
    76     * properties and their accessors
    77     */
    78     public function __construct($url = null, $label = null)
    79     {
    80         $this->url = $url;
    81         $this->label = $label;
    82     }
     75    /**
     76    * Constructor, used to input the data
     77    *
     78    * For documentation on all the parameters, see the corresponding
     79    * properties and their accessors
     80    */
     81    public function __construct($url = null, $label = null)
     82    {
     83        $this->url = $url;
     84        $this->label = $label;
     85    }
    8386
    84     /**
    85     * String-ified version
    86     *
    87     * @return string
    88     */
    89     public function __toString()
    90     {
    91         // There is no $this->data here
    92         return md5(serialize($this));
    93     }
     87    /**
     88    * String-ified version
     89    *
     90    * @return string
     91    */
     92    public function __toString()
     93    {
     94        // There is no $this->data here
     95        return md5(serialize($this));
     96    }
    9497
    95     /**
    96      * Get the copyright URL
    97      *
    98      * @return string|null URL to copyright information
    99      */
    100     public function get_url()
    101     {
    102         if ($this->url !== null)
    103         {
    104             return $this->url;
    105         }
     98    /**
     99     * Get the copyright URL
     100     *
     101     * @return string|null URL to copyright information
     102     */
     103    public function get_url()
     104    {
     105        if ($this->url !== null) {
     106            return $this->url;
     107        }
    106108
    107         return null;
    108     }
     109        return null;
     110    }
    109111
    110     /**
    111      * Get the attribution text
    112      *
    113      * @return string|null
    114      */
    115     public function get_attribution()
    116     {
    117         if ($this->label !== null)
    118         {
    119             return $this->label;
    120         }
     112    /**
     113     * Get the attribution text
     114     *
     115     * @return string|null
     116     */
     117    public function get_attribution()
     118    {
     119        if ($this->label !== null) {
     120            return $this->label;
     121        }
    121122
    122         return null;
    123     }
     123        return null;
     124    }
    124125}
     126
     127class_alias('SimplePie\Copyright', 'SimplePie_Copyright');
  • trunk/src/wp-includes/SimplePie/src/Credit.php

    r59140 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie;
     46
    4447/**
    4548 * Handles `<media:credit>` as defined in Media RSS
    4649 *
    47  * Used by {@see SimplePie_Enclosure::get_credit()} and {@see SimplePie_Enclosure::get_credits()}
     50 * Used by {@see \SimplePie\Enclosure::get_credit()} and {@see \SimplePie\Enclosure::get_credits()}
    4851 *
    49  * This class can be overloaded with {@see SimplePie::set_credit_class()}
     52 * This class can be overloaded with {@see \SimplePie\SimplePie::set_credit_class()}
    5053 *
    5154 * @package SimplePie
    5255 * @subpackage API
    5356 */
    54 class SimplePie_Credit
     57class Credit
    5558{
    56     /**
    57     * Credited role
    58     *
    59     * @var string
    60     * @see get_role()
    61     */
    62     var $role;
     59    /**
     60    * Credited role
     61    *
     62    * @var string
     63    * @see get_role()
     64    */
     65    public $role;
    6366
    64     /**
    65     * Organizational scheme
    66     *
    67     * @var string
    68     * @see get_scheme()
    69     */
    70     var $scheme;
     67    /**
     68    * Organizational scheme
     69    *
     70    * @var string
     71    * @see get_scheme()
     72    */
     73    public $scheme;
    7174
    72     /**
    73     * Credited name
    74     *
    75     * @var string
    76     * @see get_name()
    77     */
    78     var $name;
     75    /**
     76    * Credited name
     77    *
     78    * @var string
     79    * @see get_name()
     80    */
     81    public $name;
    7982
    80     /**
    81     * Constructor, used to input the data
    82     *
    83     * For documentation on all the parameters, see the corresponding
    84     * properties and their accessors
    85     */
    86     public function __construct($role = null, $scheme = null, $name = null)
    87     {
    88         $this->role = $role;
    89         $this->scheme = $scheme;
    90         $this->name = $name;
    91     }
     83    /**
     84    * Constructor, used to input the data
     85    *
     86    * For documentation on all the parameters, see the corresponding
     87    * properties and their accessors
     88    */
     89    public function __construct($role = null, $scheme = null, $name = null)
     90    {
     91        $this->role = $role;
     92        $this->scheme = $scheme;
     93        $this->name = $name;
     94    }
    9295
    93     /**
    94     * String-ified version
    95     *
    96     * @return string
    97     */
    98     public function __toString()
    99     {
    100         // There is no $this->data here
    101         return md5(serialize($this));
    102     }
     96    /**
     97    * String-ified version
     98    *
     99    * @return string
     100    */
     101    public function __toString()
     102    {
     103        // There is no $this->data here
     104        return md5(serialize($this));
     105    }
    103106
    104     /**
    105      * Get the role of the person receiving credit
    106      *
    107      * @return string|null
    108      */
    109     public function get_role()
    110     {
    111         if ($this->role !== null)
    112         {
    113             return $this->role;
    114         }
     107    /**
     108     * Get the role of the person receiving credit
     109     *
     110     * @return string|null
     111     */
     112    public function get_role()
     113    {
     114        if ($this->role !== null) {
     115            return $this->role;
     116        }
    115117
    116         return null;
    117     }
     118        return null;
     119    }
    118120
    119     /**
    120      * Get the organizational scheme
    121      *
    122      * @return string|null
    123      */
    124     public function get_scheme()
    125     {
    126         if ($this->scheme !== null)
    127         {
    128             return $this->scheme;
    129         }
     121    /**
     122     * Get the organizational scheme
     123     *
     124     * @return string|null
     125     */
     126    public function get_scheme()
     127    {
     128        if ($this->scheme !== null) {
     129            return $this->scheme;
     130        }
    130131
    131         return null;
    132     }
     132        return null;
     133    }
    133134
    134     /**
    135      * Get the credited person/entity's name
    136      *
    137      * @return string|null
    138      */
    139     public function get_name()
    140     {
    141         if ($this->name !== null)
    142         {
    143             return $this->name;
    144         }
     135    /**
     136     * Get the credited person/entity's name
     137     *
     138     * @return string|null
     139     */
     140    public function get_name()
     141    {
     142        if ($this->name !== null) {
     143            return $this->name;
     144        }
    145145
    146         return null;
    147     }
     146        return null;
     147    }
    148148}
     149
     150class_alias('SimplePie\Credit', 'SimplePie_Credit');
  • trunk/src/wp-includes/SimplePie/src/Enclosure.php

    r59140 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie;
     46
    4447/**
    4548 * Handles everything related to enclosures (including Media RSS and iTunes RSS)
    4649 *
    47  * Used by {@see SimplePie_Item::get_enclosure()} and {@see SimplePie_Item::get_enclosures()}
     50 * Used by {@see \SimplePie\Item::get_enclosure()} and {@see \SimplePie\Item::get_enclosures()}
    4851 *
    49  * This class can be overloaded with {@see SimplePie::set_enclosure_class()}
     52 * This class can be overloaded with {@see \SimplePie\SimplePie::set_enclosure_class()}
    5053 *
    5154 * @package SimplePie
    5255 * @subpackage API
    5356 */
    54 class SimplePie_Enclosure
     57class Enclosure
    5558{
    56     /**
    57      * @var string
    58      * @see get_bitrate()
    59      */
    60     var $bitrate;
    61 
    62     /**
    63      * @var array
    64      * @see get_captions()
    65      */
    66     var $captions;
    67 
    68     /**
    69      * @var array
    70      * @see get_categories()
    71      */
    72     var $categories;
    73 
    74     /**
    75      * @var int
    76      * @see get_channels()
    77      */
    78     var $channels;
    79 
    80     /**
    81      * @var SimplePie_Copyright
    82      * @see get_copyright()
    83      */
    84     var $copyright;
    85 
    86     /**
    87      * @var array
    88      * @see get_credits()
    89      */
    90     var $credits;
    91 
    92     /**
    93      * @var string
    94      * @see get_description()
    95      */
    96     var $description;
    97 
    98     /**
    99      * @var int
    100      * @see get_duration()
    101      */
    102     var $duration;
    103 
    104     /**
    105      * @var string
    106      * @see get_expression()
    107      */
    108     var $expression;
    109 
    110     /**
    111      * @var string
    112      * @see get_framerate()
    113      */
    114     var $framerate;
    115 
    116     /**
    117      * @var string
    118      * @see get_handler()
    119      */
    120     var $handler;
    121 
    122     /**
    123      * @var array
    124      * @see get_hashes()
    125      */
    126     var $hashes;
    127 
    128     /**
    129      * @var string
    130      * @see get_height()
    131      */
    132     var $height;
    133 
    134     /**
    135      * @deprecated
    136      * @var null
    137      */
    138     var $javascript;
    139 
    140     /**
    141      * @var array
    142      * @see get_keywords()
    143      */
    144     var $keywords;
    145 
    146     /**
    147      * @var string
    148      * @see get_language()
    149      */
    150     var $lang;
    151 
    152     /**
    153      * @var string
    154      * @see get_length()
    155      */
    156     var $length;
    157 
    158     /**
    159      * @var string
    160      * @see get_link()
    161      */
    162     var $link;
    163 
    164     /**
    165      * @var string
    166      * @see get_medium()
    167      */
    168     var $medium;
    169 
    170     /**
    171      * @var string
    172      * @see get_player()
    173      */
    174     var $player;
    175 
    176     /**
    177      * @var array
    178      * @see get_ratings()
    179      */
    180     var $ratings;
    181 
    182     /**
    183      * @var array
    184      * @see get_restrictions()
    185      */
    186     var $restrictions;
    187 
    188     /**
    189      * @var string
    190      * @see get_sampling_rate()
    191      */
    192     var $samplingrate;
    193 
    194     /**
    195      * @var array
    196      * @see get_thumbnails()
    197      */
    198     var $thumbnails;
    199 
    200     /**
    201      * @var string
    202      * @see get_title()
    203      */
    204     var $title;
    205 
    206     /**
    207      * @var string
    208      * @see get_type()
    209      */
    210     var $type;
    211 
    212     /**
    213      * @var string
    214      * @see get_width()
    215      */
    216     var $width;
    217 
    218     /**
    219      * Constructor, used to input the data
    220      *
    221      * For documentation on all the parameters, see the corresponding
    222      * properties and their accessors
    223      *
    224      * @uses idna_convert If available, this will convert an IDN
    225      */
    226     public function __construct($link = null, $type = null, $length = null, $javascript = null, $bitrate = null, $captions = null, $categories = null, $channels = null, $copyright = null, $credits = null, $description = null, $duration = null, $expression = null, $framerate = null, $hashes = null, $height = null, $keywords = null, $lang = null, $medium = null, $player = null, $ratings = null, $restrictions = null, $samplingrate = null, $thumbnails = null, $title = null, $width = null)
    227     {
    228         $this->bitrate = $bitrate;
    229         $this->captions = $captions;
    230         $this->categories = $categories;
    231         $this->channels = $channels;
    232         $this->copyright = $copyright;
    233         $this->credits = $credits;
    234         $this->description = $description;
    235         $this->duration = $duration;
    236         $this->expression = $expression;
    237         $this->framerate = $framerate;
    238         $this->hashes = $hashes;
    239         $this->height = $height;
    240         $this->keywords = $keywords;
    241         $this->lang = $lang;
    242         $this->length = $length;
    243         $this->link = $link;
    244         $this->medium = $medium;
    245         $this->player = $player;
    246         $this->ratings = $ratings;
    247         $this->restrictions = $restrictions;
    248         $this->samplingrate = $samplingrate;
    249         $this->thumbnails = $thumbnails;
    250         $this->title = $title;
    251         $this->type = $type;
    252         $this->width = $width;
    253 
    254         if (class_exists('idna_convert'))
    255         {
    256             $idn = new idna_convert();
    257             $parsed = SimplePie_Misc::parse_url($link);
    258             $this->link = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']);
    259         }
    260         $this->handler = $this->get_handler(); // Needs to load last
    261     }
    262 
    263     /**
    264      * String-ified version
    265      *
    266      * @return string
    267      */
    268     public function __toString()
    269     {
    270         // There is no $this->data here
    271         return md5(serialize($this));
    272     }
    273 
    274     /**
    275      * Get the bitrate
    276      *
    277      * @return string|null
    278      */
    279     public function get_bitrate()
    280     {
    281         if ($this->bitrate !== null)
    282         {
    283             return $this->bitrate;
    284         }
    285 
    286         return null;
    287     }
    288 
    289     /**
    290      * Get a single caption
    291      *
    292      * @param int $key
    293      * @return SimplePie_Caption|null
    294      */
    295     public function get_caption($key = 0)
    296     {
    297         $captions = $this->get_captions();
    298         if (isset($captions[$key]))
    299         {
    300             return $captions[$key];
    301         }
    302 
    303         return null;
    304     }
    305 
    306     /**
    307      * Get all captions
    308      *
    309      * @return array|null Array of {@see SimplePie_Caption} objects
    310      */
    311     public function get_captions()
    312     {
    313         if ($this->captions !== null)
    314         {
    315             return $this->captions;
    316         }
    317 
    318         return null;
    319     }
    320 
    321     /**
    322      * Get a single category
    323      *
    324      * @param int $key
    325      * @return SimplePie_Category|null
    326      */
    327     public function get_category($key = 0)
    328     {
    329         $categories = $this->get_categories();
    330         if (isset($categories[$key]))
    331         {
    332             return $categories[$key];
    333         }
    334 
    335         return null;
    336     }
    337 
    338     /**
    339      * Get all categories
    340      *
    341      * @return array|null Array of {@see SimplePie_Category} objects
    342      */
    343     public function get_categories()
    344     {
    345         if ($this->categories !== null)
    346         {
    347             return $this->categories;
    348         }
    349 
    350         return null;
    351     }
    352 
    353     /**
    354      * Get the number of audio channels
    355      *
    356      * @return int|null
    357      */
    358     public function get_channels()
    359     {
    360         if ($this->channels !== null)
    361         {
    362             return $this->channels;
    363         }
    364 
    365         return null;
    366     }
    367 
    368     /**
    369      * Get the copyright information
    370      *
    371      * @return SimplePie_Copyright|null
    372      */
    373     public function get_copyright()
    374     {
    375         if ($this->copyright !== null)
    376         {
    377             return $this->copyright;
    378         }
    379 
    380         return null;
    381     }
    382 
    383     /**
    384      * Get a single credit
    385      *
    386      * @param int $key
    387      * @return SimplePie_Credit|null
    388      */
    389     public function get_credit($key = 0)
    390     {
    391         $credits = $this->get_credits();
    392         if (isset($credits[$key]))
    393         {
    394             return $credits[$key];
    395         }
    396 
    397         return null;
    398     }
    399 
    400     /**
    401      * Get all credits
    402      *
    403      * @return array|null Array of {@see SimplePie_Credit} objects
    404      */
    405     public function get_credits()
    406     {
    407         if ($this->credits !== null)
    408         {
    409             return $this->credits;
    410         }
    411 
    412         return null;
    413     }
    414 
    415     /**
    416      * Get the description of the enclosure
    417      *
    418      * @return string|null
    419      */
    420     public function get_description()
    421     {
    422         if ($this->description !== null)
    423         {
    424             return $this->description;
    425         }
    426 
    427         return null;
    428     }
    429 
    430     /**
    431      * Get the duration of the enclosure
    432      *
    433      * @param bool $convert Convert seconds into hh:mm:ss
    434      * @return string|int|null 'hh:mm:ss' string if `$convert` was specified, otherwise integer (or null if none found)
    435      */
    436     public function get_duration($convert = false)
    437     {
    438         if ($this->duration !== null)
    439         {
    440             if ($convert)
    441             {
    442                 $time = SimplePie_Misc::time_hms($this->duration);
    443                 return $time;
    444             }
    445 
    446             return $this->duration;
    447         }
    448 
    449         return null;
    450     }
    451 
    452     /**
    453      * Get the expression
    454      *
    455      * @return string Probably one of 'sample', 'full', 'nonstop', 'clip'. Defaults to 'full'
    456      */
    457     public function get_expression()
    458     {
    459         if ($this->expression !== null)
    460         {
    461             return $this->expression;
    462         }
    463 
    464         return 'full';
    465     }
    466 
    467     /**
    468      * Get the file extension
    469      *
    470      * @return string|null
    471      */
    472     public function get_extension()
    473     {
    474         if ($this->link !== null)
    475         {
    476             $url = SimplePie_Misc::parse_url($this->link);
    477             if ($url['path'] !== '')
    478             {
    479                 return pathinfo($url['path'], PATHINFO_EXTENSION);
    480             }
    481         }
    482         return null;
    483     }
    484 
    485     /**
    486      * Get the framerate (in frames-per-second)
    487      *
    488      * @return string|null
    489      */
    490     public function get_framerate()
    491     {
    492         if ($this->framerate !== null)
    493         {
    494             return $this->framerate;
    495         }
    496 
    497         return null;
    498     }
    499 
    500     /**
    501      * Get the preferred handler
    502      *
    503      * @return string|null One of 'flash', 'fmedia', 'quicktime', 'wmedia', 'mp3'
    504      */
    505     public function get_handler()
    506     {
    507         return $this->get_real_type(true);
    508     }
    509 
    510     /**
    511      * Get a single hash
    512      *
    513      * @link http://www.rssboard.org/media-rss#media-hash
    514      * @param int $key
    515      * @return string|null Hash as per `media:hash`, prefixed with "$algo:"
    516      */
    517     public function get_hash($key = 0)
    518     {
    519         $hashes = $this->get_hashes();
    520         if (isset($hashes[$key]))
    521         {
    522             return $hashes[$key];
    523         }
    524 
    525         return null;
    526     }
    527 
    528     /**
    529      * Get all credits
    530      *
    531      * @return array|null Array of strings, see {@see get_hash()}
    532      */
    533     public function get_hashes()
    534     {
    535         if ($this->hashes !== null)
    536         {
    537             return $this->hashes;
    538         }
    539 
    540         return null;
    541     }
    542 
    543     /**
    544      * Get the height
    545      *
    546      * @return string|null
    547      */
    548     public function get_height()
    549     {
    550         if ($this->height !== null)
    551         {
    552             return $this->height;
    553         }
    554 
    555         return null;
    556     }
    557 
    558     /**
    559      * Get the language
    560      *
    561      * @link http://tools.ietf.org/html/rfc3066
    562      * @return string|null Language code as per RFC 3066
    563      */
    564     public function get_language()
    565     {
    566         if ($this->lang !== null)
    567         {
    568             return $this->lang;
    569         }
    570 
    571         return null;
    572     }
    573 
    574     /**
    575      * Get a single keyword
    576      *
    577      * @param int $key
    578      * @return string|null
    579      */
    580     public function get_keyword($key = 0)
    581     {
    582         $keywords = $this->get_keywords();
    583         if (isset($keywords[$key]))
    584         {
    585             return $keywords[$key];
    586         }
    587 
    588         return null;
    589     }
    590 
    591     /**
    592      * Get all keywords
    593      *
    594      * @return array|null Array of strings
    595      */
    596     public function get_keywords()
    597     {
    598         if ($this->keywords !== null)
    599         {
    600             return $this->keywords;
    601         }
    602 
    603         return null;
    604     }
    605 
    606     /**
    607      * Get length
    608      *
    609      * @return float Length in bytes
    610      */
    611     public function get_length()
    612     {
    613         if ($this->length !== null)
    614         {
    615             return $this->length;
    616         }
    617 
    618         return null;
    619     }
    620 
    621     /**
    622      * Get the URL
    623      *
    624      * @return string|null
    625      */
    626     public function get_link()
    627     {
    628         if ($this->link !== null)
    629         {
    630             return urldecode($this->link);
    631         }
    632 
    633         return null;
    634     }
    635 
    636     /**
    637      * Get the medium
    638      *
    639      * @link http://www.rssboard.org/media-rss#media-content
    640      * @return string|null Should be one of 'image', 'audio', 'video', 'document', 'executable'
    641      */
    642     public function get_medium()
    643     {
    644         if ($this->medium !== null)
    645         {
    646             return $this->medium;
    647         }
    648 
    649         return null;
    650     }
    651 
    652     /**
    653      * Get the player URL
    654      *
    655      * Typically the same as {@see get_permalink()}
    656      * @return string|null Player URL
    657      */
    658     public function get_player()
    659     {
    660         if ($this->player !== null)
    661         {
    662             return $this->player;
    663         }
    664 
    665         return null;
    666     }
    667 
    668     /**
    669      * Get a single rating
    670      *
    671      * @param int $key
    672      * @return SimplePie_Rating|null
    673      */
    674     public function get_rating($key = 0)
    675     {
    676         $ratings = $this->get_ratings();
    677         if (isset($ratings[$key]))
    678         {
    679             return $ratings[$key];
    680         }
    681 
    682         return null;
    683     }
    684 
    685     /**
    686      * Get all ratings
    687      *
    688      * @return array|null Array of {@see SimplePie_Rating} objects
    689      */
    690     public function get_ratings()
    691     {
    692         if ($this->ratings !== null)
    693         {
    694             return $this->ratings;
    695         }
    696 
    697         return null;
    698     }
    699 
    700     /**
    701      * Get a single restriction
    702      *
    703      * @param int $key
    704      * @return SimplePie_Restriction|null
    705      */
    706     public function get_restriction($key = 0)
    707     {
    708         $restrictions = $this->get_restrictions();
    709         if (isset($restrictions[$key]))
    710         {
    711             return $restrictions[$key];
    712         }
    713 
    714         return null;
    715     }
    716 
    717     /**
    718      * Get all restrictions
    719      *
    720      * @return array|null Array of {@see SimplePie_Restriction} objects
    721      */
    722     public function get_restrictions()
    723     {
    724         if ($this->restrictions !== null)
    725         {
    726             return $this->restrictions;
    727         }
    728 
    729         return null;
    730     }
    731 
    732     /**
    733      * Get the sampling rate (in kHz)
    734      *
    735      * @return string|null
    736      */
    737     public function get_sampling_rate()
    738     {
    739         if ($this->samplingrate !== null)
    740         {
    741             return $this->samplingrate;
    742         }
    743 
    744         return null;
    745     }
    746 
    747     /**
    748      * Get the file size (in MiB)
    749      *
    750      * @return float|null File size in mebibytes (1048 bytes)
    751      */
    752     public function get_size()
    753     {
    754         $length = $this->get_length();
    755         if ($length !== null)
    756         {
    757             return round($length/1048576, 2);
    758         }
    759 
    760         return null;
    761     }
    762 
    763     /**
    764      * Get a single thumbnail
    765      *
    766      * @param int $key
    767      * @return string|null Thumbnail URL
    768      */
    769     public function get_thumbnail($key = 0)
    770     {
    771         $thumbnails = $this->get_thumbnails();
    772         if (isset($thumbnails[$key]))
    773         {
    774             return $thumbnails[$key];
    775         }
    776 
    777         return null;
    778     }
    779 
    780     /**
    781      * Get all thumbnails
    782      *
    783      * @return array|null Array of thumbnail URLs
    784      */
    785     public function get_thumbnails()
    786     {
    787         if ($this->thumbnails !== null)
    788         {
    789             return $this->thumbnails;
    790         }
    791 
    792         return null;
    793     }
    794 
    795     /**
    796      * Get the title
    797      *
    798      * @return string|null
    799      */
    800     public function get_title()
    801     {
    802         if ($this->title !== null)
    803         {
    804             return $this->title;
    805         }
    806 
    807         return null;
    808     }
    809 
    810     /**
    811      * Get mimetype of the enclosure
    812      *
    813      * @see get_real_type()
    814      * @return string|null MIME type
    815      */
    816     public function get_type()
    817     {
    818         if ($this->type !== null)
    819         {
    820             return $this->type;
    821         }
    822 
    823         return null;
    824     }
    825 
    826     /**
    827      * Get the width
    828      *
    829      * @return string|null
    830      */
    831     public function get_width()
    832     {
    833         if ($this->width !== null)
    834         {
    835             return $this->width;
    836         }
    837 
    838         return null;
    839     }
    840 
    841     /**
    842      * Embed the enclosure using `<embed>`
    843      *
    844      * @deprecated Use the second parameter to {@see embed} instead
    845      *
    846      * @param array|string $options See first paramter to {@see embed}
    847      * @return string HTML string to output
    848      */
    849     public function native_embed($options='')
    850     {
    851         return $this->embed($options, true);
    852     }
    853 
    854     /**
    855      * Embed the enclosure using Javascript
    856      *
    857      * `$options` is an array or comma-separated key:value string, with the
    858      * following properties:
    859      *
    860      * - `alt` (string): Alternate content for when an end-user does not have
    861      *    the appropriate handler installed or when a file type is
    862      *    unsupported. Can be any text or HTML. Defaults to blank.
    863      * - `altclass` (string): If a file type is unsupported, the end-user will
    864      *    see the alt text (above) linked directly to the content. That link
    865      *    will have this value as its class name. Defaults to blank.
    866      * - `audio` (string): This is an image that should be used as a
    867      *    placeholder for audio files before they're loaded (QuickTime-only).
    868      *    Can be any relative or absolute URL. Defaults to blank.
    869      * - `bgcolor` (string): The background color for the media, if not
    870      *    already transparent. Defaults to `#ffffff`.
    871      * - `height` (integer): The height of the embedded media. Accepts any
    872      *    numeric pixel value (such as `360`) or `auto`. Defaults to `auto`,
    873      *    and it is recommended that you use this default.
    874      * - `loop` (boolean): Do you want the media to loop when it's done?
    875      *    Defaults to `false`.
    876      * - `mediaplayer` (string): The location of the included
    877      *    `mediaplayer.swf` file. This allows for the playback of Flash Video
    878      *    (`.flv`) files, and is the default handler for non-Odeo MP3's.
    879      *    Defaults to blank.
    880      * - `video` (string): This is an image that should be used as a
    881      *    placeholder for video files before they're loaded (QuickTime-only).
    882      *    Can be any relative or absolute URL. Defaults to blank.
    883      * - `width` (integer): The width of the embedded media. Accepts any
    884      *    numeric pixel value (such as `480`) or `auto`. Defaults to `auto`,
    885      *    and it is recommended that you use this default.
    886      * - `widescreen` (boolean): Is the enclosure widescreen or standard?
    887      *    This applies only to video enclosures, and will automatically resize
    888      *    the content appropriately.  Defaults to `false`, implying 4:3 mode.
    889      *
    890      * Note: Non-widescreen (4:3) mode with `width` and `height` set to `auto`
    891      * will default to 480x360 video resolution.  Widescreen (16:9) mode with
    892      * `width` and `height` set to `auto` will default to 480x270 video resolution.
    893      *
    894      * @todo If the dimensions for media:content are defined, use them when width/height are set to 'auto'.
    895      * @param array|string $options Comma-separated key:value list, or array
    896      * @param bool $native Use `<embed>`
    897      * @return string HTML string to output
    898      */
    899     public function embed($options = '', $native = false)
    900     {
    901         // Set up defaults
    902         $audio = '';
    903         $video = '';
    904         $alt = '';
    905         $altclass = '';
    906         $loop = 'false';
    907         $width = 'auto';
    908         $height = 'auto';
    909         $bgcolor = '#ffffff';
    910         $mediaplayer = '';
    911         $widescreen = false;
    912         $handler = $this->get_handler();
    913         $type = $this->get_real_type();
    914 
    915         // Process options and reassign values as necessary
    916         if (is_array($options))
    917         {
    918             extract($options);
    919         }
    920         else
    921         {
    922             $options = explode(',', $options);
    923             foreach($options as $option)
    924             {
    925                 $opt = explode(':', $option, 2);
    926                 if (isset($opt[0], $opt[1]))
    927                 {
    928                     $opt[0] = trim($opt[0]);
    929                     $opt[1] = trim($opt[1]);
    930                     switch ($opt[0])
    931                     {
    932                         case 'audio':
    933                             $audio = $opt[1];
    934                             break;
    935 
    936                         case 'video':
    937                             $video = $opt[1];
    938                             break;
    939 
    940                         case 'alt':
    941                             $alt = $opt[1];
    942                             break;
    943 
    944                         case 'altclass':
    945                             $altclass = $opt[1];
    946                             break;
    947 
    948                         case 'loop':
    949                             $loop = $opt[1];
    950                             break;
    951 
    952                         case 'width':
    953                             $width = $opt[1];
    954                             break;
    955 
    956                         case 'height':
    957                             $height = $opt[1];
    958                             break;
    959 
    960                         case 'bgcolor':
    961                             $bgcolor = $opt[1];
    962                             break;
    963 
    964                         case 'mediaplayer':
    965                             $mediaplayer = $opt[1];
    966                             break;
    967 
    968                         case 'widescreen':
    969                             $widescreen = $opt[1];
    970                             break;
    971                     }
    972                 }
    973             }
    974         }
    975 
    976         $mime = explode('/', $type, 2);
    977         $mime = $mime[0];
    978 
    979         // Process values for 'auto'
    980         if ($width === 'auto')
    981         {
    982             if ($mime === 'video')
    983             {
    984                 if ($height === 'auto')
    985                 {
    986                     $width = 480;
    987                 }
    988                 elseif ($widescreen)
    989                 {
    990                     $width = round((intval($height)/9)*16);
    991                 }
    992                 else
    993                 {
    994                     $width = round((intval($height)/3)*4);
    995                 }
    996             }
    997             else
    998             {
    999                 $width = '100%';
    1000             }
    1001         }
    1002 
    1003         if ($height === 'auto')
    1004         {
    1005             if ($mime === 'audio')
    1006             {
    1007                 $height = 0;
    1008             }
    1009             elseif ($mime === 'video')
    1010             {
    1011                 if ($width === 'auto')
    1012                 {
    1013                     if ($widescreen)
    1014                     {
    1015                         $height = 270;
    1016                     }
    1017                     else
    1018                     {
    1019                         $height = 360;
    1020                     }
    1021                 }
    1022                 elseif ($widescreen)
    1023                 {
    1024                     $height = round((intval($width)/16)*9);
    1025                 }
    1026                 else
    1027                 {
    1028                     $height = round((intval($width)/4)*3);
    1029                 }
    1030             }
    1031             else
    1032             {
    1033                 $height = 376;
    1034             }
    1035         }
    1036         elseif ($mime === 'audio')
    1037         {
    1038             $height = 0;
    1039         }
    1040 
    1041         // Set proper placeholder value
    1042         if ($mime === 'audio')
    1043         {
    1044             $placeholder = $audio;
    1045         }
    1046         elseif ($mime === 'video')
    1047         {
    1048             $placeholder = $video;
    1049         }
    1050 
    1051         $embed = '';
    1052 
    1053         // Flash
    1054         if ($handler === 'flash')
    1055         {
    1056             if ($native)
    1057             {
    1058                 $embed .= "<embed src=\"" . $this->get_link() . "\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"$type\" quality=\"high\" width=\"$width\" height=\"$height\" bgcolor=\"$bgcolor\" loop=\"$loop\"></embed>";
    1059             }
    1060             else
    1061             {
    1062                 $embed .= "<script type='text/javascript'>embed_flash('$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$loop', '$type');</script>";
    1063             }
    1064         }
    1065 
    1066         // Flash Media Player file types.
    1067         // Preferred handler for MP3 file types.
    1068         elseif ($handler === 'fmedia' || ($handler === 'mp3' && $mediaplayer !== ''))
    1069         {
    1070             $height += 20;
    1071             if ($native)
    1072             {
    1073                 $embed .= "<embed src=\"$mediaplayer\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"application/x-shockwave-flash\" quality=\"high\" width=\"$width\" height=\"$height\" wmode=\"transparent\" flashvars=\"file=" . rawurlencode($this->get_link().'?file_extension=.'.$this->get_extension()) . "&autostart=false&repeat=$loop&showdigits=true&showfsbutton=false\"></embed>";
    1074             }
    1075             else
    1076             {
    1077                 $embed .= "<script type='text/javascript'>embed_flv('$width', '$height', '" . rawurlencode($this->get_link().'?file_extension=.'.$this->get_extension()) . "', '$placeholder', '$loop', '$mediaplayer');</script>";
    1078             }
    1079         }
    1080 
    1081         // QuickTime 7 file types.  Need to test with QuickTime 6.
    1082         // Only handle MP3's if the Flash Media Player is not present.
    1083         elseif ($handler === 'quicktime' || ($handler === 'mp3' && $mediaplayer === ''))
    1084         {
    1085             $height += 16;
    1086             if ($native)
    1087             {
    1088                 if ($placeholder !== '')
    1089                 {
    1090                     $embed .= "<embed type=\"$type\" style=\"cursor:hand; cursor:pointer;\" href=\"" . $this->get_link() . "\" src=\"$placeholder\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"false\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\"></embed>";
    1091                 }
    1092                 else
    1093                 {
    1094                     $embed .= "<embed type=\"$type\" style=\"cursor:hand; cursor:pointer;\" src=\"" . $this->get_link() . "\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"true\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\"></embed>";
    1095                 }
    1096             }
    1097             else
    1098             {
    1099                 $embed .= "<script type='text/javascript'>embed_quicktime('$type', '$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$placeholder', '$loop');</script>";
    1100             }
    1101         }
    1102 
    1103         // Windows Media
    1104         elseif ($handler === 'wmedia')
    1105         {
    1106             $height += 45;
    1107             if ($native)
    1108             {
    1109                 $embed .= "<embed type=\"application/x-mplayer2\" src=\"" . $this->get_link() . "\" autosize=\"1\" width=\"$width\" height=\"$height\" showcontrols=\"1\" showstatusbar=\"0\" showdisplay=\"0\" autostart=\"0\"></embed>";
    1110             }
    1111             else
    1112             {
    1113                 $embed .= "<script type='text/javascript'>embed_wmedia('$width', '$height', '" . $this->get_link() . "');</script>";
    1114             }
    1115         }
    1116 
    1117         // Everything else
    1118         else $embed .= '<a href="' . $this->get_link() . '" class="' . $altclass . '">' . $alt . '</a>';
    1119 
    1120         return $embed;
    1121     }
    1122 
    1123     /**
    1124      * Get the real media type
    1125      *
    1126      * Often, feeds lie to us, necessitating a bit of deeper inspection. This
    1127      * converts types to their canonical representations based on the file
    1128      * extension
    1129      *
    1130      * @see get_type()
    1131      * @param bool $find_handler Internal use only, use {@see get_handler()} instead
    1132      * @return string MIME type
    1133      */
    1134     public function get_real_type($find_handler = false)
    1135     {
    1136         // Mime-types by handler.
    1137         $types_flash = array('application/x-shockwave-flash', 'application/futuresplash'); // Flash
    1138         $types_fmedia = array('video/flv', 'video/x-flv','flv-application/octet-stream'); // Flash Media Player
    1139         $types_quicktime = array('audio/3gpp', 'audio/3gpp2', 'audio/aac', 'audio/x-aac', 'audio/aiff', 'audio/x-aiff', 'audio/mid', 'audio/midi', 'audio/x-midi', 'audio/mp4', 'audio/m4a', 'audio/x-m4a', 'audio/wav', 'audio/x-wav', 'video/3gpp', 'video/3gpp2', 'video/m4v', 'video/x-m4v', 'video/mp4', 'video/mpeg', 'video/x-mpeg', 'video/quicktime', 'video/sd-video'); // QuickTime
    1140         $types_wmedia = array('application/asx', 'application/x-mplayer2', 'audio/x-ms-wma', 'audio/x-ms-wax', 'video/x-ms-asf-plugin', 'video/x-ms-asf', 'video/x-ms-wm', 'video/x-ms-wmv', 'video/x-ms-wvx'); // Windows Media
    1141         $types_mp3 = array('audio/mp3', 'audio/x-mp3', 'audio/mpeg', 'audio/x-mpeg'); // MP3
    1142 
    1143         if ($this->get_type() !== null)
    1144         {
    1145             $type = strtolower($this->type);
    1146         }
    1147         else
    1148         {
    1149             $type = null;
    1150         }
    1151 
    1152         // If we encounter an unsupported mime-type, check the file extension and guess intelligently.
    1153         if (!in_array($type, array_merge($types_flash, $types_fmedia, $types_quicktime, $types_wmedia, $types_mp3)))
    1154         {
    1155             $extension = $this->get_extension();
    1156             if ($extension === null) {
    1157                 return null;
    1158             }
    1159 
    1160             switch (strtolower($extension))
    1161             {
    1162                 // Audio mime-types
    1163                 case 'aac':
    1164                 case 'adts':
    1165                     $type = 'audio/acc';
    1166                     break;
    1167 
    1168                 case 'aif':
    1169                 case 'aifc':
    1170                 case 'aiff':
    1171                 case 'cdda':
    1172                     $type = 'audio/aiff';
    1173                     break;
    1174 
    1175                 case 'bwf':
    1176                     $type = 'audio/wav';
    1177                     break;
    1178 
    1179                 case 'kar':
    1180                 case 'mid':
    1181                 case 'midi':
    1182                 case 'smf':
    1183                     $type = 'audio/midi';
    1184                     break;
    1185 
    1186                 case 'm4a':
    1187                     $type = 'audio/x-m4a';
    1188                     break;
    1189 
    1190                 case 'mp3':
    1191                 case 'swa':
    1192                     $type = 'audio/mp3';
    1193                     break;
    1194 
    1195                 case 'wav':
    1196                     $type = 'audio/wav';
    1197                     break;
    1198 
    1199                 case 'wax':
    1200                     $type = 'audio/x-ms-wax';
    1201                     break;
    1202 
    1203                 case 'wma':
    1204                     $type = 'audio/x-ms-wma';
    1205                     break;
    1206 
    1207                 // Video mime-types
    1208                 case '3gp':
    1209                 case '3gpp':
    1210                     $type = 'video/3gpp';
    1211                     break;
    1212 
    1213                 case '3g2':
    1214                 case '3gp2':
    1215                     $type = 'video/3gpp2';
    1216                     break;
    1217 
    1218                 case 'asf':
    1219                     $type = 'video/x-ms-asf';
    1220                     break;
    1221 
    1222                 case 'flv':
    1223                     $type = 'video/x-flv';
    1224                     break;
    1225 
    1226                 case 'm1a':
    1227                 case 'm1s':
    1228                 case 'm1v':
    1229                 case 'm15':
    1230                 case 'm75':
    1231                 case 'mp2':
    1232                 case 'mpa':
    1233                 case 'mpeg':
    1234                 case 'mpg':
    1235                 case 'mpm':
    1236                 case 'mpv':
    1237                     $type = 'video/mpeg';
    1238                     break;
    1239 
    1240                 case 'm4v':
    1241                     $type = 'video/x-m4v';
    1242                     break;
    1243 
    1244                 case 'mov':
    1245                 case 'qt':
    1246                     $type = 'video/quicktime';
    1247                     break;
    1248 
    1249                 case 'mp4':
    1250                 case 'mpg4':
    1251                     $type = 'video/mp4';
    1252                     break;
    1253 
    1254                 case 'sdv':
    1255                     $type = 'video/sd-video';
    1256                     break;
    1257 
    1258                 case 'wm':
    1259                     $type = 'video/x-ms-wm';
    1260                     break;
    1261 
    1262                 case 'wmv':
    1263                     $type = 'video/x-ms-wmv';
    1264                     break;
    1265 
    1266                 case 'wvx':
    1267                     $type = 'video/x-ms-wvx';
    1268                     break;
    1269 
    1270                 // Flash mime-types
    1271                 case 'spl':
    1272                     $type = 'application/futuresplash';
    1273                     break;
    1274 
    1275                 case 'swf':
    1276                     $type = 'application/x-shockwave-flash';
    1277                     break;
    1278             }
    1279         }
    1280 
    1281         if ($find_handler)
    1282         {
    1283             if (in_array($type, $types_flash))
    1284             {
    1285                 return 'flash';
    1286             }
    1287             elseif (in_array($type, $types_fmedia))
    1288             {
    1289                 return 'fmedia';
    1290             }
    1291             elseif (in_array($type, $types_quicktime))
    1292             {
    1293                 return 'quicktime';
    1294             }
    1295             elseif (in_array($type, $types_wmedia))
    1296             {
    1297                 return 'wmedia';
    1298             }
    1299             elseif (in_array($type, $types_mp3))
    1300             {
    1301                 return 'mp3';
    1302             }
    1303 
    1304             return null;
    1305         }
    1306 
    1307         return $type;
    1308     }
     59    /**
     60     * @var string
     61     * @see get_bitrate()
     62     */
     63    public $bitrate;
     64
     65    /**
     66     * @var array
     67     * @see get_captions()
     68     */
     69    public $captions;
     70
     71    /**
     72     * @var array
     73     * @see get_categories()
     74     */
     75    public $categories;
     76
     77    /**
     78     * @var int
     79     * @see get_channels()
     80     */
     81    public $channels;
     82
     83    /**
     84     * @var \SimplePie\Copyright
     85     * @see get_copyright()
     86     */
     87    public $copyright;
     88
     89    /**
     90     * @var array
     91     * @see get_credits()
     92     */
     93    public $credits;
     94
     95    /**
     96     * @var string
     97     * @see get_description()
     98     */
     99    public $description;
     100
     101    /**
     102     * @var int
     103     * @see get_duration()
     104     */
     105    public $duration;
     106
     107    /**
     108     * @var string
     109     * @see get_expression()
     110     */
     111    public $expression;
     112
     113    /**
     114     * @var string
     115     * @see get_framerate()
     116     */
     117    public $framerate;
     118
     119    /**
     120     * @var string
     121     * @see get_handler()
     122     */
     123    public $handler;
     124
     125    /**
     126     * @var array
     127     * @see get_hashes()
     128     */
     129    public $hashes;
     130
     131    /**
     132     * @var string
     133     * @see get_height()
     134     */
     135    public $height;
     136
     137    /**
     138     * @deprecated
     139     * @var null
     140     */
     141    public $javascript;
     142
     143    /**
     144     * @var array
     145     * @see get_keywords()
     146     */
     147    public $keywords;
     148
     149    /**
     150     * @var string
     151     * @see get_language()
     152     */
     153    public $lang;
     154
     155    /**
     156     * @var string
     157     * @see get_length()
     158     */
     159    public $length;
     160
     161    /**
     162     * @var string
     163     * @see get_link()
     164     */
     165    public $link;
     166
     167    /**
     168     * @var string
     169     * @see get_medium()
     170     */
     171    public $medium;
     172
     173    /**
     174     * @var string
     175     * @see get_player()
     176     */
     177    public $player;
     178
     179    /**
     180     * @var array
     181     * @see get_ratings()
     182     */
     183    public $ratings;
     184
     185    /**
     186     * @var array
     187     * @see get_restrictions()
     188     */
     189    public $restrictions;
     190
     191    /**
     192     * @var string
     193     * @see get_sampling_rate()
     194     */
     195    public $samplingrate;
     196
     197    /**
     198     * @var array
     199     * @see get_thumbnails()
     200     */
     201    public $thumbnails;
     202
     203    /**
     204     * @var string
     205     * @see get_title()
     206     */
     207    public $title;
     208
     209    /**
     210     * @var string
     211     * @see get_type()
     212     */
     213    public $type;
     214
     215    /**
     216     * @var string
     217     * @see get_width()
     218     */
     219    public $width;
     220
     221    /**
     222     * Constructor, used to input the data
     223     *
     224     * For documentation on all the parameters, see the corresponding
     225     * properties and their accessors
     226     *
     227     * @uses idna_convert If available, this will convert an IDN
     228     */
     229    public function __construct($link = null, $type = null, $length = null, $javascript = null, $bitrate = null, $captions = null, $categories = null, $channels = null, $copyright = null, $credits = null, $description = null, $duration = null, $expression = null, $framerate = null, $hashes = null, $height = null, $keywords = null, $lang = null, $medium = null, $player = null, $ratings = null, $restrictions = null, $samplingrate = null, $thumbnails = null, $title = null, $width = null)
     230    {
     231        $this->bitrate = $bitrate;
     232        $this->captions = $captions;
     233        $this->categories = $categories;
     234        $this->channels = $channels;
     235        $this->copyright = $copyright;
     236        $this->credits = $credits;
     237        $this->description = $description;
     238        $this->duration = $duration;
     239        $this->expression = $expression;
     240        $this->framerate = $framerate;
     241        $this->hashes = $hashes;
     242        $this->height = $height;
     243        $this->keywords = $keywords;
     244        $this->lang = $lang;
     245        $this->length = $length;
     246        $this->link = $link;
     247        $this->medium = $medium;
     248        $this->player = $player;
     249        $this->ratings = $ratings;
     250        $this->restrictions = $restrictions;
     251        $this->samplingrate = $samplingrate;
     252        $this->thumbnails = $thumbnails;
     253        $this->title = $title;
     254        $this->type = $type;
     255        $this->width = $width;
     256
     257        if (class_exists('idna_convert')) {
     258            $idn = new \idna_convert();
     259            $parsed = \SimplePie\Misc::parse_url($link);
     260            $this->link = \SimplePie\Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']);
     261        }
     262        $this->handler = $this->get_handler(); // Needs to load last
     263    }
     264
     265    /**
     266     * String-ified version
     267     *
     268     * @return string
     269     */
     270    public function __toString()
     271    {
     272        // There is no $this->data here
     273        return md5(serialize($this));
     274    }
     275
     276    /**
     277     * Get the bitrate
     278     *
     279     * @return string|null
     280     */
     281    public function get_bitrate()
     282    {
     283        if ($this->bitrate !== null) {
     284            return $this->bitrate;
     285        }
     286
     287        return null;
     288    }
     289
     290    /**
     291     * Get a single caption
     292     *
     293     * @param int $key
     294     * @return \SimplePie\Caption|null
     295     */
     296    public function get_caption($key = 0)
     297    {
     298        $captions = $this->get_captions();
     299        if (isset($captions[$key])) {
     300            return $captions[$key];
     301        }
     302
     303        return null;
     304    }
     305
     306    /**
     307     * Get all captions
     308     *
     309     * @return array|null Array of {@see \SimplePie\Caption} objects
     310     */
     311    public function get_captions()
     312    {
     313        if ($this->captions !== null) {
     314            return $this->captions;
     315        }
     316
     317        return null;
     318    }
     319
     320    /**
     321     * Get a single category
     322     *
     323     * @param int $key
     324     * @return \SimplePie\Category|null
     325     */
     326    public function get_category($key = 0)
     327    {
     328        $categories = $this->get_categories();
     329        if (isset($categories[$key])) {
     330            return $categories[$key];
     331        }
     332
     333        return null;
     334    }
     335
     336    /**
     337     * Get all categories
     338     *
     339     * @return array|null Array of {@see \SimplePie\Category} objects
     340     */
     341    public function get_categories()
     342    {
     343        if ($this->categories !== null) {
     344            return $this->categories;
     345        }
     346
     347        return null;
     348    }
     349
     350    /**
     351     * Get the number of audio channels
     352     *
     353     * @return int|null
     354     */
     355    public function get_channels()
     356    {
     357        if ($this->channels !== null) {
     358            return $this->channels;
     359        }
     360
     361        return null;
     362    }
     363
     364    /**
     365     * Get the copyright information
     366     *
     367     * @return \SimplePie\Copyright|null
     368     */
     369    public function get_copyright()
     370    {
     371        if ($this->copyright !== null) {
     372            return $this->copyright;
     373        }
     374
     375        return null;
     376    }
     377
     378    /**
     379     * Get a single credit
     380     *
     381     * @param int $key
     382     * @return \SimplePie\Credit|null
     383     */
     384    public function get_credit($key = 0)
     385    {
     386        $credits = $this->get_credits();
     387        if (isset($credits[$key])) {
     388            return $credits[$key];
     389        }
     390
     391        return null;
     392    }
     393
     394    /**
     395     * Get all credits
     396     *
     397     * @return array|null Array of {@see \SimplePie\Credit} objects
     398     */
     399    public function get_credits()
     400    {
     401        if ($this->credits !== null) {
     402            return $this->credits;
     403        }
     404
     405        return null;
     406    }
     407
     408    /**
     409     * Get the description of the enclosure
     410     *
     411     * @return string|null
     412     */
     413    public function get_description()
     414    {
     415        if ($this->description !== null) {
     416            return $this->description;
     417        }
     418
     419        return null;
     420    }
     421
     422    /**
     423     * Get the duration of the enclosure
     424     *
     425     * @param bool $convert Convert seconds into hh:mm:ss
     426     * @return string|int|null 'hh:mm:ss' string if `$convert` was specified, otherwise integer (or null if none found)
     427     */
     428    public function get_duration($convert = false)
     429    {
     430        if ($this->duration !== null) {
     431            if ($convert) {
     432                $time = \SimplePie\Misc::time_hms($this->duration);
     433                return $time;
     434            }
     435
     436            return $this->duration;
     437        }
     438
     439        return null;
     440    }
     441
     442    /**
     443     * Get the expression
     444     *
     445     * @return string Probably one of 'sample', 'full', 'nonstop', 'clip'. Defaults to 'full'
     446     */
     447    public function get_expression()
     448    {
     449        if ($this->expression !== null) {
     450            return $this->expression;
     451        }
     452
     453        return 'full';
     454    }
     455
     456    /**
     457     * Get the file extension
     458     *
     459     * @return string|null
     460     */
     461    public function get_extension()
     462    {
     463        if ($this->link !== null) {
     464            $url = \SimplePie\Misc::parse_url($this->link);
     465            if ($url['path'] !== '') {
     466                return pathinfo($url['path'], PATHINFO_EXTENSION);
     467            }
     468        }
     469        return null;
     470    }
     471
     472    /**
     473     * Get the framerate (in frames-per-second)
     474     *
     475     * @return string|null
     476     */
     477    public function get_framerate()
     478    {
     479        if ($this->framerate !== null) {
     480            return $this->framerate;
     481        }
     482
     483        return null;
     484    }
     485
     486    /**
     487     * Get the preferred handler
     488     *
     489     * @return string|null One of 'flash', 'fmedia', 'quicktime', 'wmedia', 'mp3'
     490     */
     491    public function get_handler()
     492    {
     493        return $this->get_real_type(true);
     494    }
     495
     496    /**
     497     * Get a single hash
     498     *
     499     * @link http://www.rssboard.org/media-rss#media-hash
     500     * @param int $key
     501     * @return string|null Hash as per `media:hash`, prefixed with "$algo:"
     502     */
     503    public function get_hash($key = 0)
     504    {
     505        $hashes = $this->get_hashes();
     506        if (isset($hashes[$key])) {
     507            return $hashes[$key];
     508        }
     509
     510        return null;
     511    }
     512
     513    /**
     514     * Get all credits
     515     *
     516     * @return array|null Array of strings, see {@see get_hash()}
     517     */
     518    public function get_hashes()
     519    {
     520        if ($this->hashes !== null) {
     521            return $this->hashes;
     522        }
     523
     524        return null;
     525    }
     526
     527    /**
     528     * Get the height
     529     *
     530     * @return string|null
     531     */
     532    public function get_height()
     533    {
     534        if ($this->height !== null) {
     535            return $this->height;
     536        }
     537
     538        return null;
     539    }
     540
     541    /**
     542     * Get the language
     543     *
     544     * @link http://tools.ietf.org/html/rfc3066
     545     * @return string|null Language code as per RFC 3066
     546     */
     547    public function get_language()
     548    {
     549        if ($this->lang !== null) {
     550            return $this->lang;
     551        }
     552
     553        return null;
     554    }
     555
     556    /**
     557     * Get a single keyword
     558     *
     559     * @param int $key
     560     * @return string|null
     561     */
     562    public function get_keyword($key = 0)
     563    {
     564        $keywords = $this->get_keywords();
     565        if (isset($keywords[$key])) {
     566            return $keywords[$key];
     567        }
     568
     569        return null;
     570    }
     571
     572    /**
     573     * Get all keywords
     574     *
     575     * @return array|null Array of strings
     576     */
     577    public function get_keywords()
     578    {
     579        if ($this->keywords !== null) {
     580            return $this->keywords;
     581        }
     582
     583        return null;
     584    }
     585
     586    /**
     587     * Get length
     588     *
     589     * @return float Length in bytes
     590     */
     591    public function get_length()
     592    {
     593        if ($this->length !== null) {
     594            return $this->length;
     595        }
     596
     597        return null;
     598    }
     599
     600    /**
     601     * Get the URL
     602     *
     603     * @return string|null
     604     */
     605    public function get_link()
     606    {
     607        if ($this->link !== null) {
     608            return $this->link;
     609        }
     610
     611        return null;
     612    }
     613
     614    /**
     615     * Get the medium
     616     *
     617     * @link http://www.rssboard.org/media-rss#media-content
     618     * @return string|null Should be one of 'image', 'audio', 'video', 'document', 'executable'
     619     */
     620    public function get_medium()
     621    {
     622        if ($this->medium !== null) {
     623            return $this->medium;
     624        }
     625
     626        return null;
     627    }
     628
     629    /**
     630     * Get the player URL
     631     *
     632     * Typically the same as {@see get_permalink()}
     633     * @return string|null Player URL
     634     */
     635    public function get_player()
     636    {
     637        if ($this->player !== null) {
     638            return $this->player;
     639        }
     640
     641        return null;
     642    }
     643
     644    /**
     645     * Get a single rating
     646     *
     647     * @param int $key
     648     * @return \SimplePie\Rating|null
     649     */
     650    public function get_rating($key = 0)
     651    {
     652        $ratings = $this->get_ratings();
     653        if (isset($ratings[$key])) {
     654            return $ratings[$key];
     655        }
     656
     657        return null;
     658    }
     659
     660    /**
     661     * Get all ratings
     662     *
     663     * @return array|null Array of {@see \SimplePie\Rating} objects
     664     */
     665    public function get_ratings()
     666    {
     667        if ($this->ratings !== null) {
     668            return $this->ratings;
     669        }
     670
     671        return null;
     672    }
     673
     674    /**
     675     * Get a single restriction
     676     *
     677     * @param int $key
     678     * @return \SimplePie\Restriction|null
     679     */
     680    public function get_restriction($key = 0)
     681    {
     682        $restrictions = $this->get_restrictions();
     683        if (isset($restrictions[$key])) {
     684            return $restrictions[$key];
     685        }
     686
     687        return null;
     688    }
     689
     690    /**
     691     * Get all restrictions
     692     *
     693     * @return array|null Array of {@see \SimplePie\Restriction} objects
     694     */
     695    public function get_restrictions()
     696    {
     697        if ($this->restrictions !== null) {
     698            return $this->restrictions;
     699        }
     700
     701        return null;
     702    }
     703
     704    /**
     705     * Get the sampling rate (in kHz)
     706     *
     707     * @return string|null
     708     */
     709    public function get_sampling_rate()
     710    {
     711        if ($this->samplingrate !== null) {
     712            return $this->samplingrate;
     713        }
     714
     715        return null;
     716    }
     717
     718    /**
     719     * Get the file size (in MiB)
     720     *
     721     * @return float|null File size in mebibytes (1048 bytes)
     722     */
     723    public function get_size()
     724    {
     725        $length = $this->get_length();
     726        if ($length !== null) {
     727            return round($length / 1048576, 2);
     728        }
     729
     730        return null;
     731    }
     732
     733    /**
     734     * Get a single thumbnail
     735     *
     736     * @param int $key
     737     * @return string|null Thumbnail URL
     738     */
     739    public function get_thumbnail($key = 0)
     740    {
     741        $thumbnails = $this->get_thumbnails();
     742        if (isset($thumbnails[$key])) {
     743            return $thumbnails[$key];
     744        }
     745
     746        return null;
     747    }
     748
     749    /**
     750     * Get all thumbnails
     751     *
     752     * @return array|null Array of thumbnail URLs
     753     */
     754    public function get_thumbnails()
     755    {
     756        if ($this->thumbnails !== null) {
     757            return $this->thumbnails;
     758        }
     759
     760        return null;
     761    }
     762
     763    /**
     764     * Get the title
     765     *
     766     * @return string|null
     767     */
     768    public function get_title()
     769    {
     770        if ($this->title !== null) {
     771            return $this->title;
     772        }
     773
     774        return null;
     775    }
     776
     777    /**
     778     * Get mimetype of the enclosure
     779     *
     780     * @see get_real_type()
     781     * @return string|null MIME type
     782     */
     783    public function get_type()
     784    {
     785        if ($this->type !== null) {
     786            return $this->type;
     787        }
     788
     789        return null;
     790    }
     791
     792    /**
     793     * Get the width
     794     *
     795     * @return string|null
     796     */
     797    public function get_width()
     798    {
     799        if ($this->width !== null) {
     800            return $this->width;
     801        }
     802
     803        return null;
     804    }
     805
     806    /**
     807     * Embed the enclosure using `<embed>`
     808     *
     809     * @deprecated Use the second parameter to {@see embed} instead
     810     *
     811     * @param array|string $options See first parameter to {@see embed}
     812     * @return string HTML string to output
     813     */
     814    public function native_embed($options = '')
     815    {
     816        return $this->embed($options, true);
     817    }
     818
     819    /**
     820     * Embed the enclosure using Javascript
     821     *
     822     * `$options` is an array or comma-separated key:value string, with the
     823     * following properties:
     824     *
     825     * - `alt` (string): Alternate content for when an end-user does not have
     826     *    the appropriate handler installed or when a file type is
     827     *    unsupported. Can be any text or HTML. Defaults to blank.
     828     * - `altclass` (string): If a file type is unsupported, the end-user will
     829     *    see the alt text (above) linked directly to the content. That link
     830     *    will have this value as its class name. Defaults to blank.
     831     * - `audio` (string): This is an image that should be used as a
     832     *    placeholder for audio files before they're loaded (QuickTime-only).
     833     *    Can be any relative or absolute URL. Defaults to blank.
     834     * - `bgcolor` (string): The background color for the media, if not
     835     *    already transparent. Defaults to `#ffffff`.
     836     * - `height` (integer): The height of the embedded media. Accepts any
     837     *    numeric pixel value (such as `360`) or `auto`. Defaults to `auto`,
     838     *    and it is recommended that you use this default.
     839     * - `loop` (boolean): Do you want the media to loop when it's done?
     840     *    Defaults to `false`.
     841     * - `mediaplayer` (string): The location of the included
     842     *    `mediaplayer.swf` file. This allows for the playback of Flash Video
     843     *    (`.flv`) files, and is the default handler for non-Odeo MP3's.
     844     *    Defaults to blank.
     845     * - `video` (string): This is an image that should be used as a
     846     *    placeholder for video files before they're loaded (QuickTime-only).
     847     *    Can be any relative or absolute URL. Defaults to blank.
     848     * - `width` (integer): The width of the embedded media. Accepts any
     849     *    numeric pixel value (such as `480`) or `auto`. Defaults to `auto`,
     850     *    and it is recommended that you use this default.
     851     * - `widescreen` (boolean): Is the enclosure widescreen or standard?
     852     *    This applies only to video enclosures, and will automatically resize
     853     *    the content appropriately.  Defaults to `false`, implying 4:3 mode.
     854     *
     855     * Note: Non-widescreen (4:3) mode with `width` and `height` set to `auto`
     856     * will default to 480x360 video resolution.  Widescreen (16:9) mode with
     857     * `width` and `height` set to `auto` will default to 480x270 video resolution.
     858     *
     859     * @todo If the dimensions for media:content are defined, use them when width/height are set to 'auto'.
     860     * @param array|string $options Comma-separated key:value list, or array
     861     * @param bool $native Use `<embed>`
     862     * @return string HTML string to output
     863     */
     864    public function embed($options = '', $native = false)
     865    {
     866        // Set up defaults
     867        $audio = '';
     868        $video = '';
     869        $alt = '';
     870        $altclass = '';
     871        $loop = 'false';
     872        $width = 'auto';
     873        $height = 'auto';
     874        $bgcolor = '#ffffff';
     875        $mediaplayer = '';
     876        $widescreen = false;
     877        $handler = $this->get_handler();
     878        $type = $this->get_real_type();
     879        $placeholder = '';
     880
     881        // Process options and reassign values as necessary
     882        if (is_array($options)) {
     883            extract($options);
     884        } else {
     885            $options = explode(',', $options);
     886            foreach ($options as $option) {
     887                $opt = explode(':', $option, 2);
     888                if (isset($opt[0], $opt[1])) {
     889                    $opt[0] = trim($opt[0]);
     890                    $opt[1] = trim($opt[1]);
     891                    switch ($opt[0]) {
     892                        case 'audio':
     893                            $audio = $opt[1];
     894                            break;
     895
     896                        case 'video':
     897                            $video = $opt[1];
     898                            break;
     899
     900                        case 'alt':
     901                            $alt = $opt[1];
     902                            break;
     903
     904                        case 'altclass':
     905                            $altclass = $opt[1];
     906                            break;
     907
     908                        case 'loop':
     909                            $loop = $opt[1];
     910                            break;
     911
     912                        case 'width':
     913                            $width = $opt[1];
     914                            break;
     915
     916                        case 'height':
     917                            $height = $opt[1];
     918                            break;
     919
     920                        case 'bgcolor':
     921                            $bgcolor = $opt[1];
     922                            break;
     923
     924                        case 'mediaplayer':
     925                            $mediaplayer = $opt[1];
     926                            break;
     927
     928                        case 'widescreen':
     929                            $widescreen = $opt[1];
     930                            break;
     931                    }
     932                }
     933            }
     934        }
     935
     936        $mime = explode('/', $type, 2);
     937        $mime = $mime[0];
     938
     939        // Process values for 'auto'
     940        if ($width === 'auto') {
     941            if ($mime === 'video') {
     942                if ($height === 'auto') {
     943                    $width = 480;
     944                } elseif ($widescreen) {
     945                    $width = round((intval($height) / 9) * 16);
     946                } else {
     947                    $width = round((intval($height) / 3) * 4);
     948                }
     949            } else {
     950                $width = '100%';
     951            }
     952        }
     953
     954        if ($height === 'auto') {
     955            if ($mime === 'audio') {
     956                $height = 0;
     957            } elseif ($mime === 'video') {
     958                if ($width === 'auto') {
     959                    if ($widescreen) {
     960                        $height = 270;
     961                    } else {
     962                        $height = 360;
     963                    }
     964                } elseif ($widescreen) {
     965                    $height = round((intval($width) / 16) * 9);
     966                } else {
     967                    $height = round((intval($width) / 4) * 3);
     968                }
     969            } else {
     970                $height = 376;
     971            }
     972        } elseif ($mime === 'audio') {
     973            $height = 0;
     974        }
     975
     976        // Set proper placeholder value
     977        if ($mime === 'audio') {
     978            $placeholder = $audio;
     979        } elseif ($mime === 'video') {
     980            $placeholder = $video;
     981        }
     982
     983        $embed = '';
     984
     985        // Flash
     986        if ($handler === 'flash') {
     987            if ($native) {
     988                $embed .= "<embed src=\"" . $this->get_link() . "\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"$type\" quality=\"high\" width=\"$width\" height=\"$height\" bgcolor=\"$bgcolor\" loop=\"$loop\"></embed>";
     989            } else {
     990                $embed .= "<script type='text/javascript'>embed_flash('$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$loop', '$type');</script>";
     991            }
     992        }
     993
     994        // Flash Media Player file types.
     995        // Preferred handler for MP3 file types.
     996        elseif ($handler === 'fmedia' || ($handler === 'mp3' && $mediaplayer !== '')) {
     997            $height += 20;
     998            if ($native) {
     999                $embed .= "<embed src=\"$mediaplayer\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"application/x-shockwave-flash\" quality=\"high\" width=\"$width\" height=\"$height\" wmode=\"transparent\" flashvars=\"file=" . rawurlencode($this->get_link().'?file_extension=.'.$this->get_extension()) . "&autostart=false&repeat=$loop&showdigits=true&showfsbutton=false\"></embed>";
     1000            } else {
     1001                $embed .= "<script type='text/javascript'>embed_flv('$width', '$height', '" . rawurlencode($this->get_link().'?file_extension=.'.$this->get_extension()) . "', '$placeholder', '$loop', '$mediaplayer');</script>";
     1002            }
     1003        }
     1004
     1005        // QuickTime 7 file types.  Need to test with QuickTime 6.
     1006        // Only handle MP3's if the Flash Media Player is not present.
     1007        elseif ($handler === 'quicktime' || ($handler === 'mp3' && $mediaplayer === '')) {
     1008            $height += 16;
     1009            if ($native) {
     1010                if ($placeholder !== '') {
     1011                    $embed .= "<embed type=\"$type\" style=\"cursor:hand; cursor:pointer;\" href=\"" . $this->get_link() . "\" src=\"$placeholder\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"false\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\"></embed>";
     1012                } else {
     1013                    $embed .= "<embed type=\"$type\" style=\"cursor:hand; cursor:pointer;\" src=\"" . $this->get_link() . "\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"true\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\"></embed>";
     1014                }
     1015            } else {
     1016                $embed .= "<script type='text/javascript'>embed_quicktime('$type', '$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$placeholder', '$loop');</script>";
     1017            }
     1018        }
     1019
     1020        // Windows Media
     1021        elseif ($handler === 'wmedia') {
     1022            $height += 45;
     1023            if ($native) {
     1024                $embed .= "<embed type=\"application/x-mplayer2\" src=\"" . $this->get_link() . "\" autosize=\"1\" width=\"$width\" height=\"$height\" showcontrols=\"1\" showstatusbar=\"0\" showdisplay=\"0\" autostart=\"0\"></embed>";
     1025            } else {
     1026                $embed .= "<script type='text/javascript'>embed_wmedia('$width', '$height', '" . $this->get_link() . "');</script>";
     1027            }
     1028        }
     1029
     1030        // Everything else
     1031        else {
     1032            $embed .= '<a href="' . $this->get_link() . '" class="' . $altclass . '">' . $alt . '</a>';
     1033        }
     1034
     1035        return $embed;
     1036    }
     1037
     1038    /**
     1039     * Get the real media type
     1040     *
     1041     * Often, feeds lie to us, necessitating a bit of deeper inspection. This
     1042     * converts types to their canonical representations based on the file
     1043     * extension
     1044     *
     1045     * @see get_type()
     1046     * @param bool $find_handler Internal use only, use {@see get_handler()} instead
     1047     * @return string MIME type
     1048     */
     1049    public function get_real_type($find_handler = false)
     1050    {
     1051        // Mime-types by handler.
     1052        $types_flash = ['application/x-shockwave-flash', 'application/futuresplash']; // Flash
     1053        $types_fmedia = ['video/flv', 'video/x-flv','flv-application/octet-stream']; // Flash Media Player
     1054        $types_quicktime = ['audio/3gpp', 'audio/3gpp2', 'audio/aac', 'audio/x-aac', 'audio/aiff', 'audio/x-aiff', 'audio/mid', 'audio/midi', 'audio/x-midi', 'audio/mp4', 'audio/m4a', 'audio/x-m4a', 'audio/wav', 'audio/x-wav', 'video/3gpp', 'video/3gpp2', 'video/m4v', 'video/x-m4v', 'video/mp4', 'video/mpeg', 'video/x-mpeg', 'video/quicktime', 'video/sd-video']; // QuickTime
     1055        $types_wmedia = ['application/asx', 'application/x-mplayer2', 'audio/x-ms-wma', 'audio/x-ms-wax', 'video/x-ms-asf-plugin', 'video/x-ms-asf', 'video/x-ms-wm', 'video/x-ms-wmv', 'video/x-ms-wvx']; // Windows Media
     1056        $types_mp3 = ['audio/mp3', 'audio/x-mp3', 'audio/mpeg', 'audio/x-mpeg']; // MP3
     1057
     1058        if ($this->get_type() !== null) {
     1059            $type = strtolower($this->type);
     1060        } else {
     1061            $type = null;
     1062        }
     1063
     1064        // If we encounter an unsupported mime-type, check the file extension and guess intelligently.
     1065        if (!in_array($type, array_merge($types_flash, $types_fmedia, $types_quicktime, $types_wmedia, $types_mp3))) {
     1066            $extension = $this->get_extension();
     1067            if ($extension === null) {
     1068                return null;
     1069            }
     1070
     1071            switch (strtolower($extension)) {
     1072                // Audio mime-types
     1073                case 'aac':
     1074                case 'adts':
     1075                    $type = 'audio/acc';
     1076                    break;
     1077
     1078                case 'aif':
     1079                case 'aifc':
     1080                case 'aiff':
     1081                case 'cdda':
     1082                    $type = 'audio/aiff';
     1083                    break;
     1084
     1085                case 'bwf':
     1086                    $type = 'audio/wav';
     1087                    break;
     1088
     1089                case 'kar':
     1090                case 'mid':
     1091                case 'midi':
     1092                case 'smf':
     1093                    $type = 'audio/midi';
     1094                    break;
     1095
     1096                case 'm4a':
     1097                    $type = 'audio/x-m4a';
     1098                    break;
     1099
     1100                case 'mp3':
     1101                case 'swa':
     1102                    $type = 'audio/mp3';
     1103                    break;
     1104
     1105                case 'wav':
     1106                    $type = 'audio/wav';
     1107                    break;
     1108
     1109                case 'wax':
     1110                    $type = 'audio/x-ms-wax';
     1111                    break;
     1112
     1113                case 'wma':
     1114                    $type = 'audio/x-ms-wma';
     1115                    break;
     1116
     1117                    // Video mime-types
     1118                case '3gp':
     1119                case '3gpp':
     1120                    $type = 'video/3gpp';
     1121                    break;
     1122
     1123                case '3g2':
     1124                case '3gp2':
     1125                    $type = 'video/3gpp2';
     1126                    break;
     1127
     1128                case 'asf':
     1129                    $type = 'video/x-ms-asf';
     1130                    break;
     1131
     1132                case 'flv':
     1133                    $type = 'video/x-flv';
     1134                    break;
     1135
     1136                case 'm1a':
     1137                case 'm1s':
     1138                case 'm1v':
     1139                case 'm15':
     1140                case 'm75':
     1141                case 'mp2':
     1142                case 'mpa':
     1143                case 'mpeg':
     1144                case 'mpg':
     1145                case 'mpm':
     1146                case 'mpv':
     1147                    $type = 'video/mpeg';
     1148                    break;
     1149
     1150                case 'm4v':
     1151                    $type = 'video/x-m4v';
     1152                    break;
     1153
     1154                case 'mov':
     1155                case 'qt':
     1156                    $type = 'video/quicktime';
     1157                    break;
     1158
     1159                case 'mp4':
     1160                case 'mpg4':
     1161                    $type = 'video/mp4';
     1162                    break;
     1163
     1164                case 'sdv':
     1165                    $type = 'video/sd-video';
     1166                    break;
     1167
     1168                case 'wm':
     1169                    $type = 'video/x-ms-wm';
     1170                    break;
     1171
     1172                case 'wmv':
     1173                    $type = 'video/x-ms-wmv';
     1174                    break;
     1175
     1176                case 'wvx':
     1177                    $type = 'video/x-ms-wvx';
     1178                    break;
     1179
     1180                    // Flash mime-types
     1181                case 'spl':
     1182                    $type = 'application/futuresplash';
     1183                    break;
     1184
     1185                case 'swf':
     1186                    $type = 'application/x-shockwave-flash';
     1187                    break;
     1188            }
     1189        }
     1190
     1191        if ($find_handler) {
     1192            if (in_array($type, $types_flash)) {
     1193                return 'flash';
     1194            } elseif (in_array($type, $types_fmedia)) {
     1195                return 'fmedia';
     1196            } elseif (in_array($type, $types_quicktime)) {
     1197                return 'quicktime';
     1198            } elseif (in_array($type, $types_wmedia)) {
     1199                return 'wmedia';
     1200            } elseif (in_array($type, $types_mp3)) {
     1201                return 'mp3';
     1202            }
     1203
     1204            return null;
     1205        }
     1206
     1207        return $type;
     1208    }
    13091209}
     1210
     1211class_alias('SimplePie\Enclosure', 'SimplePie_Enclosure');
  • trunk/src/wp-includes/SimplePie/src/Exception.php

    r59140 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie;
     46
     47use Exception as NativeException;
     48
    4449/**
    4550 * General SimplePie exception class
     
    4752 * @package SimplePie
    4853 */
    49 class SimplePie_Exception extends Exception
     54class Exception extends NativeException
    5055{
    5156}
     57
     58class_alias('SimplePie\Exception', 'SimplePie_Exception');
  • trunk/src/wp-includes/SimplePie/src/File.php

    r59140 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie;
     46
    4447/**
    4548 * Used for fetching remote files and reading local files
     
    4750 * Supports HTTP 1.0 via cURL or fsockopen, with spotty HTTP 1.1 support
    4851 *
    49  * This class can be overloaded with {@see SimplePie::set_file_class()}
     52 * This class can be overloaded with {@see \SimplePie\SimplePie::set_file_class()}
    5053 *
    5154 * @package SimplePie
     
    5356 * @todo Move to properly supporting RFC2616 (HTTP/1.1)
    5457 */
    55 class SimplePie_File
     58class File
    5659{
    57     var $url;
    58     var $useragent;
    59     var $success = true;
    60     var $headers = array();
    61     var $body;
    62     var $status_code;
    63     var $redirects = 0;
    64     var $error;
    65     var $method = SIMPLEPIE_FILE_SOURCE_NONE;
    66     var $permanent_url;
    67 
    68     public function __construct($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false, $curl_options = array())
    69     {
    70         if (class_exists('idna_convert'))
    71         {
    72             $idn = new idna_convert();
    73             $parsed = SimplePie_Misc::parse_url($url);
    74             $url = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], NULL);
    75         }
    76         $this->url = $url;
    77         $this->permanent_url = $url;
    78         $this->useragent = $useragent;
    79         if (preg_match('/^http(s)?:\/\//i', $url))
    80         {
    81             if ($useragent === null)
    82             {
    83                 $useragent = ini_get('user_agent');
    84                 $this->useragent = $useragent;
    85             }
    86             if (!is_array($headers))
    87             {
    88                 $headers = array();
    89             }
    90             if (!$force_fsockopen && function_exists('curl_exec'))
    91             {
    92                 $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_CURL;
    93                 $fp = curl_init();
    94                 $headers2 = array();
    95                 foreach ($headers as $key => $value)
    96                 {
    97                     $headers2[] = "$key: $value";
    98                 }
    99                 if (version_compare(SimplePie_Misc::get_curl_version(), '7.10.5', '>='))
    100                 {
    101                     curl_setopt($fp, CURLOPT_ENCODING, '');
    102                 }
    103                 curl_setopt($fp, CURLOPT_URL, $url);
    104                 curl_setopt($fp, CURLOPT_HEADER, 1);
    105                 curl_setopt($fp, CURLOPT_RETURNTRANSFER, 1);
    106                 curl_setopt($fp, CURLOPT_FAILONERROR, 1);
    107                 curl_setopt($fp, CURLOPT_TIMEOUT, $timeout);
    108                 curl_setopt($fp, CURLOPT_CONNECTTIMEOUT, $timeout);
    109                 curl_setopt($fp, CURLOPT_REFERER, SimplePie_Misc::url_remove_credentials($url));
    110                 curl_setopt($fp, CURLOPT_USERAGENT, $useragent);
    111                 curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2);
    112                 foreach ($curl_options as $curl_param => $curl_value) {
    113                     curl_setopt($fp, $curl_param, $curl_value);
    114                 }
    115 
    116                 $this->headers = curl_exec($fp);
    117                 if (curl_errno($fp) === 23 || curl_errno($fp) === 61)
    118                 {
    119                     curl_setopt($fp, CURLOPT_ENCODING, 'none');
    120                     $this->headers = curl_exec($fp);
    121                 }
    122                 $this->status_code = curl_getinfo($fp, CURLINFO_HTTP_CODE);
    123                 if (curl_errno($fp))
    124                 {
    125                     $this->error = 'cURL error ' . curl_errno($fp) . ': ' . curl_error($fp);
    126                     $this->success = false;
    127                 }
    128                 else
    129                 {
    130                     // Use the updated url provided by curl_getinfo after any redirects.
    131                     if ($info = curl_getinfo($fp)) {
    132                         $this->url = $info['url'];
    133                     }
    134                     curl_close($fp);
    135                     $this->headers = SimplePie_HTTP_Parser::prepareHeaders($this->headers, $info['redirect_count'] + 1);
    136                     $parser = new SimplePie_HTTP_Parser($this->headers);
    137                     if ($parser->parse())
    138                     {
    139                         $this->headers = $parser->headers;
    140                         $this->body = trim($parser->body);
    141                         $this->status_code = $parser->status_code;
    142                         if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects)
    143                         {
    144                             $this->redirects++;
    145                             $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url);
    146                             $previousStatusCode = $this->status_code;
    147                             $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen, $curl_options);
    148                             $this->permanent_url = ($previousStatusCode == 301) ? $location : $url;
    149                             return;
    150                         }
    151                     }
    152                 }
    153             }
    154             else
    155             {
    156                 $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_FSOCKOPEN;
    157                 $url_parts = parse_url($url);
    158                 $socket_host = $url_parts['host'];
    159                 if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https')
    160                 {
    161                     $socket_host = "ssl://$url_parts[host]";
    162                     $url_parts['port'] = 443;
    163                 }
    164                 if (!isset($url_parts['port']))
    165                 {
    166                     $url_parts['port'] = 80;
    167                 }
    168                 $fp = @fsockopen($socket_host, $url_parts['port'], $errno, $errstr, $timeout);
    169                 if (!$fp)
    170                 {
    171                     $this->error = 'fsockopen error: ' . $errstr;
    172                     $this->success = false;
    173                 }
    174                 else
    175                 {
    176                     stream_set_timeout($fp, $timeout);
    177                     if (isset($url_parts['path']))
    178                     {
    179                         if (isset($url_parts['query']))
    180                         {
    181                             $get = "$url_parts[path]?$url_parts[query]";
    182                         }
    183                         else
    184                         {
    185                             $get = $url_parts['path'];
    186                         }
    187                     }
    188                     else
    189                     {
    190                         $get = '/';
    191                     }
    192                     $out = "GET $get HTTP/1.1\r\n";
    193                     $out .= "Host: $url_parts[host]\r\n";
    194                     $out .= "User-Agent: $useragent\r\n";
    195                     if (extension_loaded('zlib'))
    196                     {
    197                         $out .= "Accept-Encoding: x-gzip,gzip,deflate\r\n";
    198                     }
    199 
    200                     if (isset($url_parts['user']) && isset($url_parts['pass']))
    201                     {
    202                         $out .= "Authorization: Basic " . base64_encode("$url_parts[user]:$url_parts[pass]") . "\r\n";
    203                     }
    204                     foreach ($headers as $key => $value)
    205                     {
    206                         $out .= "$key: $value\r\n";
    207                     }
    208                     $out .= "Connection: Close\r\n\r\n";
    209                     fwrite($fp, $out);
    210 
    211                     $info = stream_get_meta_data($fp);
    212 
    213                     $this->headers = '';
    214                     while (!$info['eof'] && !$info['timed_out'])
    215                     {
    216                         $this->headers .= fread($fp, 1160);
    217                         $info = stream_get_meta_data($fp);
    218                     }
    219                     if (!$info['timed_out'])
    220                     {
    221                         $parser = new SimplePie_HTTP_Parser($this->headers);
    222                         if ($parser->parse())
    223                         {
    224                             $this->headers = $parser->headers;
    225                             $this->body = $parser->body;
    226                             $this->status_code = $parser->status_code;
    227                             if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects)
    228                             {
    229                                 $this->redirects++;
    230                                 $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url);
    231                                 $previousStatusCode = $this->status_code;
    232                                 $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen, $curl_options);
    233                                 $this->permanent_url = ($previousStatusCode == 301) ? $location : $url;
    234                                 return;
    235                             }
    236                             if (isset($this->headers['content-encoding']))
    237                             {
    238                                 // Hey, we act dumb elsewhere, so let's do that here too
    239                                 switch (strtolower(trim($this->headers['content-encoding'], "\x09\x0A\x0D\x20")))
    240                                 {
    241                                     case 'gzip':
    242                                     case 'x-gzip':
    243                                         $decoder = new SimplePie_gzdecode($this->body);
    244                                         if (!$decoder->parse())
    245                                         {
    246                                             $this->error = 'Unable to decode HTTP "gzip" stream';
    247                                             $this->success = false;
    248                                         }
    249                                         else
    250                                         {
    251                                             $this->body = trim($decoder->data);
    252                                         }
    253                                         break;
    254 
    255                                     case 'deflate':
    256                                         if (($decompressed = gzinflate($this->body)) !== false)
    257                                         {
    258                                             $this->body = $decompressed;
    259                                         }
    260                                         else if (($decompressed = gzuncompress($this->body)) !== false)
    261                                         {
    262                                             $this->body = $decompressed;
    263                                         }
    264                                         else if (function_exists('gzdecode') && ($decompressed = gzdecode($this->body)) !== false)
    265                                         {
    266                                             $this->body = $decompressed;
    267                                         }
    268                                         else
    269                                         {
    270                                             $this->error = 'Unable to decode HTTP "deflate" stream';
    271                                             $this->success = false;
    272                                         }
    273                                         break;
    274 
    275                                     default:
    276                                         $this->error = 'Unknown content coding';
    277                                         $this->success = false;
    278                                 }
    279                             }
    280                         }
    281                     }
    282                     else
    283                     {
    284                         $this->error = 'fsocket timed out';
    285                         $this->success = false;
    286                     }
    287                     fclose($fp);
    288                 }
    289             }
    290         }
    291         else
    292         {
    293             $this->method = SIMPLEPIE_FILE_SOURCE_LOCAL | SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS;
    294             if (empty($url) || !($this->body = trim(file_get_contents($url))))
    295             {
    296                 $this->error = 'file_get_contents could not read the file';
    297                 $this->success = false;
    298             }
    299         }
    300     }
     60    public $url;
     61    public $useragent;
     62    public $success = true;
     63    public $headers = [];
     64    public $body;
     65    public $status_code = 0;
     66    public $redirects = 0;
     67    public $error;
     68    public $method = \SimplePie\SimplePie::FILE_SOURCE_NONE;
     69    public $permanent_url;
     70
     71    public function __construct($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false, $curl_options = [])
     72    {
     73        if (class_exists('idna_convert')) {
     74            $idn = new \idna_convert();
     75            $parsed = \SimplePie\Misc::parse_url($url);
     76            $url = \SimplePie\Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], null);
     77        }
     78        $this->url = $url;
     79        $this->permanent_url = $url;
     80        $this->useragent = $useragent;
     81        if (preg_match('/^http(s)?:\/\//i', $url)) {
     82            if ($useragent === null) {
     83                $useragent = ini_get('user_agent');
     84                $this->useragent = $useragent;
     85            }
     86            if (!is_array($headers)) {
     87                $headers = [];
     88            }
     89            if (!$force_fsockopen && function_exists('curl_exec')) {
     90                $this->method = \SimplePie\SimplePie::FILE_SOURCE_REMOTE | \SimplePie\SimplePie::FILE_SOURCE_CURL;
     91                $fp = curl_init();
     92                $headers2 = [];
     93                foreach ($headers as $key => $value) {
     94                    $headers2[] = "$key: $value";
     95                }
     96                if (version_compare(\SimplePie\Misc::get_curl_version(), '7.10.5', '>=')) {
     97                    curl_setopt($fp, CURLOPT_ENCODING, '');
     98                }
     99                curl_setopt($fp, CURLOPT_URL, $url);
     100                curl_setopt($fp, CURLOPT_HEADER, 1);
     101                curl_setopt($fp, CURLOPT_RETURNTRANSFER, 1);
     102                curl_setopt($fp, CURLOPT_FAILONERROR, 1);
     103                curl_setopt($fp, CURLOPT_TIMEOUT, $timeout);
     104                curl_setopt($fp, CURLOPT_CONNECTTIMEOUT, $timeout);
     105                curl_setopt($fp, CURLOPT_REFERER, \SimplePie\Misc::url_remove_credentials($url));
     106                curl_setopt($fp, CURLOPT_USERAGENT, $useragent);
     107                curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2);
     108                foreach ($curl_options as $curl_param => $curl_value) {
     109                    curl_setopt($fp, $curl_param, $curl_value);
     110                }
     111
     112                $this->headers = curl_exec($fp);
     113                if (curl_errno($fp) === 23 || curl_errno($fp) === 61) {
     114                    curl_setopt($fp, CURLOPT_ENCODING, 'none');
     115                    $this->headers = curl_exec($fp);
     116                }
     117                $this->status_code = curl_getinfo($fp, CURLINFO_HTTP_CODE);
     118                if (curl_errno($fp)) {
     119                    $this->error = 'cURL error ' . curl_errno($fp) . ': ' . curl_error($fp);
     120                    $this->success = false;
     121                } else {
     122                    // Use the updated url provided by curl_getinfo after any redirects.
     123                    if ($info = curl_getinfo($fp)) {
     124                        $this->url = $info['url'];
     125                    }
     126                    curl_close($fp);
     127                    $this->headers = \SimplePie\HTTP\Parser::prepareHeaders($this->headers, $info['redirect_count'] + 1);
     128                    $parser = new \SimplePie\HTTP\Parser($this->headers);
     129                    if ($parser->parse()) {
     130                        $this->headers = $parser->headers;
     131                        $this->body = trim($parser->body);
     132                        $this->status_code = $parser->status_code;
     133                        if ((in_array($this->status_code, [300, 301, 302, 303, 307]) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects) {
     134                            $this->redirects++;
     135                            $location = \SimplePie\Misc::absolutize_url($this->headers['location'], $url);
     136                            $previousStatusCode = $this->status_code;
     137                            $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen, $curl_options);
     138                            $this->permanent_url = ($previousStatusCode == 301) ? $location : $url;
     139                            return;
     140                        }
     141                    }
     142                }
     143            } else {
     144                $this->method = \SimplePie\SimplePie::FILE_SOURCE_REMOTE | \SimplePie\SimplePie::FILE_SOURCE_FSOCKOPEN;
     145                $url_parts = parse_url($url);
     146                $socket_host = $url_parts['host'];
     147                if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https') {
     148                    $socket_host = "ssl://$url_parts[host]";
     149                    $url_parts['port'] = 443;
     150                }
     151                if (!isset($url_parts['port'])) {
     152                    $url_parts['port'] = 80;
     153                }
     154                $fp = @fsockopen($socket_host, $url_parts['port'], $errno, $errstr, $timeout);
     155                if (!$fp) {
     156                    $this->error = 'fsockopen error: ' . $errstr;
     157                    $this->success = false;
     158                } else {
     159                    stream_set_timeout($fp, $timeout);
     160                    if (isset($url_parts['path'])) {
     161                        if (isset($url_parts['query'])) {
     162                            $get = "$url_parts[path]?$url_parts[query]";
     163                        } else {
     164                            $get = $url_parts['path'];
     165                        }
     166                    } else {
     167                        $get = '/';
     168                    }
     169                    $out = "GET $get HTTP/1.1\r\n";
     170                    $out .= "Host: $url_parts[host]\r\n";
     171                    $out .= "User-Agent: $useragent\r\n";
     172                    if (extension_loaded('zlib')) {
     173                        $out .= "Accept-Encoding: x-gzip,gzip,deflate\r\n";
     174                    }
     175
     176                    if (isset($url_parts['user']) && isset($url_parts['pass'])) {
     177                        $out .= "Authorization: Basic " . base64_encode("$url_parts[user]:$url_parts[pass]") . "\r\n";
     178                    }
     179                    foreach ($headers as $key => $value) {
     180                        $out .= "$key: $value\r\n";
     181                    }
     182                    $out .= "Connection: Close\r\n\r\n";
     183                    fwrite($fp, $out);
     184
     185                    $info = stream_get_meta_data($fp);
     186
     187                    $this->headers = '';
     188                    while (!$info['eof'] && !$info['timed_out']) {
     189                        $this->headers .= fread($fp, 1160);
     190                        $info = stream_get_meta_data($fp);
     191                    }
     192                    if (!$info['timed_out']) {
     193                        $parser = new \SimplePie\HTTP\Parser($this->headers);
     194                        if ($parser->parse()) {
     195                            $this->headers = $parser->headers;
     196                            $this->body = $parser->body;
     197                            $this->status_code = $parser->status_code;
     198                            if ((in_array($this->status_code, [300, 301, 302, 303, 307]) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects) {
     199                                $this->redirects++;
     200                                $location = \SimplePie\Misc::absolutize_url($this->headers['location'], $url);
     201                                $previousStatusCode = $this->status_code;
     202                                $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen, $curl_options);
     203                                $this->permanent_url = ($previousStatusCode == 301) ? $location : $url;
     204                                return;
     205                            }
     206                            if (isset($this->headers['content-encoding'])) {
     207                                // Hey, we act dumb elsewhere, so let's do that here too
     208                                switch (strtolower(trim($this->headers['content-encoding'], "\x09\x0A\x0D\x20"))) {
     209                                    case 'gzip':
     210                                    case 'x-gzip':
     211                                        $decoder = new \SimplePie\Gzdecode($this->body);
     212                                        if (!$decoder->parse()) {
     213                                            $this->error = 'Unable to decode HTTP "gzip" stream';
     214                                            $this->success = false;
     215                                        } else {
     216                                            $this->body = trim($decoder->data);
     217                                        }
     218                                        break;
     219
     220                                    case 'deflate':
     221                                        if (($decompressed = gzinflate($this->body)) !== false) {
     222                                            $this->body = $decompressed;
     223                                        } elseif (($decompressed = gzuncompress($this->body)) !== false) {
     224                                            $this->body = $decompressed;
     225                                        } elseif (function_exists('gzdecode') && ($decompressed = gzdecode($this->body)) !== false) {
     226                                            $this->body = $decompressed;
     227                                        } else {
     228                                            $this->error = 'Unable to decode HTTP "deflate" stream';
     229                                            $this->success = false;
     230                                        }
     231                                        break;
     232
     233                                    default:
     234                                        $this->error = 'Unknown content coding';
     235                                        $this->success = false;
     236                                }
     237                            }
     238                        }
     239                    } else {
     240                        $this->error = 'fsocket timed out';
     241                        $this->success = false;
     242                    }
     243                    fclose($fp);
     244                }
     245            }
     246        } else {
     247            $this->method = \SimplePie\SimplePie::FILE_SOURCE_LOCAL | \SimplePie\SimplePie::FILE_SOURCE_FILE_GET_CONTENTS;
     248            if (empty($url) || !($this->body = trim(file_get_contents($url)))) {
     249                $this->error = 'file_get_contents could not read the file';
     250                $this->success = false;
     251            }
     252        }
     253    }
    301254}
     255
     256class_alias('SimplePie\File', 'SimplePie_File');
  • trunk/src/wp-includes/SimplePie/src/HTTP/Parser.php

    r52393 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie\HTTP;
    4446
    4547/**
     
    4951 * @subpackage HTTP
    5052 */
    51 class SimplePie_HTTP_Parser
     53class Parser
    5254{
    53     /**
    54      * HTTP Version
    55      *
    56      * @var float
    57      */
    58     public $http_version = 0.0;
    59 
    60     /**
    61      * Status code
    62      *
    63      * @var int
    64      */
    65     public $status_code = 0;
    66 
    67     /**
    68      * Reason phrase
    69      *
    70      * @var string
    71      */
    72     public $reason = '';
    73 
    74     /**
    75      * Key/value pairs of the headers
    76      *
    77      * @var array
    78      */
    79     public $headers = array();
    80 
    81     /**
    82      * Body of the response
    83      *
    84      * @var string
    85      */
    86     public $body = '';
    87 
    88     /**
    89      * Current state of the state machine
    90      *
    91      * @var string
    92      */
    93     protected $state = 'http_version';
    94 
    95     /**
    96      * Input data
    97      *
    98      * @var string
    99      */
    100     protected $data = '';
    101 
    102     /**
    103      * Input data length (to avoid calling strlen() everytime this is needed)
    104      *
    105      * @var int
    106      */
    107     protected $data_length = 0;
    108 
    109     /**
    110      * Current position of the pointer
    111      *
    112      * @var int
    113      */
    114     protected $position = 0;
    115 
    116     /**
    117      * Name of the hedaer currently being parsed
    118      *
    119      * @var string
    120      */
    121     protected $name = '';
    122 
    123     /**
    124      * Value of the hedaer currently being parsed
    125      *
    126      * @var string
    127      */
    128     protected $value = '';
    129 
    130     /**
    131      * Create an instance of the class with the input data
    132      *
    133      * @param string $data Input data
    134      */
    135     public function __construct($data)
    136     {
    137         $this->data = $data;
    138         $this->data_length = strlen($this->data);
    139     }
    140 
    141     /**
    142      * Parse the input data
    143      *
    144      * @return bool true on success, false on failure
    145      */
    146     public function parse()
    147     {
    148         while ($this->state && $this->state !== 'emit' && $this->has_data())
    149         {
    150             $state = $this->state;
    151             $this->$state();
    152         }
    153         $this->data = '';
    154         if ($this->state === 'emit' || $this->state === 'body')
    155         {
    156             return true;
    157         }
    158 
    159         $this->http_version = '';
    160         $this->status_code = '';
    161         $this->reason = '';
    162         $this->headers = array();
    163         $this->body = '';
    164         return false;
    165     }
    166 
    167     /**
    168      * Check whether there is data beyond the pointer
    169      *
    170      * @return bool true if there is further data, false if not
    171      */
    172     protected function has_data()
    173     {
    174         return (bool) ($this->position < $this->data_length);
    175     }
    176 
    177     /**
    178      * See if the next character is LWS
    179      *
    180      * @return bool true if the next character is LWS, false if not
    181      */
    182     protected function is_linear_whitespace()
    183     {
    184         return (bool) ($this->data[$this->position] === "\x09"
    185             || $this->data[$this->position] === "\x20"
    186             || ($this->data[$this->position] === "\x0A"
    187                 && isset($this->data[$this->position + 1])
    188                 && ($this->data[$this->position + 1] === "\x09" || $this->data[$this->position + 1] === "\x20")));
    189     }
    190 
    191     /**
    192      * Parse the HTTP version
    193      */
    194     protected function http_version()
    195     {
    196         if (strpos($this->data, "\x0A") !== false && strtoupper(substr($this->data, 0, 5)) === 'HTTP/')
    197         {
    198             $len = strspn($this->data, '0123456789.', 5);
    199             $this->http_version = substr($this->data, 5, $len);
    200             $this->position += 5 + $len;
    201             if (substr_count($this->http_version, '.') <= 1)
    202             {
    203                 $this->http_version = (float) $this->http_version;
    204                 $this->position += strspn($this->data, "\x09\x20", $this->position);
    205                 $this->state = 'status';
    206             }
    207             else
    208             {
    209                 $this->state = false;
    210             }
    211         }
    212         else
    213         {
    214             $this->state = false;
    215         }
    216     }
    217 
    218     /**
    219      * Parse the status code
    220      */
    221     protected function status()
    222     {
    223         if ($len = strspn($this->data, '0123456789', $this->position))
    224         {
    225             $this->status_code = (int) substr($this->data, $this->position, $len);
    226             $this->position += $len;
    227             $this->state = 'reason';
    228         }
    229         else
    230         {
    231             $this->state = false;
    232         }
    233     }
    234 
    235     /**
    236      * Parse the reason phrase
    237      */
    238     protected function reason()
    239     {
    240         $len = strcspn($this->data, "\x0A", $this->position);
    241         $this->reason = trim(substr($this->data, $this->position, $len), "\x09\x0D\x20");
    242         $this->position += $len + 1;
    243         $this->state = 'new_line';
    244     }
    245 
    246     /**
    247      * Deal with a new line, shifting data around as needed
    248      */
    249     protected function new_line()
    250     {
    251         $this->value = trim($this->value, "\x0D\x20");
    252         if ($this->name !== '' && $this->value !== '')
    253         {
    254             $this->name = strtolower($this->name);
    255             // We should only use the last Content-Type header. c.f. issue #1
    256             if (isset($this->headers[$this->name]) && $this->name !== 'content-type')
    257             {
    258                 $this->headers[$this->name] .= ', ' . $this->value;
    259             }
    260             else
    261             {
    262                 $this->headers[$this->name] = $this->value;
    263             }
    264         }
    265         $this->name = '';
    266         $this->value = '';
    267         if (substr($this->data[$this->position], 0, 2) === "\x0D\x0A")
    268         {
    269             $this->position += 2;
    270             $this->state = 'body';
    271         }
    272         elseif ($this->data[$this->position] === "\x0A")
    273         {
    274             $this->position++;
    275             $this->state = 'body';
    276         }
    277         else
    278         {
    279             $this->state = 'name';
    280         }
    281     }
    282 
    283     /**
    284      * Parse a header name
    285      */
    286     protected function name()
    287     {
    288         $len = strcspn($this->data, "\x0A:", $this->position);
    289         if (isset($this->data[$this->position + $len]))
    290         {
    291             if ($this->data[$this->position + $len] === "\x0A")
    292             {
    293                 $this->position += $len;
    294                 $this->state = 'new_line';
    295             }
    296             else
    297             {
    298                 $this->name = substr($this->data, $this->position, $len);
    299                 $this->position += $len + 1;
    300                 $this->state = 'value';
    301             }
    302         }
    303         else
    304         {
    305             $this->state = false;
    306         }
    307     }
    308 
    309     /**
    310      * Parse LWS, replacing consecutive LWS characters with a single space
    311      */
    312     protected function linear_whitespace()
    313     {
    314         do
    315         {
    316             if (substr($this->data, $this->position, 2) === "\x0D\x0A")
    317             {
    318                 $this->position += 2;
    319             }
    320             elseif ($this->data[$this->position] === "\x0A")
    321             {
    322                 $this->position++;
    323             }
    324             $this->position += strspn($this->data, "\x09\x20", $this->position);
    325         } while ($this->has_data() && $this->is_linear_whitespace());
    326         $this->value .= "\x20";
    327     }
    328 
    329     /**
    330      * See what state to move to while within non-quoted header values
    331      */
    332     protected function value()
    333     {
    334         if ($this->is_linear_whitespace())
    335         {
    336             $this->linear_whitespace();
    337         }
    338         else
    339         {
    340             switch ($this->data[$this->position])
    341             {
    342                 case '"':
    343                     // Workaround for ETags: we have to include the quotes as
    344                     // part of the tag.
    345                     if (strtolower($this->name) === 'etag')
    346                     {
    347                         $this->value .= '"';
    348                         $this->position++;
    349                         $this->state = 'value_char';
    350                         break;
    351                     }
    352                     $this->position++;
    353                     $this->state = 'quote';
    354                     break;
    355 
    356                 case "\x0A":
    357                     $this->position++;
    358                     $this->state = 'new_line';
    359                     break;
    360 
    361                 default:
    362                     $this->state = 'value_char';
    363                     break;
    364             }
    365         }
    366     }
    367 
    368     /**
    369      * Parse a header value while outside quotes
    370      */
    371     protected function value_char()
    372     {
    373         $len = strcspn($this->data, "\x09\x20\x0A\"", $this->position);
    374         $this->value .= substr($this->data, $this->position, $len);
    375         $this->position += $len;
    376         $this->state = 'value';
    377     }
    378 
    379     /**
    380      * See what state to move to while within quoted header values
    381      */
    382     protected function quote()
    383     {
    384         if ($this->is_linear_whitespace())
    385         {
    386             $this->linear_whitespace();
    387         }
    388         else
    389         {
    390             switch ($this->data[$this->position])
    391             {
    392                 case '"':
    393                     $this->position++;
    394                     $this->state = 'value';
    395                     break;
    396 
    397                 case "\x0A":
    398                     $this->position++;
    399                     $this->state = 'new_line';
    400                     break;
    401 
    402                 case '\\':
    403                     $this->position++;
    404                     $this->state = 'quote_escaped';
    405                     break;
    406 
    407                 default:
    408                     $this->state = 'quote_char';
    409                     break;
    410             }
    411         }
    412     }
    413 
    414     /**
    415      * Parse a header value while within quotes
    416      */
    417     protected function quote_char()
    418     {
    419         $len = strcspn($this->data, "\x09\x20\x0A\"\\", $this->position);
    420         $this->value .= substr($this->data, $this->position, $len);
    421         $this->position += $len;
    422         $this->state = 'value';
    423     }
    424 
    425     /**
    426      * Parse an escaped character within quotes
    427      */
    428     protected function quote_escaped()
    429     {
    430         $this->value .= $this->data[$this->position];
    431         $this->position++;
    432         $this->state = 'quote';
    433     }
    434 
    435     /**
    436      * Parse the body
    437      */
    438     protected function body()
    439     {
    440         $this->body = substr($this->data, $this->position);
    441         if (!empty($this->headers['transfer-encoding']))
    442         {
    443             unset($this->headers['transfer-encoding']);
    444             $this->state = 'chunked';
    445         }
    446         else
    447         {
    448             $this->state = 'emit';
    449         }
    450     }
    451 
    452     /**
    453      * Parsed a "Transfer-Encoding: chunked" body
    454      */
    455     protected function chunked()
    456     {
    457         if (!preg_match('/^([0-9a-f]+)[^\r\n]*\r\n/i', trim($this->body)))
    458         {
    459             $this->state = 'emit';
    460             return;
    461         }
    462 
    463         $decoded = '';
    464         $encoded = $this->body;
    465 
    466         while (true)
    467         {
    468             $is_chunked = (bool) preg_match( '/^([0-9a-f]+)[^\r\n]*\r\n/i', $encoded, $matches );
    469             if (!$is_chunked)
    470             {
    471                 // Looks like it's not chunked after all
    472                 $this->state = 'emit';
    473                 return;
    474             }
    475 
    476             $length = hexdec(trim($matches[1]));
    477             if ($length === 0)
    478             {
    479                 // Ignore trailer headers
    480                 $this->state = 'emit';
    481                 $this->body = $decoded;
    482                 return;
    483             }
    484 
    485             $chunk_length = strlen($matches[0]);
    486             $decoded .= $part = substr($encoded, $chunk_length, $length);
    487             $encoded = substr($encoded, $chunk_length + $length + 2);
    488 
    489             if (trim($encoded) === '0' || empty($encoded))
    490             {
    491                 $this->state = 'emit';
    492                 $this->body = $decoded;
    493                 return;
    494             }
    495         }
    496     }
    497 
    498     /**
    499      * Prepare headers (take care of proxies headers)
    500      *
    501      * @param string  $headers Raw headers
    502      * @param integer $count   Redirection count. Default to 1.
    503      *
    504      * @return string
    505      */
    506     static public function prepareHeaders($headers, $count = 1)
    507     {
    508         $data = explode("\r\n\r\n", $headers, $count);
    509         $data = array_pop($data);
    510         if (false !== stripos($data, "HTTP/1.0 200 Connection established\r\n")) {
    511             $exploded = explode("\r\n\r\n", $data, 2);
    512             $data = end($exploded);
    513         }
    514         if (false !== stripos($data, "HTTP/1.1 200 Connection established\r\n")) {
    515             $exploded = explode("\r\n\r\n", $data, 2);
    516             $data = end($exploded);
    517         }
    518         return $data;
    519     }
     55    /**
     56     * HTTP Version
     57     *
     58     * @var float
     59     */
     60    public $http_version = 0.0;
     61
     62    /**
     63     * Status code
     64     *
     65     * @var int
     66     */
     67    public $status_code = 0;
     68
     69    /**
     70     * Reason phrase
     71     *
     72     * @var string
     73     */
     74    public $reason = '';
     75
     76    /**
     77     * Key/value pairs of the headers
     78     *
     79     * @var array
     80     */
     81    public $headers = [];
     82
     83    /**
     84     * Body of the response
     85     *
     86     * @var string
     87     */
     88    public $body = '';
     89
     90    private const STATE_HTTP_VERSION = 'http_version';
     91
     92    private const STATE_STATUS = 'status';
     93
     94    private const STATE_REASON = 'reason';
     95
     96    private const STATE_NEW_LINE = 'new_line';
     97
     98    private const STATE_BODY = 'body';
     99
     100    private const STATE_NAME = 'name';
     101
     102    private const STATE_VALUE = 'value';
     103
     104    private const STATE_VALUE_CHAR = 'value_char';
     105
     106    private const STATE_QUOTE = 'quote';
     107
     108    private const STATE_QUOTE_ESCAPED = 'quote_escaped';
     109
     110    private const STATE_QUOTE_CHAR = 'quote_char';
     111
     112    private const STATE_CHUNKED = 'chunked';
     113
     114    private const STATE_EMIT = 'emit';
     115
     116    private const STATE_ERROR = false;
     117
     118    /**
     119     * Current state of the state machine
     120     *
     121     * @var self::STATE_*
     122     */
     123    protected $state = self::STATE_HTTP_VERSION;
     124
     125    /**
     126     * Input data
     127     *
     128     * @var string
     129     */
     130    protected $data = '';
     131
     132    /**
     133     * Input data length (to avoid calling strlen() everytime this is needed)
     134     *
     135     * @var int
     136     */
     137    protected $data_length = 0;
     138
     139    /**
     140     * Current position of the pointer
     141     *
     142     * @var int
     143     */
     144    protected $position = 0;
     145
     146    /**
     147     * Name of the hedaer currently being parsed
     148     *
     149     * @var string
     150     */
     151    protected $name = '';
     152
     153    /**
     154     * Value of the hedaer currently being parsed
     155     *
     156     * @var string
     157     */
     158    protected $value = '';
     159
     160    /**
     161     * Create an instance of the class with the input data
     162     *
     163     * @param string $data Input data
     164     */
     165    public function __construct($data)
     166    {
     167        $this->data = $data;
     168        $this->data_length = strlen($this->data);
     169    }
     170
     171    /**
     172     * Parse the input data
     173     *
     174     * @return bool true on success, false on failure
     175     */
     176    public function parse()
     177    {
     178        while ($this->state && $this->state !== self::STATE_EMIT && $this->has_data()) {
     179            $state = $this->state;
     180            $this->$state();
     181        }
     182        $this->data = '';
     183        if ($this->state === self::STATE_EMIT || $this->state === self::STATE_BODY) {
     184            return true;
     185        }
     186
     187        $this->http_version = '';
     188        $this->status_code = 0;
     189        $this->reason = '';
     190        $this->headers = [];
     191        $this->body = '';
     192        return false;
     193    }
     194
     195    /**
     196     * Check whether there is data beyond the pointer
     197     *
     198     * @return bool true if there is further data, false if not
     199     */
     200    protected function has_data()
     201    {
     202        return (bool) ($this->position < $this->data_length);
     203    }
     204
     205    /**
     206     * See if the next character is LWS
     207     *
     208     * @return bool true if the next character is LWS, false if not
     209     */
     210    protected function is_linear_whitespace()
     211    {
     212        return (bool) ($this->data[$this->position] === "\x09"
     213            || $this->data[$this->position] === "\x20"
     214            || ($this->data[$this->position] === "\x0A"
     215                && isset($this->data[$this->position + 1])
     216                && ($this->data[$this->position + 1] === "\x09" || $this->data[$this->position + 1] === "\x20")));
     217    }
     218
     219    /**
     220     * Parse the HTTP version
     221     */
     222    protected function http_version()
     223    {
     224        if (strpos($this->data, "\x0A") !== false && strtoupper(substr($this->data, 0, 5)) === 'HTTP/') {
     225            $len = strspn($this->data, '0123456789.', 5);
     226            $this->http_version = substr($this->data, 5, $len);
     227            $this->position += 5 + $len;
     228            if (substr_count($this->http_version, '.') <= 1) {
     229                $this->http_version = (float) $this->http_version;
     230                $this->position += strspn($this->data, "\x09\x20", $this->position);
     231                $this->state = self::STATE_STATUS;
     232            } else {
     233                $this->state = self::STATE_ERROR;
     234            }
     235        } else {
     236            $this->state = self::STATE_ERROR;
     237        }
     238    }
     239
     240    /**
     241     * Parse the status code
     242     */
     243    protected function status()
     244    {
     245        if ($len = strspn($this->data, '0123456789', $this->position)) {
     246            $this->status_code = (int) substr($this->data, $this->position, $len);
     247            $this->position += $len;
     248            $this->state = self::STATE_REASON;
     249        } else {
     250            $this->state = self::STATE_ERROR;
     251        }
     252    }
     253
     254    /**
     255     * Parse the reason phrase
     256     */
     257    protected function reason()
     258    {
     259        $len = strcspn($this->data, "\x0A", $this->position);
     260        $this->reason = trim(substr($this->data, $this->position, $len), "\x09\x0D\x20");
     261        $this->position += $len + 1;
     262        $this->state = self::STATE_NEW_LINE;
     263    }
     264
     265    /**
     266     * Deal with a new line, shifting data around as needed
     267     */
     268    protected function new_line()
     269    {
     270        $this->value = trim($this->value, "\x0D\x20");
     271        if ($this->name !== '' && $this->value !== '') {
     272            $this->name = strtolower($this->name);
     273            // We should only use the last Content-Type header. c.f. issue #1
     274            if (isset($this->headers[$this->name]) && $this->name !== 'content-type') {
     275                $this->headers[$this->name] .= ', ' . $this->value;
     276            } else {
     277                $this->headers[$this->name] = $this->value;
     278            }
     279        }
     280        $this->name = '';
     281        $this->value = '';
     282        if (substr($this->data[$this->position], 0, 2) === "\x0D\x0A") {
     283            $this->position += 2;
     284            $this->state = self::STATE_BODY;
     285        } elseif ($this->data[$this->position] === "\x0A") {
     286            $this->position++;
     287            $this->state = self::STATE_BODY;
     288        } else {
     289            $this->state = self::STATE_NAME;
     290        }
     291    }
     292
     293    /**
     294     * Parse a header name
     295     */
     296    protected function name()
     297    {
     298        $len = strcspn($this->data, "\x0A:", $this->position);
     299        if (isset($this->data[$this->position + $len])) {
     300            if ($this->data[$this->position + $len] === "\x0A") {
     301                $this->position += $len;
     302                $this->state = self::STATE_NEW_LINE;
     303            } else {
     304                $this->name = substr($this->data, $this->position, $len);
     305                $this->position += $len + 1;
     306                $this->state = self::STATE_VALUE;
     307            }
     308        } else {
     309            $this->state = self::STATE_ERROR;
     310        }
     311    }
     312
     313    /**
     314     * Parse LWS, replacing consecutive LWS characters with a single space
     315     */
     316    protected function linear_whitespace()
     317    {
     318        do {
     319            if (substr($this->data, $this->position, 2) === "\x0D\x0A") {
     320                $this->position += 2;
     321            } elseif ($this->data[$this->position] === "\x0A") {
     322                $this->position++;
     323            }
     324            $this->position += strspn($this->data, "\x09\x20", $this->position);
     325        } while ($this->has_data() && $this->is_linear_whitespace());
     326        $this->value .= "\x20";
     327    }
     328
     329    /**
     330     * See what state to move to while within non-quoted header values
     331     */
     332    protected function value()
     333    {
     334        if ($this->is_linear_whitespace()) {
     335            $this->linear_whitespace();
     336        } else {
     337            switch ($this->data[$this->position]) {
     338                case '"':
     339                    // Workaround for ETags: we have to include the quotes as
     340                    // part of the tag.
     341                    if (strtolower($this->name) === 'etag') {
     342                        $this->value .= '"';
     343                        $this->position++;
     344                        $this->state = self::STATE_VALUE_CHAR;
     345                        break;
     346                    }
     347                    $this->position++;
     348                    $this->state = self::STATE_QUOTE;
     349                    break;
     350
     351                case "\x0A":
     352                    $this->position++;
     353                    $this->state = self::STATE_NEW_LINE;
     354                    break;
     355
     356                default:
     357                    $this->state = self::STATE_VALUE_CHAR;
     358                    break;
     359            }
     360        }
     361    }
     362
     363    /**
     364     * Parse a header value while outside quotes
     365     */
     366    protected function value_char()
     367    {
     368        $len = strcspn($this->data, "\x09\x20\x0A\"", $this->position);
     369        $this->value .= substr($this->data, $this->position, $len);
     370        $this->position += $len;
     371        $this->state = self::STATE_VALUE;
     372    }
     373
     374    /**
     375     * See what state to move to while within quoted header values
     376     */
     377    protected function quote()
     378    {
     379        if ($this->is_linear_whitespace()) {
     380            $this->linear_whitespace();
     381        } else {
     382            switch ($this->data[$this->position]) {
     383                case '"':
     384                    $this->position++;
     385                    $this->state = self::STATE_VALUE;
     386                    break;
     387
     388                case "\x0A":
     389                    $this->position++;
     390                    $this->state = self::STATE_NEW_LINE;
     391                    break;
     392
     393                case '\\':
     394                    $this->position++;
     395                    $this->state = self::STATE_QUOTE_ESCAPED;
     396                    break;
     397
     398                default:
     399                    $this->state = self::STATE_QUOTE_CHAR;
     400                    break;
     401            }
     402        }
     403    }
     404
     405    /**
     406     * Parse a header value while within quotes
     407     */
     408    protected function quote_char()
     409    {
     410        $len = strcspn($this->data, "\x09\x20\x0A\"\\", $this->position);
     411        $this->value .= substr($this->data, $this->position, $len);
     412        $this->position += $len;
     413        $this->state = self::STATE_VALUE;
     414    }
     415
     416    /**
     417     * Parse an escaped character within quotes
     418     */
     419    protected function quote_escaped()
     420    {
     421        $this->value .= $this->data[$this->position];
     422        $this->position++;
     423        $this->state = self::STATE_QUOTE;
     424    }
     425
     426    /**
     427     * Parse the body
     428     */
     429    protected function body()
     430    {
     431        $this->body = substr($this->data, $this->position);
     432        if (!empty($this->headers['transfer-encoding'])) {
     433            unset($this->headers['transfer-encoding']);
     434            $this->state = self::STATE_CHUNKED;
     435        } else {
     436            $this->state = self::STATE_EMIT;
     437        }
     438    }
     439
     440    /**
     441     * Parsed a "Transfer-Encoding: chunked" body
     442     */
     443    protected function chunked()
     444    {
     445        if (!preg_match('/^([0-9a-f]+)[^\r\n]*\r\n/i', trim($this->body))) {
     446            $this->state = self::STATE_EMIT;
     447            return;
     448        }
     449
     450        $decoded = '';
     451        $encoded = $this->body;
     452
     453        while (true) {
     454            $is_chunked = (bool) preg_match('/^([0-9a-f]+)[^\r\n]*\r\n/i', $encoded, $matches);
     455            if (!$is_chunked) {
     456                // Looks like it's not chunked after all
     457                $this->state = self::STATE_EMIT;
     458                return;
     459            }
     460
     461            $length = hexdec(trim($matches[1]));
     462            if ($length === 0) {
     463                // Ignore trailer headers
     464                $this->state = self::STATE_EMIT;
     465                $this->body = $decoded;
     466                return;
     467            }
     468
     469            $chunk_length = strlen($matches[0]);
     470            $decoded .= substr($encoded, $chunk_length, $length);
     471            $encoded = substr($encoded, $chunk_length + $length + 2);
     472
     473            // BC for PHP < 8.0: substr() can return bool instead of string
     474            $encoded = ($encoded === false) ? '' : $encoded;
     475
     476            if (trim($encoded) === '0' || empty($encoded)) {
     477                $this->state = self::STATE_EMIT;
     478                $this->body = $decoded;
     479                return;
     480            }
     481        }
     482    }
     483
     484    /**
     485     * Prepare headers (take care of proxies headers)
     486     *
     487     * @param string  $headers Raw headers
     488     * @param integer $count   Redirection count. Default to 1.
     489     *
     490     * @return string
     491     */
     492    public static function prepareHeaders($headers, $count = 1)
     493    {
     494        $data = explode("\r\n\r\n", $headers, $count);
     495        $data = array_pop($data);
     496        if (false !== stripos($data, "HTTP/1.0 200 Connection established\r\n")) {
     497            $exploded = explode("\r\n\r\n", $data, 2);
     498            $data = end($exploded);
     499        }
     500        if (false !== stripos($data, "HTTP/1.1 200 Connection established\r\n")) {
     501            $exploded = explode("\r\n\r\n", $data, 2);
     502            $data = end($exploded);
     503        }
     504        return $data;
     505    }
    520506}
     507
     508class_alias('SimplePie\HTTP\Parser', 'SimplePie_HTTP_Parser');
  • trunk/src/wp-includes/SimplePie/src/IRI.php

    r59140 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie;
     46
    4447/**
    4548 * IRI parser/serialiser/normaliser
     
    5356 * @license http://www.opensource.org/licenses/bsd-license.php
    5457 */
    55 class SimplePie_IRI
     58class IRI
    5659{
    57     /**
    58      * Scheme
    59      *
    60      * @var string
    61      */
    62     protected $scheme = null;
    63 
    64     /**
    65      * User Information
    66      *
    67      * @var string
    68      */
    69     protected $iuserinfo = null;
    70 
    71     /**
    72      * ihost
    73      *
    74      * @var string
    75      */
    76     protected $ihost = null;
    77 
    78     /**
    79      * Port
    80      *
    81      * @var string
    82      */
    83     protected $port = null;
    84 
    85     /**
    86      * ipath
    87      *
    88      * @var string
    89      */
    90     protected $ipath = '';
    91 
    92     /**
    93      * iquery
    94      *
    95      * @var string
    96      */
    97     protected $iquery = null;
    98 
    99     /**
    100      * ifragment
    101      *
    102      * @var string
    103      */
    104     protected $ifragment = null;
    105 
    106     /**
    107      * Normalization database
    108      *
    109      * Each key is the scheme, each value is an array with each key as the IRI
    110      * part and value as the default value for that part.
    111      */
    112     protected $normalization = array(
    113         'acap' => array(
    114             'port' => 674
    115         ),
    116         'dict' => array(
    117             'port' => 2628
    118         ),
    119         'file' => array(
    120             'ihost' => 'localhost'
    121         ),
    122         'http' => array(
    123             'port' => 80,
    124             'ipath' => '/'
    125         ),
    126         'https' => array(
    127             'port' => 443,
    128             'ipath' => '/'
    129         ),
    130     );
    131 
    132     /**
    133      * Return the entire IRI when you try and read the object as a string
    134      *
    135      * @return string
    136      */
    137     public function __toString()
    138     {
    139         return $this->get_iri();
    140     }
    141 
    142     /**
    143      * Overload __set() to provide access via properties
    144      *
    145      * @param string $name Property name
    146      * @param mixed $value Property value
    147      */
    148     public function __set($name, $value)
    149     {
    150         if (method_exists($this, 'set_' . $name))
    151         {
    152             call_user_func(array($this, 'set_' . $name), $value);
    153         }
    154         elseif (
    155                $name === 'iauthority'
    156             || $name === 'iuserinfo'
    157             || $name === 'ihost'
    158             || $name === 'ipath'
    159             || $name === 'iquery'
    160             || $name === 'ifragment'
    161         )
    162         {
    163             call_user_func(array($this, 'set_' . substr($name, 1)), $value);
    164         }
    165     }
    166 
    167     /**
    168      * Overload __get() to provide access via properties
    169      *
    170      * @param string $name Property name
    171      * @return mixed
    172      */
    173     public function __get($name)
    174     {
    175         // isset() returns false for null, we don't want to do that
    176         // Also why we use array_key_exists below instead of isset()
    177         $props = get_object_vars($this);
    178 
    179         if (
    180             $name === 'iri' ||
    181             $name === 'uri' ||
    182             $name === 'iauthority' ||
    183             $name === 'authority'
    184         )
    185         {
    186             $return = $this->{"get_$name"}();
    187         }
    188         elseif (array_key_exists($name, $props))
    189         {
    190             $return = $this->$name;
    191         }
    192         // host -> ihost
    193         elseif (($prop = 'i' . $name) && array_key_exists($prop, $props))
    194         {
    195             $name = $prop;
    196             $return = $this->$prop;
    197         }
    198         // ischeme -> scheme
    199         elseif (($prop = substr($name, 1)) && array_key_exists($prop, $props))
    200         {
    201             $name = $prop;
    202             $return = $this->$prop;
    203         }
    204         else
    205         {
    206             trigger_error('Undefined property: ' . get_class($this) . '::' . $name, E_USER_NOTICE);
    207             $return = null;
    208         }
    209 
    210         if ($return === null && isset($this->normalization[$this->scheme][$name]))
    211         {
    212             return $this->normalization[$this->scheme][$name];
    213         }
    214 
    215         return $return;
    216     }
    217 
    218     /**
    219      * Overload __isset() to provide access via properties
    220      *
    221      * @param string $name Property name
    222      * @return bool
    223      */
    224     public function __isset($name)
    225     {
    226         return method_exists($this, 'get_' . $name) || isset($this->$name);
    227     }
    228 
    229     /**
    230      * Overload __unset() to provide access via properties
    231      *
    232      * @param string $name Property name
    233      */
    234     public function __unset($name)
    235     {
    236         if (method_exists($this, 'set_' . $name))
    237         {
    238             call_user_func(array($this, 'set_' . $name), '');
    239         }
    240     }
    241 
    242     /**
    243      * Create a new IRI object, from a specified string
    244      *
    245      * @param string $iri
    246      */
    247     public function __construct($iri = null)
    248     {
    249         $this->set_iri($iri);
    250     }
    251 
    252     /**
    253      * Clean up
    254      */
    255     public function __destruct() {
    256         $this->set_iri(null, true);
    257         $this->set_path(null, true);
    258         $this->set_authority(null, true);
    259     }
    260 
    261     /**
    262      * Create a new IRI object by resolving a relative IRI
    263      *
    264      * Returns false if $base is not absolute, otherwise an IRI.
    265      *
    266      * @param IRI|string $base (Absolute) Base IRI
    267      * @param IRI|string $relative Relative IRI
    268      * @return IRI|false
    269      */
    270     public static function absolutize($base, $relative)
    271     {
    272         if (!($relative instanceof SimplePie_IRI))
    273         {
    274             $relative = new SimplePie_IRI($relative);
    275         }
    276         if (!$relative->is_valid())
    277         {
    278             return false;
    279         }
    280         elseif ($relative->scheme !== null)
    281         {
    282             return clone $relative;
    283         }
    284         else
    285         {
    286             if (!($base instanceof SimplePie_IRI))
    287             {
    288                 $base = new SimplePie_IRI($base);
    289             }
    290             if ($base->scheme !== null && $base->is_valid())
    291             {
    292                 if ($relative->get_iri() !== '')
    293                 {
    294                     if ($relative->iuserinfo !== null || $relative->ihost !== null || $relative->port !== null)
    295                     {
    296                         $target = clone $relative;
    297                         $target->scheme = $base->scheme;
    298                     }
    299                     else
    300                     {
    301                         $target = new SimplePie_IRI;
    302                         $target->scheme = $base->scheme;
    303                         $target->iuserinfo = $base->iuserinfo;
    304                         $target->ihost = $base->ihost;
    305                         $target->port = $base->port;
    306                         if ($relative->ipath !== '')
    307                         {
    308                             if ($relative->ipath[0] === '/')
    309                             {
    310                                 $target->ipath = $relative->ipath;
    311                             }
    312                             elseif (($base->iuserinfo !== null || $base->ihost !== null || $base->port !== null) && $base->ipath === '')
    313                             {
    314                                 $target->ipath = '/' . $relative->ipath;
    315                             }
    316                             elseif (($last_segment = strrpos($base->ipath, '/')) !== false)
    317                             {
    318                                 $target->ipath = substr($base->ipath, 0, $last_segment + 1) . $relative->ipath;
    319                             }
    320                             else
    321                             {
    322                                 $target->ipath = $relative->ipath;
    323                             }
    324                             $target->ipath = $target->remove_dot_segments($target->ipath);
    325                             $target->iquery = $relative->iquery;
    326                         }
    327                         else
    328                         {
    329                             $target->ipath = $base->ipath;
    330                             if ($relative->iquery !== null)
    331                             {
    332                                 $target->iquery = $relative->iquery;
    333                             }
    334                             elseif ($base->iquery !== null)
    335                             {
    336                                 $target->iquery = $base->iquery;
    337                             }
    338                         }
    339                         $target->ifragment = $relative->ifragment;
    340                     }
    341                 }
    342                 else
    343                 {
    344                     $target = clone $base;
    345                     $target->ifragment = null;
    346                 }
    347                 $target->scheme_normalization();
    348                 return $target;
    349             }
    350 
    351             return false;
    352         }
    353     }
    354 
    355     /**
    356      * Parse an IRI into scheme/authority/path/query/fragment segments
    357      *
    358      * @param string $iri
    359      * @return array
    360      */
    361     protected function parse_iri($iri)
    362     {
    363         $iri = trim($iri, "\x20\x09\x0A\x0C\x0D");
    364         if (preg_match('/^((?P<scheme>[^:\/?#]+):)?(\/\/(?P<authority>[^\/?#]*))?(?P<path>[^?#]*)(\?(?P<query>[^#]*))?(#(?P<fragment>.*))?$/', $iri, $match))
    365         {
    366             if ($match[1] === '')
    367             {
    368                 $match['scheme'] = null;
    369             }
    370             if (!isset($match[3]) || $match[3] === '')
    371             {
    372                 $match['authority'] = null;
    373             }
    374             if (!isset($match[5]))
    375             {
    376                 $match['path'] = '';
    377             }
    378             if (!isset($match[6]) || $match[6] === '')
    379             {
    380                 $match['query'] = null;
    381             }
    382             if (!isset($match[8]) || $match[8] === '')
    383             {
    384                 $match['fragment'] = null;
    385             }
    386             return $match;
    387         }
    388 
    389         // This can occur when a paragraph is accidentally parsed as a URI
    390         return false;
    391     }
    392 
    393     /**
    394      * Remove dot segments from a path
    395      *
    396      * @param string $input
    397      * @return string
    398      */
    399     protected function remove_dot_segments($input)
    400     {
    401         $output = '';
    402         while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..')
    403         {
    404             // A: If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise,
    405             if (strpos($input, '../') === 0)
    406             {
    407                 $input = substr($input, 3);
    408             }
    409             elseif (strpos($input, './') === 0)
    410             {
    411                 $input = substr($input, 2);
    412             }
    413             // B: if the input buffer begins with a prefix of "/./" or "/.", where "." is a complete path segment, then replace that prefix with "/" in the input buffer; otherwise,
    414             elseif (strpos($input, '/./') === 0)
    415             {
    416                 $input = substr($input, 2);
    417             }
    418             elseif ($input === '/.')
    419             {
    420                 $input = '/';
    421             }
    422             // C: if the input buffer begins with a prefix of "/../" or "/..", where ".." is a complete path segment, then replace that prefix with "/" in the input buffer and remove the last segment and its preceding "/" (if any) from the output buffer; otherwise,
    423             elseif (strpos($input, '/../') === 0)
    424             {
    425                 $input = substr($input, 3);
    426                 $output = substr_replace($output, '', strrpos($output, '/'));
    427             }
    428             elseif ($input === '/..')
    429             {
    430                 $input = '/';
    431                 $output = substr_replace($output, '', strrpos($output, '/'));
    432             }
    433             // D: if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise,
    434             elseif ($input === '.' || $input === '..')
    435             {
    436                 $input = '';
    437             }
    438             // E: move the first path segment in the input buffer to the end of the output buffer, including the initial "/" character (if any) and any subsequent characters up to, but not including, the next "/" character or the end of the input buffer
    439             elseif (($pos = strpos($input, '/', 1)) !== false)
    440             {
    441                 $output .= substr($input, 0, $pos);
    442                 $input = substr_replace($input, '', 0, $pos);
    443             }
    444             else
    445             {
    446                 $output .= $input;
    447                 $input = '';
    448             }
    449         }
    450         return $output . $input;
    451     }
    452 
    453     /**
    454      * Replace invalid character with percent encoding
    455      *
    456      * @param string $string Input string
    457      * @param string $extra_chars Valid characters not in iunreserved or
    458      *                            iprivate (this is ASCII-only)
    459      * @param bool $iprivate Allow iprivate
    460      * @return string
    461      */
    462     protected function replace_invalid_with_pct_encoding($string, $extra_chars, $iprivate = false)
    463     {
    464         // Normalize as many pct-encoded sections as possible
    465         $string = preg_replace_callback('/(?:%[A-Fa-f0-9]{2})+/', array($this, 'remove_iunreserved_percent_encoded'), $string);
    466 
    467         // Replace invalid percent characters
    468         $string = preg_replace('/%(?![A-Fa-f0-9]{2})/', '%25', $string);
    469 
    470         // Add unreserved and % to $extra_chars (the latter is safe because all
    471         // pct-encoded sections are now valid).
    472         $extra_chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~%';
    473 
    474         // Now replace any bytes that aren't allowed with their pct-encoded versions
    475         $position = 0;
    476         $strlen = strlen($string);
    477         while (($position += strspn($string, $extra_chars, $position)) < $strlen)
    478         {
    479             $value = ord($string[$position]);
    480 
    481             // Start position
    482             $start = $position;
    483 
    484             // By default we are valid
    485             $valid = true;
    486 
    487             // No one byte sequences are valid due to the while.
    488             // Two byte sequence:
    489             if (($value & 0xE0) === 0xC0)
    490             {
    491                 $character = ($value & 0x1F) << 6;
    492                 $length = 2;
    493                 $remaining = 1;
    494             }
    495             // Three byte sequence:
    496             elseif (($value & 0xF0) === 0xE0)
    497             {
    498                 $character = ($value & 0x0F) << 12;
    499                 $length = 3;
    500                 $remaining = 2;
    501             }
    502             // Four byte sequence:
    503             elseif (($value & 0xF8) === 0xF0)
    504             {
    505                 $character = ($value & 0x07) << 18;
    506                 $length = 4;
    507                 $remaining = 3;
    508             }
    509             // Invalid byte:
    510             else
    511             {
    512                 $valid = false;
    513                 $length = 1;
    514                 $remaining = 0;
    515             }
    516 
    517             if ($remaining)
    518             {
    519                 if ($position + $length <= $strlen)
    520                 {
    521                     for ($position++; $remaining; $position++)
    522                     {
    523                         $value = ord($string[$position]);
    524 
    525                         // Check that the byte is valid, then add it to the character:
    526                         if (($value & 0xC0) === 0x80)
    527                         {
    528                             $character |= ($value & 0x3F) << (--$remaining * 6);
    529                         }
    530                         // If it is invalid, count the sequence as invalid and reprocess the current byte:
    531                         else
    532                         {
    533                             $valid = false;
    534                             $position--;
    535                             break;
    536                         }
    537                     }
    538                 }
    539                 else
    540                 {
    541                     $position = $strlen - 1;
    542                     $valid = false;
    543                 }
    544             }
    545 
    546             // Percent encode anything invalid or not in ucschar
    547             if (
    548                 // Invalid sequences
    549                 !$valid
    550                 // Non-shortest form sequences are invalid
    551                 || $length > 1 && $character <= 0x7F
    552                 || $length > 2 && $character <= 0x7FF
    553                 || $length > 3 && $character <= 0xFFFF
    554                 // Outside of range of ucschar codepoints
    555                 // Noncharacters
    556                 || ($character & 0xFFFE) === 0xFFFE
    557                 || $character >= 0xFDD0 && $character <= 0xFDEF
    558                 || (
    559                     // Everything else not in ucschar
    560                        $character > 0xD7FF && $character < 0xF900
    561                     || $character < 0xA0
    562                     || $character > 0xEFFFD
    563                 )
    564                 && (
    565                     // Everything not in iprivate, if it applies
    566                        !$iprivate
    567                     || $character < 0xE000
    568                     || $character > 0x10FFFD
    569                 )
    570             )
    571             {
    572                 // If we were a character, pretend we weren't, but rather an error.
    573                 if ($valid)
    574                     $position--;
    575 
    576                 for ($j = $start; $j <= $position; $j++)
    577                 {
    578                     $string = substr_replace($string, sprintf('%%%02X', ord($string[$j])), $j, 1);
    579                     $j += 2;
    580                     $position += 2;
    581                     $strlen += 2;
    582                 }
    583             }
    584         }
    585 
    586         return $string;
    587     }
    588 
    589     /**
    590      * Callback function for preg_replace_callback.
    591      *
    592      * Removes sequences of percent encoded bytes that represent UTF-8
    593      * encoded characters in iunreserved
    594      *
    595      * @param array $match PCRE match
    596      * @return string Replacement
    597      */
    598     protected function remove_iunreserved_percent_encoded($match)
    599     {
    600         // As we just have valid percent encoded sequences we can just explode
    601         // and ignore the first member of the returned array (an empty string).
    602         $bytes = explode('%', $match[0]);
    603 
    604         // Initialize the new string (this is what will be returned) and that
    605         // there are no bytes remaining in the current sequence (unsurprising
    606         // at the first byte!).
    607         $string = '';
    608         $remaining = 0;
    609 
    610         // Loop over each and every byte, and set $value to its value
    611         for ($i = 1, $len = count($bytes); $i < $len; $i++)
    612         {
    613             $value = hexdec($bytes[$i]);
    614 
    615             // If we're the first byte of sequence:
    616             if (!$remaining)
    617             {
    618                 // Start position
    619                 $start = $i;
    620 
    621                 // By default we are valid
    622                 $valid = true;
    623 
    624                 // One byte sequence:
    625                 if ($value <= 0x7F)
    626                 {
    627                     $character = $value;
    628                     $length = 1;
    629                 }
    630                 // Two byte sequence:
    631                 elseif (($value & 0xE0) === 0xC0)
    632                 {
    633                     $character = ($value & 0x1F) << 6;
    634                     $length = 2;
    635                     $remaining = 1;
    636                 }
    637                 // Three byte sequence:
    638                 elseif (($value & 0xF0) === 0xE0)
    639                 {
    640                     $character = ($value & 0x0F) << 12;
    641                     $length = 3;
    642                     $remaining = 2;
    643                 }
    644                 // Four byte sequence:
    645                 elseif (($value & 0xF8) === 0xF0)
    646                 {
    647                     $character = ($value & 0x07) << 18;
    648                     $length = 4;
    649                     $remaining = 3;
    650                 }
    651                 // Invalid byte:
    652                 else
    653                 {
    654                     $valid = false;
    655                     $remaining = 0;
    656                 }
    657             }
    658             // Continuation byte:
    659             else
    660             {
    661                 // Check that the byte is valid, then add it to the character:
    662                 if (($value & 0xC0) === 0x80)
    663                 {
    664                     $remaining--;
    665                     $character |= ($value & 0x3F) << ($remaining * 6);
    666                 }
    667                 // If it is invalid, count the sequence as invalid and reprocess the current byte as the start of a sequence:
    668                 else
    669                 {
    670                     $valid = false;
    671                     $remaining = 0;
    672                     $i--;
    673                 }
    674             }
    675 
    676             // If we've reached the end of the current byte sequence, append it to Unicode::$data
    677             if (!$remaining)
    678             {
    679                 // Percent encode anything invalid or not in iunreserved
    680                 if (
    681                     // Invalid sequences
    682                     !$valid
    683                     // Non-shortest form sequences are invalid
    684                     || $length > 1 && $character <= 0x7F
    685                     || $length > 2 && $character <= 0x7FF
    686                     || $length > 3 && $character <= 0xFFFF
    687                     // Outside of range of iunreserved codepoints
    688                     || $character < 0x2D
    689                     || $character > 0xEFFFD
    690                     // Noncharacters
    691                     || ($character & 0xFFFE) === 0xFFFE
    692                     || $character >= 0xFDD0 && $character <= 0xFDEF
    693                     // Everything else not in iunreserved (this is all BMP)
    694                     || $character === 0x2F
    695                     || $character > 0x39 && $character < 0x41
    696                     || $character > 0x5A && $character < 0x61
    697                     || $character > 0x7A && $character < 0x7E
    698                     || $character > 0x7E && $character < 0xA0
    699                     || $character > 0xD7FF && $character < 0xF900
    700                 )
    701                 {
    702                     for ($j = $start; $j <= $i; $j++)
    703                     {
    704                         $string .= '%' . strtoupper($bytes[$j]);
    705                     }
    706                 }
    707                 else
    708                 {
    709                     for ($j = $start; $j <= $i; $j++)
    710                     {
    711                         $string .= chr(hexdec($bytes[$j]));
    712                     }
    713                 }
    714             }
    715         }
    716 
    717         // If we have any bytes left over they are invalid (i.e., we are
    718         // mid-way through a multi-byte sequence)
    719         if ($remaining)
    720         {
    721             for ($j = $start; $j < $len; $j++)
    722             {
    723                 $string .= '%' . strtoupper($bytes[$j]);
    724             }
    725         }
    726 
    727         return $string;
    728     }
    729 
    730     protected function scheme_normalization()
    731     {
    732         if (isset($this->normalization[$this->scheme]['iuserinfo']) && $this->iuserinfo === $this->normalization[$this->scheme]['iuserinfo'])
    733         {
    734             $this->iuserinfo = null;
    735         }
    736         if (isset($this->normalization[$this->scheme]['ihost']) && $this->ihost === $this->normalization[$this->scheme]['ihost'])
    737         {
    738             $this->ihost = null;
    739         }
    740         if (isset($this->normalization[$this->scheme]['port']) && $this->port === $this->normalization[$this->scheme]['port'])
    741         {
    742             $this->port = null;
    743         }
    744         if (isset($this->normalization[$this->scheme]['ipath']) && $this->ipath === $this->normalization[$this->scheme]['ipath'])
    745         {
    746             $this->ipath = '';
    747         }
    748         if (isset($this->normalization[$this->scheme]['iquery']) && $this->iquery === $this->normalization[$this->scheme]['iquery'])
    749         {
    750             $this->iquery = null;
    751         }
    752         if (isset($this->normalization[$this->scheme]['ifragment']) && $this->ifragment === $this->normalization[$this->scheme]['ifragment'])
    753         {
    754             $this->ifragment = null;
    755         }
    756     }
    757 
    758     /**
    759      * Check if the object represents a valid IRI. This needs to be done on each
    760      * call as some things change depending on another part of the IRI.
    761      *
    762      * @return bool
    763      */
    764     public function is_valid()
    765     {
    766         if ($this->ipath === '') return true;
    767 
    768         $isauthority = $this->iuserinfo !== null || $this->ihost !== null ||
    769             $this->port !== null;
    770         if ($isauthority && $this->ipath[0] === '/') return true;
    771 
    772         if (!$isauthority && (substr($this->ipath, 0, 2) === '//')) return false;
    773 
    774         // Relative urls cannot have a colon in the first path segment (and the
    775         // slashes themselves are not included so skip the first character).
    776         if (!$this->scheme && !$isauthority &&
    777             strpos($this->ipath, ':') !== false &&
    778             strpos($this->ipath, '/', 1) !== false &&
    779             strpos($this->ipath, ':') < strpos($this->ipath, '/', 1)) return false;
    780 
    781         return true;
    782     }
    783 
    784     /**
    785      * Set the entire IRI. Returns true on success, false on failure (if there
    786      * are any invalid characters).
    787      *
    788      * @param string $iri
    789      * @return bool
    790      */
    791     public function set_iri($iri, $clear_cache = false)
    792     {
    793         static $cache;
    794         if ($clear_cache)
    795         {
    796             $cache = null;
    797             return;
    798         }
    799         if (!$cache)
    800         {
    801             $cache = array();
    802         }
    803 
    804         if ($iri === null)
    805         {
    806             return true;
    807         }
    808         elseif (isset($cache[$iri]))
    809         {
    810             list($this->scheme,
    811                  $this->iuserinfo,
    812                  $this->ihost,
    813                  $this->port,
    814                  $this->ipath,
    815                  $this->iquery,
    816                  $this->ifragment,
    817                  $return) = $cache[$iri];
    818             return $return;
    819         }
    820 
    821         $parsed = $this->parse_iri((string) $iri);
    822         if (!$parsed)
    823         {
    824             return false;
    825         }
    826 
    827         $return = $this->set_scheme($parsed['scheme'])
    828             && $this->set_authority($parsed['authority'])
    829             && $this->set_path($parsed['path'])
    830             && $this->set_query($parsed['query'])
    831             && $this->set_fragment($parsed['fragment']);
    832 
    833         $cache[$iri] = array($this->scheme,
    834                              $this->iuserinfo,
    835                              $this->ihost,
    836                              $this->port,
    837                              $this->ipath,
    838                              $this->iquery,
    839                              $this->ifragment,
    840                              $return);
    841         return $return;
    842     }
    843 
    844     /**
    845      * Set the scheme. Returns true on success, false on failure (if there are
    846      * any invalid characters).
    847      *
    848      * @param string $scheme
    849      * @return bool
    850      */
    851     public function set_scheme($scheme)
    852     {
    853         if ($scheme === null)
    854         {
    855             $this->scheme = null;
    856         }
    857         elseif (!preg_match('/^[A-Za-z][0-9A-Za-z+\-.]*$/', $scheme))
    858         {
    859             $this->scheme = null;
    860             return false;
    861         }
    862         else
    863         {
    864             $this->scheme = strtolower($scheme);
    865         }
    866         return true;
    867     }
    868 
    869     /**
    870      * Set the authority. Returns true on success, false on failure (if there are
    871      * any invalid characters).
    872      *
    873      * @param string $authority
    874      * @return bool
    875      */
    876     public function set_authority($authority, $clear_cache = false)
    877     {
    878         static $cache;
    879         if ($clear_cache)
    880         {
    881             $cache = null;
    882             return;
    883         }
    884         if (!$cache)
    885             $cache = array();
    886 
    887         if ($authority === null)
    888         {
    889             $this->iuserinfo = null;
    890             $this->ihost = null;
    891             $this->port = null;
    892             return true;
    893         }
    894         elseif (isset($cache[$authority]))
    895         {
    896             list($this->iuserinfo,
    897                  $this->ihost,
    898                  $this->port,
    899                  $return) = $cache[$authority];
    900 
    901             return $return;
    902         }
    903 
    904         $remaining = $authority;
    905         if (($iuserinfo_end = strrpos($remaining, '@')) !== false)
    906         {
    907             $iuserinfo = substr($remaining, 0, $iuserinfo_end);
    908             $remaining = substr($remaining, $iuserinfo_end + 1);
    909         }
    910         else
    911         {
    912             $iuserinfo = null;
    913         }
    914         if (($port_start = strpos($remaining, ':', strpos($remaining, ']'))) !== false)
    915         {
    916             if (($port = substr($remaining, $port_start + 1)) === false)
    917             {
    918                 $port = null;
    919             }
    920             $remaining = substr($remaining, 0, $port_start);
    921         }
    922         else
    923         {
    924             $port = null;
    925         }
    926 
    927         $return = $this->set_userinfo($iuserinfo) &&
    928                   $this->set_host($remaining) &&
    929                   $this->set_port($port);
    930 
    931         $cache[$authority] = array($this->iuserinfo,
    932                                    $this->ihost,
    933                                    $this->port,
    934                                    $return);
    935 
    936         return $return;
    937     }
    938 
    939     /**
    940      * Set the iuserinfo.
    941      *
    942      * @param string $iuserinfo
    943      * @return bool
    944      */
    945     public function set_userinfo($iuserinfo)
    946     {
    947         if ($iuserinfo === null)
    948         {
    949             $this->iuserinfo = null;
    950         }
    951         else
    952         {
    953             $this->iuserinfo = $this->replace_invalid_with_pct_encoding($iuserinfo, '!$&\'()*+,;=:');
    954             $this->scheme_normalization();
    955         }
    956 
    957         return true;
    958     }
    959 
    960     /**
    961      * Set the ihost. Returns true on success, false on failure (if there are
    962      * any invalid characters).
    963      *
    964      * @param string $ihost
    965      * @return bool
    966      */
    967     public function set_host($ihost)
    968     {
    969         if ($ihost === null)
    970         {
    971             $this->ihost = null;
    972             return true;
    973         }
    974         elseif (substr($ihost, 0, 1) === '[' && substr($ihost, -1) === ']')
    975         {
    976             if (SimplePie_Net_IPv6::check_ipv6(substr($ihost, 1, -1)))
    977             {
    978                 $this->ihost = '[' . SimplePie_Net_IPv6::compress(substr($ihost, 1, -1)) . ']';
    979             }
    980             else
    981             {
    982                 $this->ihost = null;
    983                 return false;
    984             }
    985         }
    986         else
    987         {
    988             $ihost = $this->replace_invalid_with_pct_encoding($ihost, '!$&\'()*+,;=');
    989 
    990             // Lowercase, but ignore pct-encoded sections (as they should
    991             // remain uppercase). This must be done after the previous step
    992             // as that can add unescaped characters.
    993             $position = 0;
    994             $strlen = strlen($ihost);
    995             while (($position += strcspn($ihost, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ%', $position)) < $strlen)
    996             {
    997                 if ($ihost[$position] === '%')
    998                 {
    999                     $position += 3;
    1000                 }
    1001                 else
    1002                 {
    1003                     $ihost[$position] = strtolower($ihost[$position]);
    1004                     $position++;
    1005                 }
    1006             }
    1007 
    1008             $this->ihost = $ihost;
    1009         }
    1010 
    1011         $this->scheme_normalization();
    1012 
    1013         return true;
    1014     }
    1015 
    1016     /**
    1017      * Set the port. Returns true on success, false on failure (if there are
    1018      * any invalid characters).
    1019      *
    1020      * @param string $port
    1021      * @return bool
    1022      */
    1023     public function set_port($port)
    1024     {
    1025         if ($port === null)
    1026         {
    1027             $this->port = null;
    1028             return true;
    1029         }
    1030         elseif (strspn($port, '0123456789') === strlen($port))
    1031         {
    1032             $this->port = (int) $port;
    1033             $this->scheme_normalization();
    1034             return true;
    1035         }
    1036 
    1037         $this->port = null;
    1038         return false;
    1039     }
    1040 
    1041     /**
    1042      * Set the ipath.
    1043      *
    1044      * @param string $ipath
    1045      * @return bool
    1046      */
    1047     public function set_path($ipath, $clear_cache = false)
    1048     {
    1049         static $cache;
    1050         if ($clear_cache)
    1051         {
    1052             $cache = null;
    1053             return;
    1054         }
    1055         if (!$cache)
    1056         {
    1057             $cache = array();
    1058         }
    1059 
    1060         $ipath = (string) $ipath;
    1061 
    1062         if (isset($cache[$ipath]))
    1063         {
    1064             $this->ipath = $cache[$ipath][(int) ($this->scheme !== null)];
    1065         }
    1066         else
    1067         {
    1068             $valid = $this->replace_invalid_with_pct_encoding($ipath, '!$&\'()*+,;=@:/');
    1069             $removed = $this->remove_dot_segments($valid);
    1070 
    1071             $cache[$ipath] = array($valid, $removed);
    1072             $this->ipath =  ($this->scheme !== null) ? $removed : $valid;
    1073         }
    1074 
    1075         $this->scheme_normalization();
    1076         return true;
    1077     }
    1078 
    1079     /**
    1080      * Set the iquery.
    1081      *
    1082      * @param string $iquery
    1083      * @return bool
    1084      */
    1085     public function set_query($iquery)
    1086     {
    1087         if ($iquery === null)
    1088         {
    1089             $this->iquery = null;
    1090         }
    1091         else
    1092         {
    1093             $this->iquery = $this->replace_invalid_with_pct_encoding($iquery, '!$&\'()*+,;=:@/?', true);
    1094             $this->scheme_normalization();
    1095         }
    1096         return true;
    1097     }
    1098 
    1099     /**
    1100      * Set the ifragment.
    1101      *
    1102      * @param string $ifragment
    1103      * @return bool
    1104      */
    1105     public function set_fragment($ifragment)
    1106     {
    1107         if ($ifragment === null)
    1108         {
    1109             $this->ifragment = null;
    1110         }
    1111         else
    1112         {
    1113             $this->ifragment = $this->replace_invalid_with_pct_encoding($ifragment, '!$&\'()*+,;=:@/?');
    1114             $this->scheme_normalization();
    1115         }
    1116         return true;
    1117     }
    1118 
    1119     /**
    1120      * Convert an IRI to a URI (or parts thereof)
    1121      *
    1122      * @return string
    1123      */
    1124     public function to_uri($string)
    1125     {
    1126         static $non_ascii;
    1127         if (!$non_ascii)
    1128         {
    1129             $non_ascii = implode('', range("\x80", "\xFF"));
    1130         }
    1131 
    1132         $position = 0;
    1133         $strlen = strlen($string);
    1134         while (($position += strcspn($string, $non_ascii, $position)) < $strlen)
    1135         {
    1136             $string = substr_replace($string, sprintf('%%%02X', ord($string[$position])), $position, 1);
    1137             $position += 3;
    1138             $strlen += 2;
    1139         }
    1140 
    1141         return $string;
    1142     }
    1143 
    1144     /**
    1145      * Get the complete IRI
    1146      *
    1147      * @return string
    1148      */
    1149     public function get_iri()
    1150     {
    1151         if (!$this->is_valid())
    1152         {
    1153             return false;
    1154         }
    1155 
    1156         $iri = '';
    1157         if ($this->scheme !== null)
    1158         {
    1159             $iri .= $this->scheme . ':';
    1160         }
    1161         if (($iauthority = $this->get_iauthority()) !== null)
    1162         {
    1163             $iri .= '//' . $iauthority;
    1164         }
    1165         if ($this->ipath !== '')
    1166         {
    1167             $iri .= $this->ipath;
    1168         }
    1169         elseif (!empty($this->normalization[$this->scheme]['ipath']) && $iauthority !== null && $iauthority !== '')
    1170         {
    1171             $iri .= $this->normalization[$this->scheme]['ipath'];
    1172         }
    1173         if ($this->iquery !== null)
    1174         {
    1175             $iri .= '?' . $this->iquery;
    1176         }
    1177         if ($this->ifragment !== null)
    1178         {
    1179             $iri .= '#' . $this->ifragment;
    1180         }
    1181 
    1182         return $iri;
    1183     }
    1184 
    1185     /**
    1186      * Get the complete URI
    1187      *
    1188      * @return string
    1189      */
    1190     public function get_uri()
    1191     {
    1192         return $this->to_uri($this->get_iri());
    1193     }
    1194 
    1195     /**
    1196      * Get the complete iauthority
    1197      *
    1198      * @return string
    1199      */
    1200     protected function get_iauthority()
    1201     {
    1202         if ($this->iuserinfo !== null || $this->ihost !== null || $this->port !== null)
    1203         {
    1204             $iauthority = '';
    1205             if ($this->iuserinfo !== null)
    1206             {
    1207                 $iauthority .= $this->iuserinfo . '@';
    1208             }
    1209             if ($this->ihost !== null)
    1210             {
    1211                 $iauthority .= $this->ihost;
    1212             }
    1213             if ($this->port !== null && $this->port !== 0)
    1214             {
    1215                 $iauthority .= ':' . $this->port;
    1216             }
    1217             return $iauthority;
    1218         }
    1219 
    1220         return null;
    1221     }
    1222 
    1223     /**
    1224      * Get the complete authority
    1225      *
    1226      * @return string
    1227      */
    1228     protected function get_authority()
    1229     {
    1230         $iauthority = $this->get_iauthority();
    1231         if (is_string($iauthority))
    1232             return $this->to_uri($iauthority);
    1233 
    1234         return $iauthority;
    1235     }
     60    /**
     61     * Scheme
     62     *
     63     * @var string
     64     */
     65    protected $scheme = null;
     66
     67    /**
     68     * User Information
     69     *
     70     * @var string
     71     */
     72    protected $iuserinfo = null;
     73
     74    /**
     75     * ihost
     76     *
     77     * @var string
     78     */
     79    protected $ihost = null;
     80
     81    /**
     82     * Port
     83     *
     84     * @var string
     85     */
     86    protected $port = null;
     87
     88    /**
     89     * ipath
     90     *
     91     * @var string
     92     */
     93    protected $ipath = '';
     94
     95    /**
     96     * iquery
     97     *
     98     * @var string
     99     */
     100    protected $iquery = null;
     101
     102    /**
     103     * ifragment
     104     *
     105     * @var string
     106     */
     107    protected $ifragment = null;
     108
     109    /**
     110     * Normalization database
     111     *
     112     * Each key is the scheme, each value is an array with each key as the IRI
     113     * part and value as the default value for that part.
     114     */
     115    protected $normalization = [
     116        'acap' => [
     117            'port' => 674
     118        ],
     119        'dict' => [
     120            'port' => 2628
     121        ],
     122        'file' => [
     123            'ihost' => 'localhost'
     124        ],
     125        'http' => [
     126            'port' => 80,
     127            'ipath' => '/'
     128        ],
     129        'https' => [
     130            'port' => 443,
     131            'ipath' => '/'
     132        ],
     133    ];
     134
     135    /**
     136     * Return the entire IRI when you try and read the object as a string
     137     *
     138     * @return string
     139     */
     140    public function __toString()
     141    {
     142        return $this->get_iri();
     143    }
     144
     145    /**
     146     * Overload __set() to provide access via properties
     147     *
     148     * @param string $name Property name
     149     * @param mixed $value Property value
     150     */
     151    public function __set($name, $value)
     152    {
     153        if (method_exists($this, 'set_' . $name)) {
     154            call_user_func([$this, 'set_' . $name], $value);
     155        } elseif (
     156            $name === 'iauthority'
     157            || $name === 'iuserinfo'
     158            || $name === 'ihost'
     159            || $name === 'ipath'
     160            || $name === 'iquery'
     161            || $name === 'ifragment'
     162        ) {
     163            call_user_func([$this, 'set_' . substr($name, 1)], $value);
     164        }
     165    }
     166
     167    /**
     168     * Overload __get() to provide access via properties
     169     *
     170     * @param string $name Property name
     171     * @return mixed
     172     */
     173    public function __get($name)
     174    {
     175        // isset() returns false for null, we don't want to do that
     176        // Also why we use array_key_exists below instead of isset()
     177        $props = get_object_vars($this);
     178
     179        if (
     180            $name === 'iri' ||
     181            $name === 'uri' ||
     182            $name === 'iauthority' ||
     183            $name === 'authority'
     184        ) {
     185            $return = $this->{"get_$name"}();
     186        } elseif (array_key_exists($name, $props)) {
     187            $return = $this->$name;
     188        }
     189        // host -> ihost
     190        elseif (($prop = 'i' . $name) && array_key_exists($prop, $props)) {
     191            $name = $prop;
     192            $return = $this->$prop;
     193        }
     194        // ischeme -> scheme
     195        elseif (($prop = substr($name, 1)) && array_key_exists($prop, $props)) {
     196            $name = $prop;
     197            $return = $this->$prop;
     198        } else {
     199            trigger_error('Undefined property: ' . get_class($this) . '::' . $name, E_USER_NOTICE);
     200            $return = null;
     201        }
     202
     203        if ($return === null && isset($this->normalization[$this->scheme][$name])) {
     204            return $this->normalization[$this->scheme][$name];
     205        }
     206
     207        return $return;
     208    }
     209
     210    /**
     211     * Overload __isset() to provide access via properties
     212     *
     213     * @param string $name Property name
     214     * @return bool
     215     */
     216    public function __isset($name)
     217    {
     218        return method_exists($this, 'get_' . $name) || isset($this->$name);
     219    }
     220
     221    /**
     222     * Overload __unset() to provide access via properties
     223     *
     224     * @param string $name Property name
     225     */
     226    public function __unset($name)
     227    {
     228        if (method_exists($this, 'set_' . $name)) {
     229            call_user_func([$this, 'set_' . $name], '');
     230        }
     231    }
     232
     233    /**
     234     * Create a new IRI object, from a specified string
     235     *
     236     * @param string $iri
     237     */
     238    public function __construct($iri = null)
     239    {
     240        $this->set_iri($iri);
     241    }
     242
     243    /**
     244     * Clean up
     245     */
     246    public function __destruct()
     247    {
     248        $this->set_iri(null, true);
     249        $this->set_path(null, true);
     250        $this->set_authority(null, true);
     251    }
     252
     253    /**
     254     * Create a new IRI object by resolving a relative IRI
     255     *
     256     * Returns false if $base is not absolute, otherwise an IRI.
     257     *
     258     * @param IRI|string $base (Absolute) Base IRI
     259     * @param IRI|string $relative Relative IRI
     260     * @return IRI|false
     261     */
     262    public static function absolutize($base, $relative)
     263    {
     264        if (!($relative instanceof IRI)) {
     265            $relative = new IRI($relative);
     266        }
     267        if (!$relative->is_valid()) {
     268            return false;
     269        } elseif ($relative->scheme !== null) {
     270            return clone $relative;
     271        } else {
     272            if (!($base instanceof IRI)) {
     273                $base = new IRI($base);
     274            }
     275            if ($base->scheme !== null && $base->is_valid()) {
     276                if ($relative->get_iri() !== '') {
     277                    if ($relative->iuserinfo !== null || $relative->ihost !== null || $relative->port !== null) {
     278                        $target = clone $relative;
     279                        $target->scheme = $base->scheme;
     280                    } else {
     281                        $target = new IRI();
     282                        $target->scheme = $base->scheme;
     283                        $target->iuserinfo = $base->iuserinfo;
     284                        $target->ihost = $base->ihost;
     285                        $target->port = $base->port;
     286                        if ($relative->ipath !== '') {
     287                            if ($relative->ipath[0] === '/') {
     288                                $target->ipath = $relative->ipath;
     289                            } elseif (($base->iuserinfo !== null || $base->ihost !== null || $base->port !== null) && $base->ipath === '') {
     290                                $target->ipath = '/' . $relative->ipath;
     291                            } elseif (($last_segment = strrpos($base->ipath, '/')) !== false) {
     292                                $target->ipath = substr($base->ipath, 0, $last_segment + 1) . $relative->ipath;
     293                            } else {
     294                                $target->ipath = $relative->ipath;
     295                            }
     296                            $target->ipath = $target->remove_dot_segments($target->ipath);
     297                            $target->iquery = $relative->iquery;
     298                        } else {
     299                            $target->ipath = $base->ipath;
     300                            if ($relative->iquery !== null) {
     301                                $target->iquery = $relative->iquery;
     302                            } elseif ($base->iquery !== null) {
     303                                $target->iquery = $base->iquery;
     304                            }
     305                        }
     306                        $target->ifragment = $relative->ifragment;
     307                    }
     308                } else {
     309                    $target = clone $base;
     310                    $target->ifragment = null;
     311                }
     312                $target->scheme_normalization();
     313                return $target;
     314            }
     315
     316            return false;
     317        }
     318    }
     319
     320    /**
     321     * Parse an IRI into scheme/authority/path/query/fragment segments
     322     *
     323     * @param string $iri
     324     * @return array
     325     */
     326    protected function parse_iri($iri)
     327    {
     328        $iri = trim($iri, "\x20\x09\x0A\x0C\x0D");
     329        if (preg_match('/^((?P<scheme>[^:\/?#]+):)?(\/\/(?P<authority>[^\/?#]*))?(?P<path>[^?#]*)(\?(?P<query>[^#]*))?(#(?P<fragment>.*))?$/', $iri, $match)) {
     330            if ($match[1] === '') {
     331                $match['scheme'] = null;
     332            }
     333            if (!isset($match[3]) || $match[3] === '') {
     334                $match['authority'] = null;
     335            }
     336            if (!isset($match[5])) {
     337                $match['path'] = '';
     338            }
     339            if (!isset($match[6]) || $match[6] === '') {
     340                $match['query'] = null;
     341            }
     342            if (!isset($match[8]) || $match[8] === '') {
     343                $match['fragment'] = null;
     344            }
     345            return $match;
     346        }
     347
     348        // This can occur when a paragraph is accidentally parsed as a URI
     349        return false;
     350    }
     351
     352    /**
     353     * Remove dot segments from a path
     354     *
     355     * @param string $input
     356     * @return string
     357     */
     358    protected function remove_dot_segments($input)
     359    {
     360        $output = '';
     361        while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..') {
     362            // A: If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise,
     363            if (strpos($input, '../') === 0) {
     364                $input = substr($input, 3);
     365            } elseif (strpos($input, './') === 0) {
     366                $input = substr($input, 2);
     367            }
     368            // B: if the input buffer begins with a prefix of "/./" or "/.", where "." is a complete path segment, then replace that prefix with "/" in the input buffer; otherwise,
     369            elseif (strpos($input, '/./') === 0) {
     370                $input = substr($input, 2);
     371            } elseif ($input === '/.') {
     372                $input = '/';
     373            }
     374            // C: if the input buffer begins with a prefix of "/../" or "/..", where ".." is a complete path segment, then replace that prefix with "/" in the input buffer and remove the last segment and its preceding "/" (if any) from the output buffer; otherwise,
     375            elseif (strpos($input, '/../') === 0) {
     376                $input = substr($input, 3);
     377                $output = substr_replace($output, '', intval(strrpos($output, '/')));
     378            } elseif ($input === '/..') {
     379                $input = '/';
     380                $output = substr_replace($output, '', intval(strrpos($output, '/')));
     381            }
     382            // D: if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise,
     383            elseif ($input === '.' || $input === '..') {
     384                $input = '';
     385            }
     386            // E: move the first path segment in the input buffer to the end of the output buffer, including the initial "/" character (if any) and any subsequent characters up to, but not including, the next "/" character or the end of the input buffer
     387            elseif (($pos = strpos($input, '/', 1)) !== false) {
     388                $output .= substr($input, 0, $pos);
     389                $input = substr_replace($input, '', 0, $pos);
     390            } else {
     391                $output .= $input;
     392                $input = '';
     393            }
     394        }
     395        return $output . $input;
     396    }
     397
     398    /**
     399     * Replace invalid character with percent encoding
     400     *
     401     * @param string $string Input string
     402     * @param string $extra_chars Valid characters not in iunreserved or
     403     *                            iprivate (this is ASCII-only)
     404     * @param bool $iprivate Allow iprivate
     405     * @return string
     406     */
     407    protected function replace_invalid_with_pct_encoding($string, $extra_chars, $iprivate = false)
     408    {
     409        // Normalize as many pct-encoded sections as possible
     410        $string = preg_replace_callback('/(?:%[A-Fa-f0-9]{2})+/', [$this, 'remove_iunreserved_percent_encoded'], $string);
     411
     412        // Replace invalid percent characters
     413        $string = preg_replace('/%(?![A-Fa-f0-9]{2})/', '%25', $string);
     414
     415        // Add unreserved and % to $extra_chars (the latter is safe because all
     416        // pct-encoded sections are now valid).
     417        $extra_chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~%';
     418
     419        // Now replace any bytes that aren't allowed with their pct-encoded versions
     420        $position = 0;
     421        $strlen = strlen($string);
     422        while (($position += strspn($string, $extra_chars, $position)) < $strlen) {
     423            $value = ord($string[$position]);
     424            $character = 0;
     425
     426            // Start position
     427            $start = $position;
     428
     429            // By default we are valid
     430            $valid = true;
     431
     432            // No one byte sequences are valid due to the while.
     433            // Two byte sequence:
     434            if (($value & 0xE0) === 0xC0) {
     435                $character = ($value & 0x1F) << 6;
     436                $length = 2;
     437                $remaining = 1;
     438            }
     439            // Three byte sequence:
     440            elseif (($value & 0xF0) === 0xE0) {
     441                $character = ($value & 0x0F) << 12;
     442                $length = 3;
     443                $remaining = 2;
     444            }
     445            // Four byte sequence:
     446            elseif (($value & 0xF8) === 0xF0) {
     447                $character = ($value & 0x07) << 18;
     448                $length = 4;
     449                $remaining = 3;
     450            }
     451            // Invalid byte:
     452            else {
     453                $valid = false;
     454                $length = 1;
     455                $remaining = 0;
     456            }
     457
     458            if ($remaining) {
     459                if ($position + $length <= $strlen) {
     460                    for ($position++; $remaining; $position++) {
     461                        $value = ord($string[$position]);
     462
     463                        // Check that the byte is valid, then add it to the character:
     464                        if (($value & 0xC0) === 0x80) {
     465                            $character |= ($value & 0x3F) << (--$remaining * 6);
     466                        }
     467                        // If it is invalid, count the sequence as invalid and reprocess the current byte:
     468                        else {
     469                            $valid = false;
     470                            $position--;
     471                            break;
     472                        }
     473                    }
     474                } else {
     475                    $position = $strlen - 1;
     476                    $valid = false;
     477                }
     478            }
     479
     480            // Percent encode anything invalid or not in ucschar
     481            if (
     482                // Invalid sequences
     483                !$valid
     484                // Non-shortest form sequences are invalid
     485                || $length > 1 && $character <= 0x7F
     486                || $length > 2 && $character <= 0x7FF
     487                || $length > 3 && $character <= 0xFFFF
     488                // Outside of range of ucschar codepoints
     489                // Noncharacters
     490                || ($character & 0xFFFE) === 0xFFFE
     491                || $character >= 0xFDD0 && $character <= 0xFDEF
     492                || (
     493                    // Everything else not in ucschar
     494                    $character > 0xD7FF && $character < 0xF900
     495                    || $character < 0xA0
     496                    || $character > 0xEFFFD
     497                )
     498                && (
     499                    // Everything not in iprivate, if it applies
     500                    !$iprivate
     501                    || $character < 0xE000
     502                    || $character > 0x10FFFD
     503                )
     504            ) {
     505                // If we were a character, pretend we weren't, but rather an error.
     506                if ($valid) {
     507                    $position--;
     508                }
     509
     510                for ($j = $start; $j <= $position; $j++) {
     511                    $string = substr_replace($string, sprintf('%%%02X', ord($string[$j])), $j, 1);
     512                    $j += 2;
     513                    $position += 2;
     514                    $strlen += 2;
     515                }
     516            }
     517        }
     518
     519        return $string;
     520    }
     521
     522    /**
     523     * Callback function for preg_replace_callback.
     524     *
     525     * Removes sequences of percent encoded bytes that represent UTF-8
     526     * encoded characters in iunreserved
     527     *
     528     * @param array $match PCRE match
     529     * @return string Replacement
     530     */
     531    protected function remove_iunreserved_percent_encoded($match)
     532    {
     533        // As we just have valid percent encoded sequences we can just explode
     534        // and ignore the first member of the returned array (an empty string).
     535        $bytes = explode('%', $match[0]);
     536
     537        // Initialize the new string (this is what will be returned) and that
     538        // there are no bytes remaining in the current sequence (unsurprising
     539        // at the first byte!).
     540        $string = '';
     541        $remaining = 0;
     542
     543        // these variables will be initialized in the loop but PHPStan is not able to detect it currently
     544        $start = 0;
     545        $character = 0;
     546        $length = 0;
     547        $valid = true;
     548
     549        // Loop over each and every byte, and set $value to its value
     550        for ($i = 1, $len = count($bytes); $i < $len; $i++) {
     551            $value = hexdec($bytes[$i]);
     552
     553            // If we're the first byte of sequence:
     554            if (!$remaining) {
     555                // Start position
     556                $start = $i;
     557
     558                // By default we are valid
     559                $valid = true;
     560
     561                // One byte sequence:
     562                if ($value <= 0x7F) {
     563                    $character = $value;
     564                    $length = 1;
     565                }
     566                // Two byte sequence:
     567                elseif (($value & 0xE0) === 0xC0) {
     568                    $character = ($value & 0x1F) << 6;
     569                    $length = 2;
     570                    $remaining = 1;
     571                }
     572                // Three byte sequence:
     573                elseif (($value & 0xF0) === 0xE0) {
     574                    $character = ($value & 0x0F) << 12;
     575                    $length = 3;
     576                    $remaining = 2;
     577                }
     578                // Four byte sequence:
     579                elseif (($value & 0xF8) === 0xF0) {
     580                    $character = ($value & 0x07) << 18;
     581                    $length = 4;
     582                    $remaining = 3;
     583                }
     584                // Invalid byte:
     585                else {
     586                    $valid = false;
     587                    $remaining = 0;
     588                }
     589            }
     590            // Continuation byte:
     591            else {
     592                // Check that the byte is valid, then add it to the character:
     593                if (($value & 0xC0) === 0x80) {
     594                    $remaining--;
     595                    $character |= ($value & 0x3F) << ($remaining * 6);
     596                }
     597                // If it is invalid, count the sequence as invalid and reprocess the current byte as the start of a sequence:
     598                else {
     599                    $valid = false;
     600                    $remaining = 0;
     601                    $i--;
     602                }
     603            }
     604
     605            // If we've reached the end of the current byte sequence, append it to Unicode::$data
     606            if (!$remaining) {
     607                // Percent encode anything invalid or not in iunreserved
     608                if (
     609                    // Invalid sequences
     610                    !$valid
     611                    // Non-shortest form sequences are invalid
     612                    || $length > 1 && $character <= 0x7F
     613                    || $length > 2 && $character <= 0x7FF
     614                    || $length > 3 && $character <= 0xFFFF
     615                    // Outside of range of iunreserved codepoints
     616                    || $character < 0x2D
     617                    || $character > 0xEFFFD
     618                    // Noncharacters
     619                    || ($character & 0xFFFE) === 0xFFFE
     620                    || $character >= 0xFDD0 && $character <= 0xFDEF
     621                    // Everything else not in iunreserved (this is all BMP)
     622                    || $character === 0x2F
     623                    || $character > 0x39 && $character < 0x41
     624                    || $character > 0x5A && $character < 0x61
     625                    || $character > 0x7A && $character < 0x7E
     626                    || $character > 0x7E && $character < 0xA0
     627                    || $character > 0xD7FF && $character < 0xF900
     628                ) {
     629                    for ($j = $start; $j <= $i; $j++) {
     630                        $string .= '%' . strtoupper($bytes[$j]);
     631                    }
     632                } else {
     633                    for ($j = $start; $j <= $i; $j++) {
     634                        $string .= chr(hexdec($bytes[$j]));
     635                    }
     636                }
     637            }
     638        }
     639
     640        // If we have any bytes left over they are invalid (i.e., we are
     641        // mid-way through a multi-byte sequence)
     642        if ($remaining) {
     643            for ($j = $start; $j < $len; $j++) {
     644                $string .= '%' . strtoupper($bytes[$j]);
     645            }
     646        }
     647
     648        return $string;
     649    }
     650
     651    protected function scheme_normalization()
     652    {
     653        if (isset($this->normalization[$this->scheme]['iuserinfo']) && $this->iuserinfo === $this->normalization[$this->scheme]['iuserinfo']) {
     654            $this->iuserinfo = null;
     655        }
     656        if (isset($this->normalization[$this->scheme]['ihost']) && $this->ihost === $this->normalization[$this->scheme]['ihost']) {
     657            $this->ihost = null;
     658        }
     659        if (isset($this->normalization[$this->scheme]['port']) && $this->port === $this->normalization[$this->scheme]['port']) {
     660            $this->port = null;
     661        }
     662        if (isset($this->normalization[$this->scheme]['ipath']) && $this->ipath === $this->normalization[$this->scheme]['ipath']) {
     663            $this->ipath = '';
     664        }
     665        if (isset($this->normalization[$this->scheme]['iquery']) && $this->iquery === $this->normalization[$this->scheme]['iquery']) {
     666            $this->iquery = null;
     667        }
     668        if (isset($this->normalization[$this->scheme]['ifragment']) && $this->ifragment === $this->normalization[$this->scheme]['ifragment']) {
     669            $this->ifragment = null;
     670        }
     671    }
     672
     673    /**
     674     * Check if the object represents a valid IRI. This needs to be done on each
     675     * call as some things change depending on another part of the IRI.
     676     *
     677     * @return bool
     678     */
     679    public function is_valid()
     680    {
     681        if ($this->ipath === '') {
     682            return true;
     683        }
     684
     685        $isauthority = $this->iuserinfo !== null || $this->ihost !== null ||
     686            $this->port !== null;
     687        if ($isauthority && $this->ipath[0] === '/') {
     688            return true;
     689        }
     690
     691        if (!$isauthority && (substr($this->ipath, 0, 2) === '//')) {
     692            return false;
     693        }
     694
     695        // Relative urls cannot have a colon in the first path segment (and the
     696        // slashes themselves are not included so skip the first character).
     697        if (!$this->scheme && !$isauthority &&
     698            strpos($this->ipath, ':') !== false &&
     699            strpos($this->ipath, '/', 1) !== false &&
     700            strpos($this->ipath, ':') < strpos($this->ipath, '/', 1)) {
     701            return false;
     702        }
     703
     704        return true;
     705    }
     706
     707    /**
     708     * Set the entire IRI. Returns true on success, false on failure (if there
     709     * are any invalid characters).
     710     *
     711     * @param string $iri
     712     * @return bool
     713     */
     714    public function set_iri($iri, $clear_cache = false)
     715    {
     716        static $cache;
     717        if ($clear_cache) {
     718            $cache = null;
     719            return;
     720        }
     721        if (!$cache) {
     722            $cache = [];
     723        }
     724
     725        if ($iri === null) {
     726            return true;
     727        } elseif (isset($cache[$iri])) {
     728            [
     729                $this->scheme,
     730                $this->iuserinfo,
     731                $this->ihost,
     732                $this->port,
     733                $this->ipath,
     734                $this->iquery,
     735                $this->ifragment,
     736                $return
     737            ] = $cache[$iri];
     738
     739            return $return;
     740        }
     741
     742        $parsed = $this->parse_iri((string) $iri);
     743        if (!$parsed) {
     744            return false;
     745        }
     746
     747        $return = $this->set_scheme($parsed['scheme'])
     748            && $this->set_authority($parsed['authority'])
     749            && $this->set_path($parsed['path'])
     750            && $this->set_query($parsed['query'])
     751            && $this->set_fragment($parsed['fragment']);
     752
     753        $cache[$iri] = [
     754            $this->scheme,
     755            $this->iuserinfo,
     756            $this->ihost,
     757            $this->port,
     758            $this->ipath,
     759            $this->iquery,
     760            $this->ifragment,
     761            $return
     762        ];
     763
     764        return $return;
     765    }
     766
     767    /**
     768     * Set the scheme. Returns true on success, false on failure (if there are
     769     * any invalid characters).
     770     *
     771     * @param string $scheme
     772     * @return bool
     773     */
     774    public function set_scheme($scheme)
     775    {
     776        if ($scheme === null) {
     777            $this->scheme = null;
     778        } elseif (!preg_match('/^[A-Za-z][0-9A-Za-z+\-.]*$/', $scheme)) {
     779            $this->scheme = null;
     780            return false;
     781        } else {
     782            $this->scheme = strtolower($scheme);
     783        }
     784        return true;
     785    }
     786
     787    /**
     788     * Set the authority. Returns true on success, false on failure (if there are
     789     * any invalid characters).
     790     *
     791     * @param string $authority
     792     * @return bool
     793     */
     794    public function set_authority($authority, $clear_cache = false)
     795    {
     796        static $cache;
     797        if ($clear_cache) {
     798            $cache = null;
     799            return;
     800        }
     801        if (!$cache) {
     802            $cache = [];
     803        }
     804
     805        if ($authority === null) {
     806            $this->iuserinfo = null;
     807            $this->ihost = null;
     808            $this->port = null;
     809            return true;
     810        } elseif (isset($cache[$authority])) {
     811            [
     812                $this->iuserinfo,
     813                $this->ihost,
     814                $this->port,
     815                $return
     816            ] = $cache[$authority];
     817
     818            return $return;
     819        }
     820
     821        $remaining = $authority;
     822        if (($iuserinfo_end = strrpos($remaining, '@')) !== false) {
     823            $iuserinfo = substr($remaining, 0, $iuserinfo_end);
     824            $remaining = substr($remaining, $iuserinfo_end + 1);
     825        } else {
     826            $iuserinfo = null;
     827        }
     828        if (($port_start = strpos($remaining, ':', intval(strpos($remaining, ']')))) !== false) {
     829            if (($port = substr($remaining, $port_start + 1)) === false) {
     830                $port = null;
     831            }
     832            $remaining = substr($remaining, 0, $port_start);
     833        } else {
     834            $port = null;
     835        }
     836
     837        $return = $this->set_userinfo($iuserinfo) &&
     838                  $this->set_host($remaining) &&
     839                  $this->set_port($port);
     840
     841        $cache[$authority] = [
     842            $this->iuserinfo,
     843            $this->ihost,
     844            $this->port,
     845            $return
     846        ];
     847
     848        return $return;
     849    }
     850
     851    /**
     852     * Set the iuserinfo.
     853     *
     854     * @param string $iuserinfo
     855     * @return bool
     856     */
     857    public function set_userinfo($iuserinfo)
     858    {
     859        if ($iuserinfo === null) {
     860            $this->iuserinfo = null;
     861        } else {
     862            $this->iuserinfo = $this->replace_invalid_with_pct_encoding($iuserinfo, '!$&\'()*+,;=:');
     863            $this->scheme_normalization();
     864        }
     865
     866        return true;
     867    }
     868
     869    /**
     870     * Set the ihost. Returns true on success, false on failure (if there are
     871     * any invalid characters).
     872     *
     873     * @param string $ihost
     874     * @return bool
     875     */
     876    public function set_host($ihost)
     877    {
     878        if ($ihost === null) {
     879            $this->ihost = null;
     880            return true;
     881        } elseif (substr($ihost, 0, 1) === '[' && substr($ihost, -1) === ']') {
     882            if (\SimplePie\Net\IPv6::check_ipv6(substr($ihost, 1, -1))) {
     883                $this->ihost = '[' . \SimplePie\Net\IPv6::compress(substr($ihost, 1, -1)) . ']';
     884            } else {
     885                $this->ihost = null;
     886                return false;
     887            }
     888        } else {
     889            $ihost = $this->replace_invalid_with_pct_encoding($ihost, '!$&\'()*+,;=');
     890
     891            // Lowercase, but ignore pct-encoded sections (as they should
     892            // remain uppercase). This must be done after the previous step
     893            // as that can add unescaped characters.
     894            $position = 0;
     895            $strlen = strlen($ihost);
     896            while (($position += strcspn($ihost, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ%', $position)) < $strlen) {
     897                if ($ihost[$position] === '%') {
     898                    $position += 3;
     899                } else {
     900                    $ihost[$position] = strtolower($ihost[$position]);
     901                    $position++;
     902                }
     903            }
     904
     905            $this->ihost = $ihost;
     906        }
     907
     908        $this->scheme_normalization();
     909
     910        return true;
     911    }
     912
     913    /**
     914     * Set the port. Returns true on success, false on failure (if there are
     915     * any invalid characters).
     916     *
     917     * @param string $port
     918     * @return bool
     919     */
     920    public function set_port($port)
     921    {
     922        if ($port === null) {
     923            $this->port = null;
     924            return true;
     925        } elseif (strspn($port, '0123456789') === strlen($port)) {
     926            $this->port = (int) $port;
     927            $this->scheme_normalization();
     928            return true;
     929        }
     930
     931        $this->port = null;
     932        return false;
     933    }
     934
     935    /**
     936     * Set the ipath.
     937     *
     938     * @param string $ipath
     939     * @return bool
     940     */
     941    public function set_path($ipath, $clear_cache = false)
     942    {
     943        static $cache;
     944        if ($clear_cache) {
     945            $cache = null;
     946            return;
     947        }
     948        if (!$cache) {
     949            $cache = [];
     950        }
     951
     952        $ipath = (string) $ipath;
     953
     954        if (isset($cache[$ipath])) {
     955            $this->ipath = $cache[$ipath][(int) ($this->scheme !== null)];
     956        } else {
     957            $valid = $this->replace_invalid_with_pct_encoding($ipath, '!$&\'()*+,;=@:/');
     958            $removed = $this->remove_dot_segments($valid);
     959
     960            $cache[$ipath] = [$valid, $removed];
     961            $this->ipath =  ($this->scheme !== null) ? $removed : $valid;
     962        }
     963
     964        $this->scheme_normalization();
     965        return true;
     966    }
     967
     968    /**
     969     * Set the iquery.
     970     *
     971     * @param string $iquery
     972     * @return bool
     973     */
     974    public function set_query($iquery)
     975    {
     976        if ($iquery === null) {
     977            $this->iquery = null;
     978        } else {
     979            $this->iquery = $this->replace_invalid_with_pct_encoding($iquery, '!$&\'()*+,;=:@/?', true);
     980            $this->scheme_normalization();
     981        }
     982        return true;
     983    }
     984
     985    /**
     986     * Set the ifragment.
     987     *
     988     * @param string $ifragment
     989     * @return bool
     990     */
     991    public function set_fragment($ifragment)
     992    {
     993        if ($ifragment === null) {
     994            $this->ifragment = null;
     995        } else {
     996            $this->ifragment = $this->replace_invalid_with_pct_encoding($ifragment, '!$&\'()*+,;=:@/?');
     997            $this->scheme_normalization();
     998        }
     999        return true;
     1000    }
     1001
     1002    /**
     1003     * Convert an IRI to a URI (or parts thereof)
     1004     *
     1005     * @return string
     1006     */
     1007    public function to_uri($string)
     1008    {
     1009        static $non_ascii;
     1010        if (!$non_ascii) {
     1011            $non_ascii = implode('', range("\x80", "\xFF"));
     1012        }
     1013
     1014        $position = 0;
     1015        $strlen = strlen($string);
     1016        while (($position += strcspn($string, $non_ascii, $position)) < $strlen) {
     1017            $string = substr_replace($string, sprintf('%%%02X', ord($string[$position])), $position, 1);
     1018            $position += 3;
     1019            $strlen += 2;
     1020        }
     1021
     1022        return $string;
     1023    }
     1024
     1025    /**
     1026     * Get the complete IRI
     1027     *
     1028     * @return string
     1029     */
     1030    public function get_iri()
     1031    {
     1032        if (!$this->is_valid()) {
     1033            return false;
     1034        }
     1035
     1036        $iri = '';
     1037        if ($this->scheme !== null) {
     1038            $iri .= $this->scheme . ':';
     1039        }
     1040        if (($iauthority = $this->get_iauthority()) !== null) {
     1041            $iri .= '//' . $iauthority;
     1042        }
     1043        if ($this->ipath !== '') {
     1044            $iri .= $this->ipath;
     1045        } elseif (!empty($this->normalization[$this->scheme]['ipath']) && $iauthority !== null && $iauthority !== '') {
     1046            $iri .= $this->normalization[$this->scheme]['ipath'];
     1047        }
     1048        if ($this->iquery !== null) {
     1049            $iri .= '?' . $this->iquery;
     1050        }
     1051        if ($this->ifragment !== null) {
     1052            $iri .= '#' . $this->ifragment;
     1053        }
     1054
     1055        return $iri;
     1056    }
     1057
     1058    /**
     1059     * Get the complete URI
     1060     *
     1061     * @return string
     1062     */
     1063    public function get_uri()
     1064    {
     1065        return $this->to_uri($this->get_iri());
     1066    }
     1067
     1068    /**
     1069     * Get the complete iauthority
     1070     *
     1071     * @return string
     1072     */
     1073    protected function get_iauthority()
     1074    {
     1075        if ($this->iuserinfo !== null || $this->ihost !== null || $this->port !== null) {
     1076            $iauthority = '';
     1077            if ($this->iuserinfo !== null) {
     1078                $iauthority .= $this->iuserinfo . '@';
     1079            }
     1080            if ($this->ihost !== null) {
     1081                $iauthority .= $this->ihost;
     1082            }
     1083            if ($this->port !== null && $this->port !== 0) {
     1084                $iauthority .= ':' . $this->port;
     1085            }
     1086            return $iauthority;
     1087        }
     1088
     1089        return null;
     1090    }
     1091
     1092    /**
     1093     * Get the complete authority
     1094     *
     1095     * @return string
     1096     */
     1097    protected function get_authority()
     1098    {
     1099        $iauthority = $this->get_iauthority();
     1100        if (is_string($iauthority)) {
     1101            return $this->to_uri($iauthority);
     1102        }
     1103
     1104        return $iauthority;
     1105    }
    12361106}
     1107
     1108class_alias('SimplePie\IRI', 'SimplePie_IRI');
  • trunk/src/wp-includes/SimplePie/src/Item.php

    r59140 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie;
    4446
    4547/**
    4648 * Manages all item-related data
    4749 *
    48  * Used by {@see SimplePie::get_item()} and {@see SimplePie::get_items()}
     50 * Used by {@see \SimplePie\SimplePie::get_item()} and {@see \SimplePie\SimplePie::get_items()}
    4951 *
    50  * This class can be overloaded with {@see SimplePie::set_item_class()}
     52 * This class can be overloaded with {@see \SimplePie\SimplePie::set_item_class()}
    5153 *
    52  * @package SimplePie
     54 * @package \SimplePie\SimplePie
    5355 * @subpackage API
    5456 */
    55 class SimplePie_Item
     57class Item implements RegistryAware
    5658{
    57     /**
    58      * Parent feed
    59      *
    60      * @access private
    61      * @var SimplePie
    62      */
    63     var $feed;
    64 
    65     /**
    66      * Raw data
    67      *
    68      * @access private
    69      * @var array
    70      */
    71     var $data = array();
    72 
    73     /**
    74      * Registry object
    75      *
    76      * @see set_registry
    77      * @var SimplePie_Registry
    78      */
    79     protected $registry;
    80 
    81     /**
    82      * Create a new item object
    83      *
    84      * This is usually used by {@see SimplePie::get_items} and
    85      * {@see SimplePie::get_item}. Avoid creating this manually.
    86      *
    87      * @param SimplePie $feed Parent feed
    88      * @param array $data Raw data
    89      */
    90     public function __construct($feed, $data)
    91     {
    92         $this->feed = $feed;
    93         $this->data = $data;
    94     }
    95 
    96     /**
    97      * Set the registry handler
    98      *
    99      * This is usually used by {@see SimplePie_Registry::create}
    100      *
    101      * @since 1.3
    102      * @param SimplePie_Registry $registry
    103      */
    104     public function set_registry(SimplePie_Registry $registry)
    105     {
    106         $this->registry = $registry;
    107     }
    108 
    109     /**
    110      * Get a string representation of the item
    111      *
    112      * @return string
    113      */
    114     public function __toString()
    115     {
    116         return md5(serialize($this->data));
    117     }
    118 
    119     /**
    120      * Remove items that link back to this before destroying this object
    121      */
    122     public function __destruct()
    123     {
    124         if (!gc_enabled())
    125         {
    126             unset($this->feed);
    127         }
    128     }
    129 
    130     /**
    131      * Get data for an item-level element
    132      *
    133      * This method allows you to get access to ANY element/attribute that is a
    134      * sub-element of the item/entry tag.
    135      *
    136      * See {@see SimplePie::get_feed_tags()} for a description of the return value
    137      *
    138      * @since 1.0
    139      * @see http://simplepie.org/wiki/faq/supported_xml_namespaces
    140      * @param string $namespace The URL of the XML namespace of the elements you're trying to access
    141      * @param string $tag Tag name
    142      * @return array
    143      */
    144     public function get_item_tags($namespace, $tag)
    145     {
    146         if (isset($this->data['child'][$namespace][$tag]))
    147         {
    148             return $this->data['child'][$namespace][$tag];
    149         }
    150 
    151         return null;
    152     }
    153 
    154     /**
    155      * Get the base URL value from the parent feed
    156      *
    157      * Uses `<xml:base>`
    158      *
    159      * @param array $element
    160      * @return string
    161      */
    162     public function get_base($element = array())
    163     {
    164         return $this->feed->get_base($element);
    165     }
    166 
    167     /**
    168      * Sanitize feed data
    169      *
    170      * @access private
    171      * @see SimplePie::sanitize()
    172      * @param string $data Data to sanitize
    173      * @param int $type One of the SIMPLEPIE_CONSTRUCT_* constants
    174      * @param string $base Base URL to resolve URLs against
    175      * @return string Sanitized data
    176      */
    177     public function sanitize($data, $type, $base = '')
    178     {
    179         return $this->feed->sanitize($data, $type, $base);
    180     }
    181 
    182     /**
    183      * Get the parent feed
    184      *
    185      * Note: this may not work as you think for multifeeds!
    186      *
    187      * @link http://simplepie.org/faq/typical_multifeed_gotchas#missing_data_from_feed
    188      * @since 1.0
    189      * @return SimplePie
    190      */
    191     public function get_feed()
    192     {
    193         return $this->feed;
    194     }
    195 
    196     /**
    197      * Get the unique identifier for the item
    198      *
    199      * This is usually used when writing code to check for new items in a feed.
    200      *
    201      * Uses `<atom:id>`, `<guid>`, `<dc:identifier>` or the `about` attribute
    202      * for RDF. If none of these are supplied (or `$hash` is true), creates an
    203      * MD5 hash based on the permalink, title and content.
    204      *
    205      * @since Beta 2
    206      * @param boolean $hash Should we force using a hash instead of the supplied ID?
    207      * @param string|false $fn User-supplied function to generate an hash
    208      * @return string|null
    209      */
    210     public function get_id($hash = false, $fn = 'md5')
    211     {
    212         if (!$hash)
    213         {
    214             if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'id'))
    215             {
    216                 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    217             }
    218             elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'id'))
    219             {
    220                 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    221             }
    222             elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'guid'))
    223             {
    224                 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    225             }
    226             elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'identifier'))
    227             {
    228                 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    229             }
    230             elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'identifier'))
    231             {
    232                 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    233             }
    234             elseif (isset($this->data['attribs'][SIMPLEPIE_NAMESPACE_RDF]['about']))
    235             {
    236                 return $this->sanitize($this->data['attribs'][SIMPLEPIE_NAMESPACE_RDF]['about'], SIMPLEPIE_CONSTRUCT_TEXT);
    237             }
    238         }
    239         if ($fn === false)
    240         {
    241             return null;
    242         }
    243         elseif (!is_callable($fn))
    244         {
    245             trigger_error('User-supplied function $fn must be callable', E_USER_WARNING);
    246             $fn = 'md5';
    247         }
    248         return call_user_func($fn,
    249                $this->get_permalink().$this->get_title().$this->get_content());
    250     }
    251 
    252     /**
    253      * Get the title of the item
    254      *
    255      * Uses `<atom:title>`, `<title>` or `<dc:title>`
    256      *
    257      * @since Beta 2 (previously called `get_item_title` since 0.8)
    258      * @return string|null
    259      */
    260     public function get_title()
    261     {
    262         if (!isset($this->data['title']))
    263         {
    264             if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title'))
    265             {
    266                 $this->data['title'] = $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
    267             }
    268             elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title'))
    269             {
    270                 $this->data['title'] = $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
    271             }
    272             elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
    273             {
    274                 $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
    275             }
    276             elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
    277             {
    278                 $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
    279             }
    280             elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
    281             {
    282                 $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
    283             }
    284             elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
    285             {
    286                 $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    287             }
    288             elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
    289             {
    290                 $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    291             }
    292             else
    293             {
    294                 $this->data['title'] = null;
    295             }
    296         }
    297         return $this->data['title'];
    298     }
    299 
    300     /**
    301      * Get the content for the item
    302      *
    303      * Prefers summaries over full content , but will return full content if a
    304      * summary does not exist.
    305      *
    306      * To prefer full content instead, use {@see get_content}
    307      *
    308      * Uses `<atom:summary>`, `<description>`, `<dc:description>` or
    309      * `<itunes:subtitle>`
    310      *
    311      * @since 0.8
    312      * @param boolean $description_only Should we avoid falling back to the content?
    313      * @return string|null
    314      */
    315     public function get_description($description_only = false)
    316     {
    317         if (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'summary')) &&
    318             ($return = $this->sanitize($tags[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($tags[0]['attribs'])), $this->get_base($tags[0]))))
    319         {
    320             return $return;
    321         }
    322         elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'summary')) &&
    323                 ($return = $this->sanitize($tags[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($tags[0]['attribs'])), $this->get_base($tags[0]))))
    324         {
    325             return $return;
    326         }
    327         elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) &&
    328                 ($return = $this->sanitize($tags[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($tags[0]))))
    329         {
    330             return $return;
    331         }
    332         elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description')) &&
    333                 ($return = $this->sanitize($tags[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($tags[0]))))
    334         {
    335             return $return;
    336         }
    337         elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) &&
    338                 ($return = $this->sanitize($tags[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)))
    339         {
    340             return $return;
    341         }
    342         elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) &&
    343                 ($return = $this->sanitize($tags[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)))
    344         {
    345             return $return;
    346         }
    347         elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) &&
    348                 ($return = $this->sanitize($tags[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($tags[0]))))
    349         {
    350             return $return;
    351         }
    352         elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) &&
    353                 ($return = $this->sanitize($tags[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)))
    354         {
    355             return $return;
    356         }
    357         elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description')) &&
    358                 ($return = $this->sanitize($tags[0]['data'], SIMPLEPIE_CONSTRUCT_HTML)))
    359         {
    360             return $return;
    361         }
    362 
    363         elseif (!$description_only)
    364         {
    365             return $this->get_content(true);
    366         }
    367 
    368         return null;
    369     }
    370 
    371     /**
    372      * Get the content for the item
    373      *
    374      * Prefers full content over summaries, but will return a summary if full
    375      * content does not exist.
    376      *
    377      * To prefer summaries instead, use {@see get_description}
    378      *
    379      * Uses `<atom:content>` or `<content:encoded>` (RSS 1.0 Content Module)
    380      *
    381      * @since 1.0
    382      * @param boolean $content_only Should we avoid falling back to the description?
    383      * @return string|null
    384      */
    385     public function get_content($content_only = false)
    386     {
    387         if (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'content')) &&
    388             ($return = $this->sanitize($tags[0]['data'], $this->registry->call('Misc', 'atom_10_content_construct_type', array($tags[0]['attribs'])), $this->get_base($tags[0]))))
    389         {
    390             return $return;
    391         }
    392         elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'content')) &&
    393                 ($return = $this->sanitize($tags[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($tags[0]['attribs'])), $this->get_base($tags[0]))))
    394         {
    395             return $return;
    396         }
    397         elseif (($tags = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT, 'encoded')) &&
    398                 ($return = $this->sanitize($tags[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($tags[0]))))
    399         {
    400             return $return;
    401         }
    402         elseif (!$content_only)
    403         {
    404             return $this->get_description(true);
    405         }
    406 
    407         return null;
    408     }
    409 
    410     /**
    411      * Get the media:thumbnail of the item
    412      *
    413      * Uses `<media:thumbnail>`
    414      *
    415      *
    416      * @return array|null
    417      */
    418     public function get_thumbnail()
    419     {
    420         if (!isset($this->data['thumbnail']))
    421         {
    422             if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail'))
    423             {
    424                 $this->data['thumbnail'] = $return[0]['attribs'][''];
    425             }
    426             else
    427             {
    428                 $this->data['thumbnail'] = null;
    429             }
    430         }
    431         return $this->data['thumbnail'];
    432     }
    433 
    434     /**
    435      * Get a category for the item
    436      *
    437      * @since Beta 3 (previously called `get_categories()` since Beta 2)
    438      * @param int $key The category that you want to return.  Remember that arrays begin with 0, not 1
    439      * @return SimplePie_Category|null
    440      */
    441     public function get_category($key = 0)
    442     {
    443         $categories = $this->get_categories();
    444         if (isset($categories[$key]))
    445         {
    446             return $categories[$key];
    447         }
    448 
    449         return null;
    450     }
    451 
    452     /**
    453      * Get all categories for the item
    454      *
    455      * Uses `<atom:category>`, `<category>` or `<dc:subject>`
    456      *
    457      * @since Beta 3
    458      * @return SimplePie_Category[]|null List of {@see SimplePie_Category} objects
    459      */
    460     public function get_categories()
    461     {
    462         $categories = array();
    463 
    464         $type = 'category';
    465         foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, $type) as $category)
    466         {
    467             $term = null;
    468             $scheme = null;
    469             $label = null;
    470             if (isset($category['attribs']['']['term']))
    471             {
    472                 $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT);
    473             }
    474             if (isset($category['attribs']['']['scheme']))
    475             {
    476                 $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
    477             }
    478             if (isset($category['attribs']['']['label']))
    479             {
    480                 $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
    481             }
    482             $categories[] = $this->registry->create('Category', array($term, $scheme, $label, $type));
    483         }
    484         foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, $type) as $category)
    485         {
    486             // This is really the label, but keep this as the term also for BC.
    487             // Label will also work on retrieving because that falls back to term.
    488             $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    489             if (isset($category['attribs']['']['domain']))
    490             {
    491                 $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT);
    492             }
    493             else
    494             {
    495                 $scheme = null;
    496             }
    497             $categories[] = $this->registry->create('Category', array($term, $scheme, null, $type));
    498         }
    499 
    500         $type = 'subject';
    501         foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, $type) as $category)
    502         {
    503             $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null, $type));
    504         }
    505         foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, $type) as $category)
    506         {
    507             $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null, $type));
    508         }
    509 
    510         if (!empty($categories))
    511         {
    512             return array_unique($categories);
    513         }
    514 
    515         return null;
    516     }
    517 
    518     /**
    519      * Get an author for the item
    520      *
    521      * @since Beta 2
    522      * @param int $key The author that you want to return.  Remember that arrays begin with 0, not 1
    523      * @return SimplePie_Author|null
    524      */
    525     public function get_author($key = 0)
    526     {
    527         $authors = $this->get_authors();
    528         if (isset($authors[$key]))
    529         {
    530             return $authors[$key];
    531         }
    532 
    533         return null;
    534     }
    535 
    536     /**
    537      * Get a contributor for the item
    538      *
    539      * @since 1.1
    540      * @param int $key The contrbutor that you want to return.  Remember that arrays begin with 0, not 1
    541      * @return SimplePie_Author|null
    542      */
    543     public function get_contributor($key = 0)
    544     {
    545         $contributors = $this->get_contributors();
    546         if (isset($contributors[$key]))
    547         {
    548             return $contributors[$key];
    549         }
    550 
    551         return null;
    552     }
    553 
    554     /**
    555      * Get all contributors for the item
    556      *
    557      * Uses `<atom:contributor>`
    558      *
    559      * @since 1.1
    560      * @return SimplePie_Author[]|null List of {@see SimplePie_Author} objects
    561      */
    562     public function get_contributors()
    563     {
    564         $contributors = array();
    565         foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor)
    566         {
    567             $name = null;
    568             $uri = null;
    569             $email = null;
    570             if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
    571             {
    572                 $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    573             }
    574             if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
    575             {
    576                 $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]));
    577             }
    578             if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
    579             {
    580                 $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    581             }
    582             if ($name !== null || $email !== null || $uri !== null)
    583             {
    584                 $contributors[] = $this->registry->create('Author', array($name, $uri, $email));
    585             }
    586         }
    587         foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor)
    588         {
    589             $name = null;
    590             $url = null;
    591             $email = null;
    592             if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
    593             {
    594                 $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    595             }
    596             if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
    597             {
    598                 $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]));
    599             }
    600             if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
    601             {
    602                 $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    603             }
    604             if ($name !== null || $email !== null || $url !== null)
    605             {
    606                 $contributors[] = $this->registry->create('Author', array($name, $url, $email));
    607             }
    608         }
    609 
    610         if (!empty($contributors))
    611         {
    612             return array_unique($contributors);
    613         }
    614 
    615         return null;
    616     }
    617 
    618     /**
    619      * Get all authors for the item
    620      *
    621      * Uses `<atom:author>`, `<author>`, `<dc:creator>` or `<itunes:author>`
    622      *
    623      * @since Beta 2
    624      * @return SimplePie_Author[]|null List of {@see SimplePie_Author} objects
    625      */
    626     public function get_authors()
    627     {
    628         $authors = array();
    629         foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author)
    630         {
    631             $name = null;
    632             $uri = null;
    633             $email = null;
    634             if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
    635             {
    636                 $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    637             }
    638             if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
    639             {
    640                 $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]));
    641             }
    642             if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
    643             {
    644                 $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    645             }
    646             if ($name !== null || $email !== null || $uri !== null)
    647             {
    648                 $authors[] = $this->registry->create('Author', array($name, $uri, $email));
    649             }
    650         }
    651         if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author'))
    652         {
    653             $name = null;
    654             $url = null;
    655             $email = null;
    656             if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
    657             {
    658                 $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    659             }
    660             if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
    661             {
    662                 $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]));
    663             }
    664             if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
    665             {
    666                 $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    667             }
    668             if ($name !== null || $email !== null || $url !== null)
    669             {
    670                 $authors[] = $this->registry->create('Author', array($name, $url, $email));
    671             }
    672         }
    673         if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'author'))
    674         {
    675             $authors[] = $this->registry->create('Author', array(null, null, $this->sanitize($author[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)));
    676         }
    677         foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author)
    678         {
    679             $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
    680         }
    681         foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author)
    682         {
    683             $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
    684         }
    685         foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author)
    686         {
    687             $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null));
    688         }
    689 
    690         if (!empty($authors))
    691         {
    692             return array_unique($authors);
    693         }
    694         elseif (($source = $this->get_source()) && ($authors = $source->get_authors()))
    695         {
    696             return $authors;
    697         }
    698         elseif ($authors = $this->feed->get_authors())
    699         {
    700             return $authors;
    701         }
    702 
    703         return null;
    704     }
    705 
    706     /**
    707      * Get the copyright info for the item
    708      *
    709      * Uses `<atom:rights>` or `<dc:rights>`
    710      *
    711      * @since 1.1
    712      * @return string
    713      */
    714     public function get_copyright()
    715     {
    716         if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights'))
    717         {
    718             return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0]));
    719         }
    720         elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights'))
    721         {
    722             return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    723         }
    724         elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights'))
    725         {
    726             return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    727         }
    728 
    729         return null;
    730     }
    731 
    732     /**
    733      * Get the posting date/time for the item
    734      *
    735      * Uses `<atom:published>`, `<atom:updated>`, `<atom:issued>`,
    736      * `<atom:modified>`, `<pubDate>` or `<dc:date>`
    737      *
    738      * Note: obeys PHP's timezone setting. To get a UTC date/time, use
    739      * {@see get_gmdate}
    740      *
    741      * @since Beta 2 (previously called `get_item_date` since 0.8)
    742      *
    743      * @param string $date_format Supports any PHP date format from {@see http://php.net/date} (empty for the raw data)
    744      * @return int|string|null
    745      */
    746     public function get_date($date_format = 'j F Y, g:i a')
    747     {
    748         if (!isset($this->data['date']))
    749         {
    750             if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'published'))
    751             {
    752                 $this->data['date']['raw'] = $return[0]['data'];
    753             }
    754             elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'pubDate'))
    755             {
    756                 $this->data['date']['raw'] = $return[0]['data'];
    757             }
    758             elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'date'))
    759             {
    760                 $this->data['date']['raw'] = $return[0]['data'];
    761             }
    762             elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'date'))
    763             {
    764                 $this->data['date']['raw'] = $return[0]['data'];
    765             }
    766             elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated'))
    767             {
    768                 $this->data['date']['raw'] = $return[0]['data'];
    769             }
    770             elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'issued'))
    771             {
    772                 $this->data['date']['raw'] = $return[0]['data'];
    773             }
    774             elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'created'))
    775             {
    776                 $this->data['date']['raw'] = $return[0]['data'];
    777             }
    778             elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'modified'))
    779             {
    780                 $this->data['date']['raw'] = $return[0]['data'];
    781             }
    782 
    783             if (!empty($this->data['date']['raw']))
    784             {
    785                 $parser = $this->registry->call('Parse_Date', 'get');
    786                 $this->data['date']['parsed'] = $parser->parse($this->data['date']['raw']);
    787             }
    788             else
    789             {
    790                 $this->data['date'] = null;
    791             }
    792         }
    793         if ($this->data['date'])
    794         {
    795             $date_format = (string) $date_format;
    796             switch ($date_format)
    797             {
    798                 case '':
    799                     return $this->sanitize($this->data['date']['raw'], SIMPLEPIE_CONSTRUCT_TEXT);
    800 
    801                 case 'U':
    802                     return $this->data['date']['parsed'];
    803 
    804                 default:
    805                     return date($date_format, $this->data['date']['parsed']);
    806             }
    807         }
    808 
    809         return null;
    810     }
    811 
    812     /**
    813      * Get the update date/time for the item
    814      *
    815      * Uses `<atom:updated>`
    816      *
    817      * Note: obeys PHP's timezone setting. To get a UTC date/time, use
    818      * {@see get_gmdate}
    819      *
    820      * @param string $date_format Supports any PHP date format from {@see http://php.net/date} (empty for the raw data)
    821      * @return int|string|null
    822      */
    823     public function get_updated_date($date_format = 'j F Y, g:i a')
    824     {
    825         if (!isset($this->data['updated']))
    826         {
    827             if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated'))
    828             {
    829                 $this->data['updated']['raw'] = $return[0]['data'];
    830             }
    831 
    832             if (!empty($this->data['updated']['raw']))
    833             {
    834                 $parser = $this->registry->call('Parse_Date', 'get');
    835                 $this->data['updated']['parsed'] = $parser->parse($this->data['updated']['raw']);
    836             }
    837             else
    838             {
    839                 $this->data['updated'] = null;
    840             }
    841         }
    842         if ($this->data['updated'])
    843         {
    844             $date_format = (string) $date_format;
    845             switch ($date_format)
    846             {
    847                 case '':
    848                     return $this->sanitize($this->data['updated']['raw'], SIMPLEPIE_CONSTRUCT_TEXT);
    849 
    850                 case 'U':
    851                     return $this->data['updated']['parsed'];
    852 
    853                 default:
    854                     return date($date_format, $this->data['updated']['parsed']);
    855             }
    856         }
    857 
    858         return null;
    859     }
    860 
    861     /**
    862      * Get the localized posting date/time for the item
    863      *
    864      * Returns the date formatted in the localized language. To display in
    865      * languages other than the server's default, you need to change the locale
    866      * with {@link http://php.net/setlocale setlocale()}. The available
    867      * localizations depend on which ones are installed on your web server.
    868      *
    869      * @since 1.0
    870      *
    871      * @param string $date_format Supports any PHP date format from {@see http://php.net/strftime} (empty for the raw data)
    872      * @return int|string|null
    873      */
    874     public function get_local_date($date_format = '%c')
    875     {
    876         if (!$date_format)
    877         {
    878             return $this->sanitize($this->get_date(''), SIMPLEPIE_CONSTRUCT_TEXT);
    879         }
    880         elseif (($date = $this->get_date('U')) !== null && $date !== false)
    881         {
    882             return strftime($date_format, $date);
    883         }
    884 
    885         return null;
    886     }
    887 
    888     /**
    889      * Get the posting date/time for the item (UTC time)
    890      *
    891      * @see get_date
    892      * @param string $date_format Supports any PHP date format from {@see http://php.net/date}
    893      * @return int|string|null
    894      */
    895     public function get_gmdate($date_format = 'j F Y, g:i a')
    896     {
    897         $date = $this->get_date('U');
    898         if ($date === null)
    899         {
    900             return null;
    901         }
    902 
    903         return gmdate($date_format, $date);
    904     }
    905 
    906     /**
    907      * Get the update date/time for the item (UTC time)
    908      *
    909      * @see get_updated_date
    910      * @param string $date_format Supports any PHP date format from {@see http://php.net/date}
    911      * @return int|string|null
    912      */
    913     public function get_updated_gmdate($date_format = 'j F Y, g:i a')
    914     {
    915         $date = $this->get_updated_date('U');
    916         if ($date === null)
    917         {
    918             return null;
    919         }
    920 
    921         return gmdate($date_format, $date);
    922     }
    923 
    924     /**
    925      * Get the permalink for the item
    926      *
    927      * Returns the first link available with a relationship of "alternate".
    928      * Identical to {@see get_link()} with key 0
    929      *
    930      * @see get_link
    931      * @since 0.8
    932      * @return string|null Permalink URL
    933      */
    934     public function get_permalink()
    935     {
    936         $link = $this->get_link();
    937         $enclosure = $this->get_enclosure(0);
    938         if ($link !== null)
    939         {
    940             return $link;
    941         }
    942         elseif ($enclosure !== null)
    943         {
    944             return $enclosure->get_link();
    945         }
    946 
    947         return null;
    948     }
    949 
    950     /**
    951      * Get a single link for the item
    952      *
    953      * @since Beta 3
    954      * @param int $key The link that you want to return.  Remember that arrays begin with 0, not 1
    955      * @param string $rel The relationship of the link to return
    956      * @return string|null Link URL
    957      */
    958     public function get_link($key = 0, $rel = 'alternate')
    959     {
    960         $links = $this->get_links($rel);
    961         if ($links && $links[$key] !== null)
    962         {
    963             return $links[$key];
    964         }
    965 
    966         return null;
    967     }
    968 
    969     /**
    970      * Get all links for the item
    971      *
    972      * Uses `<atom:link>`, `<link>` or `<guid>`
    973      *
    974      * @since Beta 2
    975      * @param string $rel The relationship of links to return
    976      * @return array|null Links found for the item (strings)
    977      */
    978     public function get_links($rel = 'alternate')
    979     {
    980         if (!isset($this->data['links']))
    981         {
    982             $this->data['links'] = array();
    983             foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link)
    984             {
    985                 if (isset($link['attribs']['']['href']))
    986                 {
    987                     $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
    988                     $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
    989 
    990                 }
    991             }
    992             foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link)
    993             {
    994                 if (isset($link['attribs']['']['href']))
    995                 {
    996                     $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
    997                     $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
    998                 }
    999             }
    1000             if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
    1001             {
    1002                 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
    1003             }
    1004             if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
    1005             {
    1006                 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
    1007             }
    1008             if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
    1009             {
    1010                 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
    1011             }
    1012             if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'guid'))
    1013             {
    1014                 if (!isset($links[0]['attribs']['']['isPermaLink']) || strtolower(trim($links[0]['attribs']['']['isPermaLink'])) === 'true')
    1015                 {
    1016                     $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
    1017                 }
    1018             }
    1019 
    1020             $keys = array_keys($this->data['links']);
    1021             foreach ($keys as $key)
    1022             {
    1023                 if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key)))
    1024                 {
    1025                     if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]))
    1026                     {
    1027                         $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]);
    1028                         $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key];
    1029                     }
    1030                     else
    1031                     {
    1032                         $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key];
    1033                     }
    1034                 }
    1035                 elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY)
    1036                 {
    1037                     $this->data['links'][substr($key, 41)] =& $this->data['links'][$key];
    1038                 }
    1039                 $this->data['links'][$key] = array_unique($this->data['links'][$key]);
    1040             }
    1041         }
    1042         if (isset($this->data['links'][$rel]))
    1043         {
    1044             return $this->data['links'][$rel];
    1045         }
    1046 
    1047         return null;
    1048     }
    1049 
    1050     /**
    1051      * Get an enclosure from the item
    1052      *
    1053      * Supports the <enclosure> RSS tag, as well as Media RSS and iTunes RSS.
    1054      *
    1055      * @since Beta 2
    1056      * @todo Add ability to prefer one type of content over another (in a media group).
    1057      * @param int $key The enclosure that you want to return.  Remember that arrays begin with 0, not 1
    1058      * @return SimplePie_Enclosure|null
    1059      */
    1060     public function get_enclosure($key = 0, $prefer = null)
    1061     {
    1062         $enclosures = $this->get_enclosures();
    1063         if (isset($enclosures[$key]))
    1064         {
    1065             return $enclosures[$key];
    1066         }
    1067 
    1068         return null;
    1069     }
    1070 
    1071     /**
    1072      * Get all available enclosures (podcasts, etc.)
    1073      *
    1074      * Supports the <enclosure> RSS tag, as well as Media RSS and iTunes RSS.
    1075      *
    1076      * At this point, we're pretty much assuming that all enclosures for an item
    1077      * are the same content.  Anything else is too complicated to
    1078      * properly support.
    1079      *
    1080      * @since Beta 2
    1081      * @todo Add support for end-user defined sorting of enclosures by type/handler (so we can prefer the faster-loading FLV over MP4).
    1082      * @todo If an element exists at a level, but its value is empty, we should fall back to the value from the parent (if it exists).
    1083      * @return SimplePie_Enclosure[]|null List of SimplePie_Enclosure items
    1084      */
    1085     public function get_enclosures()
    1086     {
    1087         if (!isset($this->data['enclosures']))
    1088         {
    1089             $this->data['enclosures'] = array();
    1090 
    1091             // Elements
    1092             $captions_parent = null;
    1093             $categories_parent = null;
    1094             $copyrights_parent = null;
    1095             $credits_parent = null;
    1096             $description_parent = null;
    1097             $duration_parent = null;
    1098             $hashes_parent = null;
    1099             $keywords_parent = null;
    1100             $player_parent = null;
    1101             $ratings_parent = null;
    1102             $restrictions_parent = null;
    1103             $thumbnails_parent = null;
    1104             $title_parent = null;
    1105 
    1106             // Let's do the channel and item-level ones first, and just re-use them if we need to.
    1107             $parent = $this->get_feed();
    1108 
    1109             // CAPTIONS
    1110             if ($captions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text'))
    1111             {
    1112                 foreach ($captions as $caption)
    1113                 {
    1114                     $caption_type = null;
    1115                     $caption_lang = null;
    1116                     $caption_startTime = null;
    1117                     $caption_endTime = null;
    1118                     $caption_text = null;
    1119                     if (isset($caption['attribs']['']['type']))
    1120                     {
    1121                         $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
    1122                     }
    1123                     if (isset($caption['attribs']['']['lang']))
    1124                     {
    1125                         $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
    1126                     }
    1127                     if (isset($caption['attribs']['']['start']))
    1128                     {
    1129                         $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
    1130                     }
    1131                     if (isset($caption['attribs']['']['end']))
    1132                     {
    1133                         $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
    1134                     }
    1135                     if (isset($caption['data']))
    1136                     {
    1137                         $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1138                     }
    1139                     $captions_parent[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
    1140                 }
    1141             }
    1142             elseif ($captions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text'))
    1143             {
    1144                 foreach ($captions as $caption)
    1145                 {
    1146                     $caption_type = null;
    1147                     $caption_lang = null;
    1148                     $caption_startTime = null;
    1149                     $caption_endTime = null;
    1150                     $caption_text = null;
    1151                     if (isset($caption['attribs']['']['type']))
    1152                     {
    1153                         $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
    1154                     }
    1155                     if (isset($caption['attribs']['']['lang']))
    1156                     {
    1157                         $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
    1158                     }
    1159                     if (isset($caption['attribs']['']['start']))
    1160                     {
    1161                         $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
    1162                     }
    1163                     if (isset($caption['attribs']['']['end']))
    1164                     {
    1165                         $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
    1166                     }
    1167                     if (isset($caption['data']))
    1168                     {
    1169                         $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1170                     }
    1171                     $captions_parent[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
    1172                 }
    1173             }
    1174             if (is_array($captions_parent))
    1175             {
    1176                 $captions_parent = array_values(array_unique($captions_parent));
    1177             }
    1178 
    1179             // CATEGORIES
    1180             foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category)
    1181             {
    1182                 $term = null;
    1183                 $scheme = null;
    1184                 $label = null;
    1185                 if (isset($category['data']))
    1186                 {
    1187                     $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1188                 }
    1189                 if (isset($category['attribs']['']['scheme']))
    1190                 {
    1191                     $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
    1192                 }
    1193                 else
    1194                 {
    1195                     $scheme = 'http://search.yahoo.com/mrss/category_schema';
    1196                 }
    1197                 if (isset($category['attribs']['']['label']))
    1198                 {
    1199                     $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
    1200                 }
    1201                 $categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label));
    1202             }
    1203             foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category)
    1204             {
    1205                 $term = null;
    1206                 $scheme = null;
    1207                 $label = null;
    1208                 if (isset($category['data']))
    1209                 {
    1210                     $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1211                 }
    1212                 if (isset($category['attribs']['']['scheme']))
    1213                 {
    1214                     $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
    1215                 }
    1216                 else
    1217                 {
    1218                     $scheme = 'http://search.yahoo.com/mrss/category_schema';
    1219                 }
    1220                 if (isset($category['attribs']['']['label']))
    1221                 {
    1222                     $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
    1223                 }
    1224                 $categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label));
    1225             }
    1226             foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'category') as $category)
    1227             {
    1228                 $term = null;
    1229                 $scheme = 'http://www.itunes.com/dtds/podcast-1.0.dtd';
    1230                 $label = null;
    1231                 if (isset($category['attribs']['']['text']))
    1232                 {
    1233                     $label = $this->sanitize($category['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT);
    1234                 }
    1235                 $categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label));
    1236 
    1237                 if (isset($category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category']))
    1238                 {
    1239                     foreach ((array) $category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category'] as $subcategory)
    1240                     {
    1241                         if (isset($subcategory['attribs']['']['text']))
    1242                         {
    1243                             $label = $this->sanitize($subcategory['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT);
    1244                         }
    1245                         $categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label));
    1246                     }
    1247                 }
    1248             }
    1249             if (is_array($categories_parent))
    1250             {
    1251                 $categories_parent = array_values(array_unique($categories_parent));
    1252             }
    1253 
    1254             // COPYRIGHT
    1255             if ($copyright = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright'))
    1256             {
    1257                 $copyright_url = null;
    1258                 $copyright_label = null;
    1259                 if (isset($copyright[0]['attribs']['']['url']))
    1260                 {
    1261                     $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
    1262                 }
    1263                 if (isset($copyright[0]['data']))
    1264                 {
    1265                     $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1266                 }
    1267                 $copyrights_parent = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
    1268             }
    1269             elseif ($copyright = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright'))
    1270             {
    1271                 $copyright_url = null;
    1272                 $copyright_label = null;
    1273                 if (isset($copyright[0]['attribs']['']['url']))
    1274                 {
    1275                     $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
    1276                 }
    1277                 if (isset($copyright[0]['data']))
    1278                 {
    1279                     $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1280                 }
    1281                 $copyrights_parent = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
    1282             }
    1283 
    1284             // CREDITS
    1285             if ($credits = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit'))
    1286             {
    1287                 foreach ($credits as $credit)
    1288                 {
    1289                     $credit_role = null;
    1290                     $credit_scheme = null;
    1291                     $credit_name = null;
    1292                     if (isset($credit['attribs']['']['role']))
    1293                     {
    1294                         $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
    1295                     }
    1296                     if (isset($credit['attribs']['']['scheme']))
    1297                     {
    1298                         $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
    1299                     }
    1300                     else
    1301                     {
    1302                         $credit_scheme = 'urn:ebu';
    1303                     }
    1304                     if (isset($credit['data']))
    1305                     {
    1306                         $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1307                     }
    1308                     $credits_parent[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
    1309                 }
    1310             }
    1311             elseif ($credits = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit'))
    1312             {
    1313                 foreach ($credits as $credit)
    1314                 {
    1315                     $credit_role = null;
    1316                     $credit_scheme = null;
    1317                     $credit_name = null;
    1318                     if (isset($credit['attribs']['']['role']))
    1319                     {
    1320                         $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
    1321                     }
    1322                     if (isset($credit['attribs']['']['scheme']))
    1323                     {
    1324                         $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
    1325                     }
    1326                     else
    1327                     {
    1328                         $credit_scheme = 'urn:ebu';
    1329                     }
    1330                     if (isset($credit['data']))
    1331                     {
    1332                         $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1333                     }
    1334                     $credits_parent[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
    1335                 }
    1336             }
    1337             if (is_array($credits_parent))
    1338             {
    1339                 $credits_parent = array_values(array_unique($credits_parent));
    1340             }
    1341 
    1342             // DESCRIPTION
    1343             if ($description_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description'))
    1344             {
    1345                 if (isset($description_parent[0]['data']))
    1346                 {
    1347                     $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1348                 }
    1349             }
    1350             elseif ($description_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description'))
    1351             {
    1352                 if (isset($description_parent[0]['data']))
    1353                 {
    1354                     $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1355                 }
    1356             }
    1357 
    1358             // DURATION
    1359             if ($duration_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'duration'))
    1360             {
    1361                 $seconds = null;
    1362                 $minutes = null;
    1363                 $hours = null;
    1364                 if (isset($duration_parent[0]['data']))
    1365                 {
    1366                     $temp = explode(':', $this->sanitize($duration_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
    1367                     if (sizeof($temp) > 0)
    1368                     {
    1369                         $seconds = (int) array_pop($temp);
    1370                     }
    1371                     if (sizeof($temp) > 0)
    1372                     {
    1373                         $minutes = (int) array_pop($temp);
    1374                         $seconds += $minutes * 60;
    1375                     }
    1376                     if (sizeof($temp) > 0)
    1377                     {
    1378                         $hours = (int) array_pop($temp);
    1379                         $seconds += $hours * 3600;
    1380                     }
    1381                     unset($temp);
    1382                     $duration_parent = $seconds;
    1383                 }
    1384             }
    1385 
    1386             // HASHES
    1387             if ($hashes_iterator = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash'))
    1388             {
    1389                 foreach ($hashes_iterator as $hash)
    1390                 {
    1391                     $value = null;
    1392                     $algo = null;
    1393                     if (isset($hash['data']))
    1394                     {
    1395                         $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1396                     }
    1397                     if (isset($hash['attribs']['']['algo']))
    1398                     {
    1399                         $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
    1400                     }
    1401                     else
    1402                     {
    1403                         $algo = 'md5';
    1404                     }
    1405                     $hashes_parent[] = $algo.':'.$value;
    1406                 }
    1407             }
    1408             elseif ($hashes_iterator = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash'))
    1409             {
    1410                 foreach ($hashes_iterator as $hash)
    1411                 {
    1412                     $value = null;
    1413                     $algo = null;
    1414                     if (isset($hash['data']))
    1415                     {
    1416                         $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1417                     }
    1418                     if (isset($hash['attribs']['']['algo']))
    1419                     {
    1420                         $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
    1421                     }
    1422                     else
    1423                     {
    1424                         $algo = 'md5';
    1425                     }
    1426                     $hashes_parent[] = $algo.':'.$value;
    1427                 }
    1428             }
    1429             if (is_array($hashes_parent))
    1430             {
    1431                 $hashes_parent = array_values(array_unique($hashes_parent));
    1432             }
    1433 
    1434             // KEYWORDS
    1435             if ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords'))
    1436             {
    1437                 if (isset($keywords[0]['data']))
    1438                 {
    1439                     $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
    1440                     foreach ($temp as $word)
    1441                     {
    1442                         $keywords_parent[] = trim($word);
    1443                     }
    1444                 }
    1445                 unset($temp);
    1446             }
    1447             elseif ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords'))
    1448             {
    1449                 if (isset($keywords[0]['data']))
    1450                 {
    1451                     $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
    1452                     foreach ($temp as $word)
    1453                     {
    1454                         $keywords_parent[] = trim($word);
    1455                     }
    1456                 }
    1457                 unset($temp);
    1458             }
    1459             elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords'))
    1460             {
    1461                 if (isset($keywords[0]['data']))
    1462                 {
    1463                     $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
    1464                     foreach ($temp as $word)
    1465                     {
    1466                         $keywords_parent[] = trim($word);
    1467                     }
    1468                 }
    1469                 unset($temp);
    1470             }
    1471             elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords'))
    1472             {
    1473                 if (isset($keywords[0]['data']))
    1474                 {
    1475                     $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
    1476                     foreach ($temp as $word)
    1477                     {
    1478                         $keywords_parent[] = trim($word);
    1479                     }
    1480                 }
    1481                 unset($temp);
    1482             }
    1483             if (is_array($keywords_parent))
    1484             {
    1485                 $keywords_parent = array_values(array_unique($keywords_parent));
    1486             }
    1487 
    1488             // PLAYER
    1489             if ($player_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player'))
    1490             {
    1491                 if (isset($player_parent[0]['attribs']['']['url']))
    1492                 {
    1493                     $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
    1494                 }
    1495             }
    1496             elseif ($player_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player'))
    1497             {
    1498                 if (isset($player_parent[0]['attribs']['']['url']))
    1499                 {
    1500                     $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
    1501                 }
    1502             }
    1503 
    1504             // RATINGS
    1505             if ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating'))
    1506             {
    1507                 foreach ($ratings as $rating)
    1508                 {
    1509                     $rating_scheme = null;
    1510                     $rating_value = null;
    1511                     if (isset($rating['attribs']['']['scheme']))
    1512                     {
    1513                         $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
    1514                     }
    1515                     else
    1516                     {
    1517                         $rating_scheme = 'urn:simple';
    1518                     }
    1519                     if (isset($rating['data']))
    1520                     {
    1521                         $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1522                     }
    1523                     $ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
    1524                 }
    1525             }
    1526             elseif ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit'))
    1527             {
    1528                 foreach ($ratings as $rating)
    1529                 {
    1530                     $rating_scheme = 'urn:itunes';
    1531                     $rating_value = null;
    1532                     if (isset($rating['data']))
    1533                     {
    1534                         $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1535                     }
    1536                     $ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
    1537                 }
    1538             }
    1539             elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating'))
    1540             {
    1541                 foreach ($ratings as $rating)
    1542                 {
    1543                     $rating_scheme = null;
    1544                     $rating_value = null;
    1545                     if (isset($rating['attribs']['']['scheme']))
    1546                     {
    1547                         $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
    1548                     }
    1549                     else
    1550                     {
    1551                         $rating_scheme = 'urn:simple';
    1552                     }
    1553                     if (isset($rating['data']))
    1554                     {
    1555                         $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1556                     }
    1557                     $ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
    1558                 }
    1559             }
    1560             elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit'))
    1561             {
    1562                 foreach ($ratings as $rating)
    1563                 {
    1564                     $rating_scheme = 'urn:itunes';
    1565                     $rating_value = null;
    1566                     if (isset($rating['data']))
    1567                     {
    1568                         $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1569                     }
    1570                     $ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
    1571                 }
    1572             }
    1573             if (is_array($ratings_parent))
    1574             {
    1575                 $ratings_parent = array_values(array_unique($ratings_parent));
    1576             }
    1577 
    1578             // RESTRICTIONS
    1579             if ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction'))
    1580             {
    1581                 foreach ($restrictions as $restriction)
    1582                 {
    1583                     $restriction_relationship = null;
    1584                     $restriction_type = null;
    1585                     $restriction_value = null;
    1586                     if (isset($restriction['attribs']['']['relationship']))
    1587                     {
    1588                         $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
    1589                     }
    1590                     if (isset($restriction['attribs']['']['type']))
    1591                     {
    1592                         $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
    1593                     }
    1594                     if (isset($restriction['data']))
    1595                     {
    1596                         $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1597                     }
    1598                     $restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
    1599                 }
    1600             }
    1601             elseif ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block'))
    1602             {
    1603                 foreach ($restrictions as $restriction)
    1604                 {
    1605                     $restriction_relationship = 'allow';
    1606                     $restriction_type = null;
    1607                     $restriction_value = 'itunes';
    1608                     if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes')
    1609                     {
    1610                         $restriction_relationship = 'deny';
    1611                     }
    1612                     $restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
    1613                 }
    1614             }
    1615             elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction'))
    1616             {
    1617                 foreach ($restrictions as $restriction)
    1618                 {
    1619                     $restriction_relationship = null;
    1620                     $restriction_type = null;
    1621                     $restriction_value = null;
    1622                     if (isset($restriction['attribs']['']['relationship']))
    1623                     {
    1624                         $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
    1625                     }
    1626                     if (isset($restriction['attribs']['']['type']))
    1627                     {
    1628                         $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
    1629                     }
    1630                     if (isset($restriction['data']))
    1631                     {
    1632                         $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1633                     }
    1634                     $restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
    1635                 }
    1636             }
    1637             elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block'))
    1638             {
    1639                 foreach ($restrictions as $restriction)
    1640                 {
    1641                     $restriction_relationship = 'allow';
    1642                     $restriction_type = null;
    1643                     $restriction_value = 'itunes';
    1644                     if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes')
    1645                     {
    1646                         $restriction_relationship = 'deny';
    1647                     }
    1648                     $restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
    1649                 }
    1650             }
    1651             if (is_array($restrictions_parent))
    1652             {
    1653                 $restrictions_parent = array_values(array_unique($restrictions_parent));
    1654             }
    1655             else
    1656             {
    1657                 $restrictions_parent = array(new SimplePie_Restriction('allow', null, 'default'));
    1658             }
    1659 
    1660             // THUMBNAILS
    1661             if ($thumbnails = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail'))
    1662             {
    1663                 foreach ($thumbnails as $thumbnail)
    1664                 {
    1665                     if (isset($thumbnail['attribs']['']['url']))
    1666                     {
    1667                         $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
    1668                     }
    1669                 }
    1670             }
    1671             elseif ($thumbnails = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail'))
    1672             {
    1673                 foreach ($thumbnails as $thumbnail)
    1674                 {
    1675                     if (isset($thumbnail['attribs']['']['url']))
    1676                     {
    1677                         $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
    1678                     }
    1679                 }
    1680             }
    1681 
    1682             // TITLES
    1683             if ($title_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title'))
    1684             {
    1685                 if (isset($title_parent[0]['data']))
    1686                 {
    1687                     $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1688                 }
    1689             }
    1690             elseif ($title_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title'))
    1691             {
    1692                 if (isset($title_parent[0]['data']))
    1693                 {
    1694                     $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1695                 }
    1696             }
    1697 
    1698             // Clear the memory
    1699             unset($parent);
    1700 
    1701             // Attributes
    1702             $bitrate = null;
    1703             $channels = null;
    1704             $duration = null;
    1705             $expression = null;
    1706             $framerate = null;
    1707             $height = null;
    1708             $javascript = null;
    1709             $lang = null;
    1710             $length = null;
    1711             $medium = null;
    1712             $samplingrate = null;
    1713             $type = null;
    1714             $url = null;
    1715             $width = null;
    1716 
    1717             // Elements
    1718             $captions = null;
    1719             $categories = null;
    1720             $copyrights = null;
    1721             $credits = null;
    1722             $description = null;
    1723             $hashes = null;
    1724             $keywords = null;
    1725             $player = null;
    1726             $ratings = null;
    1727             $restrictions = null;
    1728             $thumbnails = null;
    1729             $title = null;
    1730 
    1731             // If we have media:group tags, loop through them.
    1732             foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'group') as $group)
    1733             {
    1734                 if(isset($group['child']) && isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content']))
    1735                 {
    1736                     // If we have media:content tags, loop through them.
    1737                     foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content)
    1738                     {
    1739                         if (isset($content['attribs']['']['url']))
    1740                         {
    1741                             // Attributes
    1742                             $bitrate = null;
    1743                             $channels = null;
    1744                             $duration = null;
    1745                             $expression = null;
    1746                             $framerate = null;
    1747                             $height = null;
    1748                             $javascript = null;
    1749                             $lang = null;
    1750                             $length = null;
    1751                             $medium = null;
    1752                             $samplingrate = null;
    1753                             $type = null;
    1754                             $url = null;
    1755                             $width = null;
    1756 
    1757                             // Elements
    1758                             $captions = null;
    1759                             $categories = null;
    1760                             $copyrights = null;
    1761                             $credits = null;
    1762                             $description = null;
    1763                             $hashes = null;
    1764                             $keywords = null;
    1765                             $player = null;
    1766                             $ratings = null;
    1767                             $restrictions = null;
    1768                             $thumbnails = null;
    1769                             $title = null;
    1770 
    1771                             // Start checking the attributes of media:content
    1772                             if (isset($content['attribs']['']['bitrate']))
    1773                             {
    1774                                 $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT);
    1775                             }
    1776                             if (isset($content['attribs']['']['channels']))
    1777                             {
    1778                                 $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT);
    1779                             }
    1780                             if (isset($content['attribs']['']['duration']))
    1781                             {
    1782                                 $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT);
    1783                             }
    1784                             else
    1785                             {
    1786                                 $duration = $duration_parent;
    1787                             }
    1788                             if (isset($content['attribs']['']['expression']))
    1789                             {
    1790                                 $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT);
    1791                             }
    1792                             if (isset($content['attribs']['']['framerate']))
    1793                             {
    1794                                 $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT);
    1795                             }
    1796                             if (isset($content['attribs']['']['height']))
    1797                             {
    1798                                 $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT);
    1799                             }
    1800                             if (isset($content['attribs']['']['lang']))
    1801                             {
    1802                                 $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
    1803                             }
    1804                             if (isset($content['attribs']['']['fileSize']))
    1805                             {
    1806                                 $length = intval($content['attribs']['']['fileSize']);
    1807                             }
    1808                             if (isset($content['attribs']['']['medium']))
    1809                             {
    1810                                 $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT);
    1811                             }
    1812                             if (isset($content['attribs']['']['samplingrate']))
    1813                             {
    1814                                 $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT);
    1815                             }
    1816                             if (isset($content['attribs']['']['type']))
    1817                             {
    1818                                 $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
    1819                             }
    1820                             if (isset($content['attribs']['']['width']))
    1821                             {
    1822                                 $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT);
    1823                             }
    1824                             $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
    1825 
    1826                             // Checking the other optional media: elements. Priority: media:content, media:group, item, channel
    1827 
    1828                             // CAPTIONS
    1829                             if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text']))
    1830                             {
    1831                                 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption)
    1832                                 {
    1833                                     $caption_type = null;
    1834                                     $caption_lang = null;
    1835                                     $caption_startTime = null;
    1836                                     $caption_endTime = null;
    1837                                     $caption_text = null;
    1838                                     if (isset($caption['attribs']['']['type']))
    1839                                     {
    1840                                         $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
    1841                                     }
    1842                                     if (isset($caption['attribs']['']['lang']))
    1843                                     {
    1844                                         $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
    1845                                     }
    1846                                     if (isset($caption['attribs']['']['start']))
    1847                                     {
    1848                                         $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
    1849                                     }
    1850                                     if (isset($caption['attribs']['']['end']))
    1851                                     {
    1852                                         $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
    1853                                     }
    1854                                     if (isset($caption['data']))
    1855                                     {
    1856                                         $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1857                                     }
    1858                                     $captions[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
    1859                                 }
    1860                                 if (is_array($captions))
    1861                                 {
    1862                                     $captions = array_values(array_unique($captions));
    1863                                 }
    1864                             }
    1865                             elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text']))
    1866                             {
    1867                                 foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption)
    1868                                 {
    1869                                     $caption_type = null;
    1870                                     $caption_lang = null;
    1871                                     $caption_startTime = null;
    1872                                     $caption_endTime = null;
    1873                                     $caption_text = null;
    1874                                     if (isset($caption['attribs']['']['type']))
    1875                                     {
    1876                                         $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
    1877                                     }
    1878                                     if (isset($caption['attribs']['']['lang']))
    1879                                     {
    1880                                         $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
    1881                                     }
    1882                                     if (isset($caption['attribs']['']['start']))
    1883                                     {
    1884                                         $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
    1885                                     }
    1886                                     if (isset($caption['attribs']['']['end']))
    1887                                     {
    1888                                         $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
    1889                                     }
    1890                                     if (isset($caption['data']))
    1891                                     {
    1892                                         $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1893                                     }
    1894                                     $captions[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
    1895                                 }
    1896                                 if (is_array($captions))
    1897                                 {
    1898                                     $captions = array_values(array_unique($captions));
    1899                                 }
    1900                             }
    1901                             else
    1902                             {
    1903                                 $captions = $captions_parent;
    1904                             }
    1905 
    1906                             // CATEGORIES
    1907                             if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category']))
    1908                             {
    1909                                 foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category)
    1910                                 {
    1911                                     $term = null;
    1912                                     $scheme = null;
    1913                                     $label = null;
    1914                                     if (isset($category['data']))
    1915                                     {
    1916                                         $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1917                                     }
    1918                                     if (isset($category['attribs']['']['scheme']))
    1919                                     {
    1920                                         $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
    1921                                     }
    1922                                     else
    1923                                     {
    1924                                         $scheme = 'http://search.yahoo.com/mrss/category_schema';
    1925                                     }
    1926                                     if (isset($category['attribs']['']['label']))
    1927                                     {
    1928                                         $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
    1929                                     }
    1930                                     $categories[] = $this->registry->create('Category', array($term, $scheme, $label));
    1931                                 }
    1932                             }
    1933                             if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category']))
    1934                             {
    1935                                 foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category)
    1936                                 {
    1937                                     $term = null;
    1938                                     $scheme = null;
    1939                                     $label = null;
    1940                                     if (isset($category['data']))
    1941                                     {
    1942                                         $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1943                                     }
    1944                                     if (isset($category['attribs']['']['scheme']))
    1945                                     {
    1946                                         $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
    1947                                     }
    1948                                     else
    1949                                     {
    1950                                         $scheme = 'http://search.yahoo.com/mrss/category_schema';
    1951                                     }
    1952                                     if (isset($category['attribs']['']['label']))
    1953                                     {
    1954                                         $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
    1955                                     }
    1956                                     $categories[] = $this->registry->create('Category', array($term, $scheme, $label));
    1957                                 }
    1958                             }
    1959                             if (is_array($categories) && is_array($categories_parent))
    1960                             {
    1961                                 $categories = array_values(array_unique(array_merge($categories, $categories_parent)));
    1962                             }
    1963                             elseif (is_array($categories))
    1964                             {
    1965                                 $categories = array_values(array_unique($categories));
    1966                             }
    1967                             elseif (is_array($categories_parent))
    1968                             {
    1969                                 $categories = array_values(array_unique($categories_parent));
    1970                             }
    1971 
    1972                             // COPYRIGHTS
    1973                             if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright']))
    1974                             {
    1975                                 $copyright_url = null;
    1976                                 $copyright_label = null;
    1977                                 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url']))
    1978                                 {
    1979                                     $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
    1980                                 }
    1981                                 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data']))
    1982                                 {
    1983                                     $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1984                                 }
    1985                                 $copyrights = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
    1986                             }
    1987                             elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright']))
    1988                             {
    1989                                 $copyright_url = null;
    1990                                 $copyright_label = null;
    1991                                 if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url']))
    1992                                 {
    1993                                     $copyright_url = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
    1994                                 }
    1995                                 if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data']))
    1996                                 {
    1997                                     $copyright_label = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    1998                                 }
    1999                                 $copyrights = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
    2000                             }
    2001                             else
    2002                             {
    2003                                 $copyrights = $copyrights_parent;
    2004                             }
    2005 
    2006                             // CREDITS
    2007                             if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit']))
    2008                             {
    2009                                 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit)
    2010                                 {
    2011                                     $credit_role = null;
    2012                                     $credit_scheme = null;
    2013                                     $credit_name = null;
    2014                                     if (isset($credit['attribs']['']['role']))
    2015                                     {
    2016                                         $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
    2017                                     }
    2018                                     if (isset($credit['attribs']['']['scheme']))
    2019                                     {
    2020                                         $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
    2021                                     }
    2022                                     else
    2023                                     {
    2024                                         $credit_scheme = 'urn:ebu';
    2025                                     }
    2026                                     if (isset($credit['data']))
    2027                                     {
    2028                                         $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    2029                                     }
    2030                                     $credits[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
    2031                                 }
    2032                                 if (is_array($credits))
    2033                                 {
    2034                                     $credits = array_values(array_unique($credits));
    2035                                 }
    2036                             }
    2037                             elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit']))
    2038                             {
    2039                                 foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit)
    2040                                 {
    2041                                     $credit_role = null;
    2042                                     $credit_scheme = null;
    2043                                     $credit_name = null;
    2044                                     if (isset($credit['attribs']['']['role']))
    2045                                     {
    2046                                         $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
    2047                                     }
    2048                                     if (isset($credit['attribs']['']['scheme']))
    2049                                     {
    2050                                         $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
    2051                                     }
    2052                                     else
    2053                                     {
    2054                                         $credit_scheme = 'urn:ebu';
    2055                                     }
    2056                                     if (isset($credit['data']))
    2057                                     {
    2058                                         $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    2059                                     }
    2060                                     $credits[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
    2061                                 }
    2062                                 if (is_array($credits))
    2063                                 {
    2064                                     $credits = array_values(array_unique($credits));
    2065                                 }
    2066                             }
    2067                             else
    2068                             {
    2069                                 $credits = $credits_parent;
    2070                             }
    2071 
    2072                             // DESCRIPTION
    2073                             if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description']))
    2074                             {
    2075                                 $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    2076                             }
    2077                             elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description']))
    2078                             {
    2079                                 $description = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    2080                             }
    2081                             else
    2082                             {
    2083                                 $description = $description_parent;
    2084                             }
    2085 
    2086                             // HASHES
    2087                             if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash']))
    2088                             {
    2089                                 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash)
    2090                                 {
    2091                                     $value = null;
    2092                                     $algo = null;
    2093                                     if (isset($hash['data']))
    2094                                     {
    2095                                         $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    2096                                     }
    2097                                     if (isset($hash['attribs']['']['algo']))
    2098                                     {
    2099                                         $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
    2100                                     }
    2101                                     else
    2102                                     {
    2103                                         $algo = 'md5';
    2104                                     }
    2105                                     $hashes[] = $algo.':'.$value;
    2106                                 }
    2107                                 if (is_array($hashes))
    2108                                 {
    2109                                     $hashes = array_values(array_unique($hashes));
    2110                                 }
    2111                             }
    2112                             elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash']))
    2113                             {
    2114                                 foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash)
    2115                                 {
    2116                                     $value = null;
    2117                                     $algo = null;
    2118                                     if (isset($hash['data']))
    2119                                     {
    2120                                         $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    2121                                     }
    2122                                     if (isset($hash['attribs']['']['algo']))
    2123                                     {
    2124                                         $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
    2125                                     }
    2126                                     else
    2127                                     {
    2128                                         $algo = 'md5';
    2129                                     }
    2130                                     $hashes[] = $algo.':'.$value;
    2131                                 }
    2132                                 if (is_array($hashes))
    2133                                 {
    2134                                     $hashes = array_values(array_unique($hashes));
    2135                                 }
    2136                             }
    2137                             else
    2138                             {
    2139                                 $hashes = $hashes_parent;
    2140                             }
    2141 
    2142                             // KEYWORDS
    2143                             if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords']))
    2144                             {
    2145                                 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data']))
    2146                                 {
    2147                                     $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
    2148                                     foreach ($temp as $word)
    2149                                     {
    2150                                         $keywords[] = trim($word);
    2151                                     }
    2152                                     unset($temp);
    2153                                 }
    2154                                 if (is_array($keywords))
    2155                                 {
    2156                                     $keywords = array_values(array_unique($keywords));
    2157                                 }
    2158                             }
    2159                             elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords']))
    2160                             {
    2161                                 if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data']))
    2162                                 {
    2163                                     $temp = explode(',', $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
    2164                                     foreach ($temp as $word)
    2165                                     {
    2166                                         $keywords[] = trim($word);
    2167                                     }
    2168                                     unset($temp);
    2169                                 }
    2170                                 if (is_array($keywords))
    2171                                 {
    2172                                     $keywords = array_values(array_unique($keywords));
    2173                                 }
    2174                             }
    2175                             else
    2176                             {
    2177                                 $keywords = $keywords_parent;
    2178                             }
    2179 
    2180                             // PLAYER
    2181                             if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
    2182                             {
    2183                                 $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
    2184                             }
    2185                             elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
    2186                             {
    2187                                 $player = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
    2188                             }
    2189                             else
    2190                             {
    2191                                 $player = $player_parent;
    2192                             }
    2193 
    2194                             // RATINGS
    2195                             if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating']))
    2196                             {
    2197                                 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating)
    2198                                 {
    2199                                     $rating_scheme = null;
    2200                                     $rating_value = null;
    2201                                     if (isset($rating['attribs']['']['scheme']))
    2202                                     {
    2203                                         $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
    2204                                     }
    2205                                     else
    2206                                     {
    2207                                         $rating_scheme = 'urn:simple';
    2208                                     }
    2209                                     if (isset($rating['data']))
    2210                                     {
    2211                                         $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    2212                                     }
    2213                                     $ratings[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
    2214                                 }
    2215                                 if (is_array($ratings))
    2216                                 {
    2217                                     $ratings = array_values(array_unique($ratings));
    2218                                 }
    2219                             }
    2220                             elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating']))
    2221                             {
    2222                                 foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating)
    2223                                 {
    2224                                     $rating_scheme = null;
    2225                                     $rating_value = null;
    2226                                     if (isset($rating['attribs']['']['scheme']))
    2227                                     {
    2228                                         $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
    2229                                     }
    2230                                     else
    2231                                     {
    2232                                         $rating_scheme = 'urn:simple';
    2233                                     }
    2234                                     if (isset($rating['data']))
    2235                                     {
    2236                                         $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    2237                                     }
    2238                                     $ratings[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
    2239                                 }
    2240                                 if (is_array($ratings))
    2241                                 {
    2242                                     $ratings = array_values(array_unique($ratings));
    2243                                 }
    2244                             }
    2245                             else
    2246                             {
    2247                                 $ratings = $ratings_parent;
    2248                             }
    2249 
    2250                             // RESTRICTIONS
    2251                             if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction']))
    2252                             {
    2253                                 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction)
    2254                                 {
    2255                                     $restriction_relationship = null;
    2256                                     $restriction_type = null;
    2257                                     $restriction_value = null;
    2258                                     if (isset($restriction['attribs']['']['relationship']))
    2259                                     {
    2260                                         $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
    2261                                     }
    2262                                     if (isset($restriction['attribs']['']['type']))
    2263                                     {
    2264                                         $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
    2265                                     }
    2266                                     if (isset($restriction['data']))
    2267                                     {
    2268                                         $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    2269                                     }
    2270                                     $restrictions[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
    2271                                 }
    2272                                 if (is_array($restrictions))
    2273                                 {
    2274                                     $restrictions = array_values(array_unique($restrictions));
    2275                                 }
    2276                             }
    2277                             elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction']))
    2278                             {
    2279                                 foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction)
    2280                                 {
    2281                                     $restriction_relationship = null;
    2282                                     $restriction_type = null;
    2283                                     $restriction_value = null;
    2284                                     if (isset($restriction['attribs']['']['relationship']))
    2285                                     {
    2286                                         $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
    2287                                     }
    2288                                     if (isset($restriction['attribs']['']['type']))
    2289                                     {
    2290                                         $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
    2291                                     }
    2292                                     if (isset($restriction['data']))
    2293                                     {
    2294                                         $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    2295                                     }
    2296                                     $restrictions[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
    2297                                 }
    2298                                 if (is_array($restrictions))
    2299                                 {
    2300                                     $restrictions = array_values(array_unique($restrictions));
    2301                                 }
    2302                             }
    2303                             else
    2304                             {
    2305                                 $restrictions = $restrictions_parent;
    2306                             }
    2307 
    2308                             // THUMBNAILS
    2309                             if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail']))
    2310                             {
    2311                                 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail)
    2312                                 {
    2313                                     $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
    2314                                 }
    2315                                 if (is_array($thumbnails))
    2316                                 {
    2317                                     $thumbnails = array_values(array_unique($thumbnails));
    2318                                 }
    2319                             }
    2320                             elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail']))
    2321                             {
    2322                                 foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail)
    2323                                 {
    2324                                     $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
    2325                                 }
    2326                                 if (is_array($thumbnails))
    2327                                 {
    2328                                     $thumbnails = array_values(array_unique($thumbnails));
    2329                                 }
    2330                             }
    2331                             else
    2332                             {
    2333                                 $thumbnails = $thumbnails_parent;
    2334                             }
    2335 
    2336                             // TITLES
    2337                             if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title']))
    2338                             {
    2339                                 $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    2340                             }
    2341                             elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title']))
    2342                             {
    2343                                 $title = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    2344                             }
    2345                             else
    2346                             {
    2347                                 $title = $title_parent;
    2348                             }
    2349 
    2350                             $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width));
    2351                         }
    2352                     }
    2353                 }
    2354             }
    2355 
    2356             // If we have standalone media:content tags, loop through them.
    2357             if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content']))
    2358             {
    2359                 foreach ((array) $this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content)
    2360                 {
    2361                     if (isset($content['attribs']['']['url']) || isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
    2362                     {
    2363                         // Attributes
    2364                         $bitrate = null;
    2365                         $channels = null;
    2366                         $duration = null;
    2367                         $expression = null;
    2368                         $framerate = null;
    2369                         $height = null;
    2370                         $javascript = null;
    2371                         $lang = null;
    2372                         $length = null;
    2373                         $medium = null;
    2374                         $samplingrate = null;
    2375                         $type = null;
    2376                         $url = null;
    2377                         $width = null;
    2378 
    2379                         // Elements
    2380                         $captions = null;
    2381                         $categories = null;
    2382                         $copyrights = null;
    2383                         $credits = null;
    2384                         $description = null;
    2385                         $hashes = null;
    2386                         $keywords = null;
    2387                         $player = null;
    2388                         $ratings = null;
    2389                         $restrictions = null;
    2390                         $thumbnails = null;
    2391                         $title = null;
    2392 
    2393                         // Start checking the attributes of media:content
    2394                         if (isset($content['attribs']['']['bitrate']))
    2395                         {
    2396                             $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT);
    2397                         }
    2398                         if (isset($content['attribs']['']['channels']))
    2399                         {
    2400                             $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT);
    2401                         }
    2402                         if (isset($content['attribs']['']['duration']))
    2403                         {
    2404                             $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT);
    2405                         }
    2406                         else
    2407                         {
    2408                             $duration = $duration_parent;
    2409                         }
    2410                         if (isset($content['attribs']['']['expression']))
    2411                         {
    2412                             $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT);
    2413                         }
    2414                         if (isset($content['attribs']['']['framerate']))
    2415                         {
    2416                             $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT);
    2417                         }
    2418                         if (isset($content['attribs']['']['height']))
    2419                         {
    2420                             $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT);
    2421                         }
    2422                         if (isset($content['attribs']['']['lang']))
    2423                         {
    2424                             $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
    2425                         }
    2426                         if (isset($content['attribs']['']['fileSize']))
    2427                         {
    2428                             $length = intval($content['attribs']['']['fileSize']);
    2429                         }
    2430                         if (isset($content['attribs']['']['medium']))
    2431                         {
    2432                             $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT);
    2433                         }
    2434                         if (isset($content['attribs']['']['samplingrate']))
    2435                         {
    2436                             $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT);
    2437                         }
    2438                         if (isset($content['attribs']['']['type']))
    2439                         {
    2440                             $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
    2441                         }
    2442                         if (isset($content['attribs']['']['width']))
    2443                         {
    2444                             $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT);
    2445                         }
    2446                         if (isset($content['attribs']['']['url']))
    2447                         {
    2448                             $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
    2449                         }
    2450                         // Checking the other optional media: elements. Priority: media:content, media:group, item, channel
    2451 
    2452                         // CAPTIONS
    2453                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text']))
    2454                         {
    2455                             foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption)
    2456                             {
    2457                                 $caption_type = null;
    2458                                 $caption_lang = null;
    2459                                 $caption_startTime = null;
    2460                                 $caption_endTime = null;
    2461                                 $caption_text = null;
    2462                                 if (isset($caption['attribs']['']['type']))
    2463                                 {
    2464                                     $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
    2465                                 }
    2466                                 if (isset($caption['attribs']['']['lang']))
    2467                                 {
    2468                                     $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
    2469                                 }
    2470                                 if (isset($caption['attribs']['']['start']))
    2471                                 {
    2472                                     $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
    2473                                 }
    2474                                 if (isset($caption['attribs']['']['end']))
    2475                                 {
    2476                                     $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
    2477                                 }
    2478                                 if (isset($caption['data']))
    2479                                 {
    2480                                     $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    2481                                 }
    2482                                 $captions[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text));
    2483                             }
    2484                             if (is_array($captions))
    2485                             {
    2486                                 $captions = array_values(array_unique($captions));
    2487                             }
    2488                         }
    2489                         else
    2490                         {
    2491                             $captions = $captions_parent;
    2492                         }
    2493 
    2494                         // CATEGORIES
    2495                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category']))
    2496                         {
    2497                             foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category)
    2498                             {
    2499                                 $term = null;
    2500                                 $scheme = null;
    2501                                 $label = null;
    2502                                 if (isset($category['data']))
    2503                                 {
    2504                                     $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    2505                                 }
    2506                                 if (isset($category['attribs']['']['scheme']))
    2507                                 {
    2508                                     $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
    2509                                 }
    2510                                 else
    2511                                 {
    2512                                     $scheme = 'http://search.yahoo.com/mrss/category_schema';
    2513                                 }
    2514                                 if (isset($category['attribs']['']['label']))
    2515                                 {
    2516                                     $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
    2517                                 }
    2518                                 $categories[] = $this->registry->create('Category', array($term, $scheme, $label));
    2519                             }
    2520                         }
    2521                         if (is_array($categories) && is_array($categories_parent))
    2522                         {
    2523                             $categories = array_values(array_unique(array_merge($categories, $categories_parent)));
    2524                         }
    2525                         elseif (is_array($categories))
    2526                         {
    2527                             $categories = array_values(array_unique($categories));
    2528                         }
    2529                         elseif (is_array($categories_parent))
    2530                         {
    2531                             $categories = array_values(array_unique($categories_parent));
    2532                         }
    2533                         else
    2534                         {
    2535                             $categories = null;
    2536                         }
    2537 
    2538                         // COPYRIGHTS
    2539                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright']))
    2540                         {
    2541                             $copyright_url = null;
    2542                             $copyright_label = null;
    2543                             if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url']))
    2544                             {
    2545                                 $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
    2546                             }
    2547                             if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data']))
    2548                             {
    2549                                 $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    2550                             }
    2551                             $copyrights = $this->registry->create('Copyright', array($copyright_url, $copyright_label));
    2552                         }
    2553                         else
    2554                         {
    2555                             $copyrights = $copyrights_parent;
    2556                         }
    2557 
    2558                         // CREDITS
    2559                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit']))
    2560                         {
    2561                             foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit)
    2562                             {
    2563                                 $credit_role = null;
    2564                                 $credit_scheme = null;
    2565                                 $credit_name = null;
    2566                                 if (isset($credit['attribs']['']['role']))
    2567                                 {
    2568                                     $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
    2569                                 }
    2570                                 if (isset($credit['attribs']['']['scheme']))
    2571                                 {
    2572                                     $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
    2573                                 }
    2574                                 else
    2575                                 {
    2576                                     $credit_scheme = 'urn:ebu';
    2577                                 }
    2578                                 if (isset($credit['data']))
    2579                                 {
    2580                                     $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    2581                                 }
    2582                                 $credits[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name));
    2583                             }
    2584                             if (is_array($credits))
    2585                             {
    2586                                 $credits = array_values(array_unique($credits));
    2587                             }
    2588                         }
    2589                         else
    2590                         {
    2591                             $credits = $credits_parent;
    2592                         }
    2593 
    2594                         // DESCRIPTION
    2595                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description']))
    2596                         {
    2597                             $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    2598                         }
    2599                         else
    2600                         {
    2601                             $description = $description_parent;
    2602                         }
    2603 
    2604                         // HASHES
    2605                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash']))
    2606                         {
    2607                             foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash)
    2608                             {
    2609                                 $value = null;
    2610                                 $algo = null;
    2611                                 if (isset($hash['data']))
    2612                                 {
    2613                                     $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    2614                                 }
    2615                                 if (isset($hash['attribs']['']['algo']))
    2616                                 {
    2617                                     $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
    2618                                 }
    2619                                 else
    2620                                 {
    2621                                     $algo = 'md5';
    2622                                 }
    2623                                 $hashes[] = $algo.':'.$value;
    2624                             }
    2625                             if (is_array($hashes))
    2626                             {
    2627                                 $hashes = array_values(array_unique($hashes));
    2628                             }
    2629                         }
    2630                         else
    2631                         {
    2632                             $hashes = $hashes_parent;
    2633                         }
    2634 
    2635                         // KEYWORDS
    2636                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords']))
    2637                         {
    2638                             if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data']))
    2639                             {
    2640                                 $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
    2641                                 foreach ($temp as $word)
    2642                                 {
    2643                                     $keywords[] = trim($word);
    2644                                 }
    2645                                 unset($temp);
    2646                             }
    2647                             if (is_array($keywords))
    2648                             {
    2649                                 $keywords = array_values(array_unique($keywords));
    2650                             }
    2651                         }
    2652                         else
    2653                         {
    2654                             $keywords = $keywords_parent;
    2655                         }
    2656 
    2657                         // PLAYER
    2658                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
    2659                         {
    2660                             if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'])) {
    2661                                 $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
    2662                             }
    2663                         }
    2664                         else
    2665                         {
    2666                             $player = $player_parent;
    2667                         }
    2668 
    2669                         // RATINGS
    2670                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating']))
    2671                         {
    2672                             foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating)
    2673                             {
    2674                                 $rating_scheme = null;
    2675                                 $rating_value = null;
    2676                                 if (isset($rating['attribs']['']['scheme']))
    2677                                 {
    2678                                     $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
    2679                                 }
    2680                                 else
    2681                                 {
    2682                                     $rating_scheme = 'urn:simple';
    2683                                 }
    2684                                 if (isset($rating['data']))
    2685                                 {
    2686                                     $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    2687                                 }
    2688                                 $ratings[] = $this->registry->create('Rating', array($rating_scheme, $rating_value));
    2689                             }
    2690                             if (is_array($ratings))
    2691                             {
    2692                                 $ratings = array_values(array_unique($ratings));
    2693                             }
    2694                         }
    2695                         else
    2696                         {
    2697                             $ratings = $ratings_parent;
    2698                         }
    2699 
    2700                         // RESTRICTIONS
    2701                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction']))
    2702                         {
    2703                             foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction)
    2704                             {
    2705                                 $restriction_relationship = null;
    2706                                 $restriction_type = null;
    2707                                 $restriction_value = null;
    2708                                 if (isset($restriction['attribs']['']['relationship']))
    2709                                 {
    2710                                     $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
    2711                                 }
    2712                                 if (isset($restriction['attribs']['']['type']))
    2713                                 {
    2714                                     $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
    2715                                 }
    2716                                 if (isset($restriction['data']))
    2717                                 {
    2718                                     $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    2719                                 }
    2720                                 $restrictions[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value));
    2721                             }
    2722                             if (is_array($restrictions))
    2723                             {
    2724                                 $restrictions = array_values(array_unique($restrictions));
    2725                             }
    2726                         }
    2727                         else
    2728                         {
    2729                             $restrictions = $restrictions_parent;
    2730                         }
    2731 
    2732                         // THUMBNAILS
    2733                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail']))
    2734                         {
    2735                             foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail)
    2736                             {
    2737                                 if (isset($thumbnail['attribs']['']['url'])) {
    2738                                     $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
    2739                                 }
    2740                             }
    2741                             if (is_array($thumbnails))
    2742                             {
    2743                                 $thumbnails = array_values(array_unique($thumbnails));
    2744                             }
    2745                         }
    2746                         else
    2747                         {
    2748                             $thumbnails = $thumbnails_parent;
    2749                         }
    2750 
    2751                         // TITLES
    2752                         if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title']))
    2753                         {
    2754                             $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
    2755                         }
    2756                         else
    2757                         {
    2758                             $title = $title_parent;
    2759                         }
    2760 
    2761                         $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width));
    2762                     }
    2763                 }
    2764             }
    2765 
    2766             foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link)
    2767             {
    2768                 if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure')
    2769                 {
    2770                     // Attributes
    2771                     $bitrate = null;
    2772                     $channels = null;
    2773                     $duration = null;
    2774                     $expression = null;
    2775                     $framerate = null;
    2776                     $height = null;
    2777                     $javascript = null;
    2778                     $lang = null;
    2779                     $length = null;
    2780                     $medium = null;
    2781                     $samplingrate = null;
    2782                     $type = null;
    2783                     $url = null;
    2784                     $width = null;
    2785 
    2786                     $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
    2787                     if (isset($link['attribs']['']['type']))
    2788                     {
    2789                         $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
    2790                     }
    2791                     if (isset($link['attribs']['']['length']))
    2792                     {
    2793                         $length = intval($link['attribs']['']['length']);
    2794                     }
    2795                     if (isset($link['attribs']['']['title']))
    2796                     {
    2797                         $title = $this->sanitize($link['attribs']['']['title'], SIMPLEPIE_CONSTRUCT_TEXT);
    2798                     }
    2799                     else
    2800                     {
    2801                         $title = $title_parent;
    2802                     }
    2803 
    2804                     // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
    2805                     $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title, $width));
    2806                 }
    2807             }
    2808 
    2809             foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link)
    2810             {
    2811                 if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure')
    2812                 {
    2813                     // Attributes
    2814                     $bitrate = null;
    2815                     $channels = null;
    2816                     $duration = null;
    2817                     $expression = null;
    2818                     $framerate = null;
    2819                     $height = null;
    2820                     $javascript = null;
    2821                     $lang = null;
    2822                     $length = null;
    2823                     $medium = null;
    2824                     $samplingrate = null;
    2825                     $type = null;
    2826                     $url = null;
    2827                     $width = null;
    2828 
    2829                     $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
    2830                     if (isset($link['attribs']['']['type']))
    2831                     {
    2832                         $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
    2833                     }
    2834                     if (isset($link['attribs']['']['length']))
    2835                     {
    2836                         $length = intval($link['attribs']['']['length']);
    2837                     }
    2838 
    2839                     // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
    2840                     $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width));
    2841                 }
    2842             }
    2843 
    2844             if ($enclosure = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'enclosure'))
    2845             {
    2846                 if (isset($enclosure[0]['attribs']['']['url']))
    2847                 {
    2848                     // Attributes
    2849                     $bitrate = null;
    2850                     $channels = null;
    2851                     $duration = null;
    2852                     $expression = null;
    2853                     $framerate = null;
    2854                     $height = null;
    2855                     $javascript = null;
    2856                     $lang = null;
    2857                     $length = null;
    2858                     $medium = null;
    2859                     $samplingrate = null;
    2860                     $type = null;
    2861                     $url = null;
    2862                     $width = null;
    2863 
    2864                     $url = $this->sanitize($enclosure[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($enclosure[0]));
    2865                     $url = $this->feed->sanitize->https_url($url);
    2866                     if (isset($enclosure[0]['attribs']['']['type']))
    2867                     {
    2868                         $type = $this->sanitize($enclosure[0]['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
    2869                     }
    2870                     if (isset($enclosure[0]['attribs']['']['length']))
    2871                     {
    2872                         $length = intval($enclosure[0]['attribs']['']['length']);
    2873                     }
    2874 
    2875                     // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
    2876                     $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width));
    2877                 }
    2878             }
    2879 
    2880             if (sizeof($this->data['enclosures']) === 0 && ($url || $type || $length || $bitrate || $captions_parent || $categories_parent || $channels || $copyrights_parent || $credits_parent || $description_parent || $duration_parent || $expression || $framerate || $hashes_parent || $height || $keywords_parent || $lang || $medium || $player_parent || $ratings_parent || $restrictions_parent || $samplingrate || $thumbnails_parent || $title_parent || $width))
    2881             {
    2882                 // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
    2883                 $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width));
    2884             }
    2885 
    2886             $this->data['enclosures'] = array_values(array_unique($this->data['enclosures']));
    2887         }
    2888         if (!empty($this->data['enclosures']))
    2889         {
    2890             return $this->data['enclosures'];
    2891         }
    2892 
    2893         return null;
    2894     }
    2895 
    2896     /**
    2897      * Get the latitude coordinates for the item
    2898      *
    2899      * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
    2900      *
    2901      * Uses `<geo:lat>` or `<georss:point>`
    2902      *
    2903      * @since 1.0
    2904      * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
    2905      * @link http://www.georss.org/ GeoRSS
    2906      * @return string|null
    2907      */
    2908     public function get_latitude()
    2909     {
    2910         if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat'))
    2911         {
    2912             return (float) $return[0]['data'];
    2913         }
    2914         elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
    2915         {
    2916             return (float) $match[1];
    2917         }
    2918 
    2919         return null;
    2920     }
    2921 
    2922     /**
    2923      * Get the longitude coordinates for the item
    2924      *
    2925      * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
    2926      *
    2927      * Uses `<geo:long>`, `<geo:lon>` or `<georss:point>`
    2928      *
    2929      * @since 1.0
    2930      * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
    2931      * @link http://www.georss.org/ GeoRSS
    2932      * @return string|null
    2933      */
    2934     public function get_longitude()
    2935     {
    2936         if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long'))
    2937         {
    2938             return (float) $return[0]['data'];
    2939         }
    2940         elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon'))
    2941         {
    2942             return (float) $return[0]['data'];
    2943         }
    2944         elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
    2945         {
    2946             return (float) $match[2];
    2947         }
    2948 
    2949         return null;
    2950     }
    2951 
    2952     /**
    2953      * Get the `<atom:source>` for the item
    2954      *
    2955      * @since 1.1
    2956      * @return SimplePie_Source|null
    2957      */
    2958     public function get_source()
    2959     {
    2960         if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'source'))
    2961         {
    2962             return $this->registry->create('Source', array($this, $return[0]));
    2963         }
    2964 
    2965         return null;
    2966     }
     59    /**
     60     * Parent feed
     61     *
     62     * @access private
     63     * @var \SimplePie\SimplePie
     64     */
     65    public $feed;
     66
     67    /**
     68     * Raw data
     69     *
     70     * @access private
     71     * @var array
     72     */
     73    public $data = [];
     74
     75    /**
     76     * Registry object
     77     *
     78     * @see set_registry
     79     * @var \SimplePie\Registry
     80     */
     81    protected $registry;
     82
     83    /**
     84     * Create a new item object
     85     *
     86     * This is usually used by {@see \SimplePie\SimplePie::get_items} and
     87     * {@see \SimplePie\SimplePie::get_item}. Avoid creating this manually.
     88     *
     89     * @param \SimplePie\SimplePie $feed Parent feed
     90     * @param array $data Raw data
     91     */
     92    public function __construct($feed, $data)
     93    {
     94        $this->feed = $feed;
     95        $this->data = $data;
     96    }
     97
     98    /**
     99     * Set the registry handler
     100     *
     101     * This is usually used by {@see \SimplePie\Registry::create}
     102     *
     103     * @since 1.3
     104     * @param \SimplePie\Registry $registry
     105     */
     106    public function set_registry(\SimplePie\Registry $registry)/* : void */
     107    {
     108        $this->registry = $registry;
     109    }
     110
     111    /**
     112     * Get a string representation of the item
     113     *
     114     * @return string
     115     */
     116    public function __toString()
     117    {
     118        return md5(serialize($this->data));
     119    }
     120
     121    /**
     122     * Remove items that link back to this before destroying this object
     123     */
     124    public function __destruct()
     125    {
     126        if (!gc_enabled()) {
     127            unset($this->feed);
     128        }
     129    }
     130
     131    /**
     132     * Get data for an item-level element
     133     *
     134     * This method allows you to get access to ANY element/attribute that is a
     135     * sub-element of the item/entry tag.
     136     *
     137     * See {@see \SimplePie\SimplePie::get_feed_tags()} for a description of the return value
     138     *
     139     * @since 1.0
     140     * @see http://simplepie.org/wiki/faq/supported_xml_namespaces
     141     * @param string $namespace The URL of the XML namespace of the elements you're trying to access
     142     * @param string $tag Tag name
     143     * @return array
     144     */
     145    public function get_item_tags($namespace, $tag)
     146    {
     147        if (isset($this->data['child'][$namespace][$tag])) {
     148            return $this->data['child'][$namespace][$tag];
     149        }
     150
     151        return null;
     152    }
     153
     154    /**
     155     * Get the base URL value.
     156     * Uses `<xml:base>`, or item link, or feed base URL.
     157     *
     158     * @param array $element
     159     * @return string
     160     */
     161    public function get_base($element = [])
     162    {
     163        if (!empty($element['xml_base_explicit']) && isset($element['xml_base'])) {
     164            return $element['xml_base'];
     165        }
     166        $link = $this->get_permalink();
     167        if ($link != null) {
     168            return $link;
     169        }
     170        return $this->feed->get_base($element);
     171    }
     172
     173    /**
     174     * Sanitize feed data
     175     *
     176     * @access private
     177     * @see \SimplePie\SimplePie::sanitize()
     178     * @param string $data Data to sanitize
     179     * @param int $type One of the \SimplePie\SimplePie::CONSTRUCT_* constants
     180     * @param string $base Base URL to resolve URLs against
     181     * @return string Sanitized data
     182     */
     183    public function sanitize($data, $type, $base = '')
     184    {
     185        return $this->feed->sanitize($data, $type, $base);
     186    }
     187
     188    /**
     189     * Get the parent feed
     190     *
     191     * Note: this may not work as you think for multifeeds!
     192     *
     193     * @link http://simplepie.org/faq/typical_multifeed_gotchas#missing_data_from_feed
     194     * @since 1.0
     195     * @return \SimplePie\SimplePie
     196     */
     197    public function get_feed()
     198    {
     199        return $this->feed;
     200    }
     201
     202    /**
     203     * Get the unique identifier for the item
     204     *
     205     * This is usually used when writing code to check for new items in a feed.
     206     *
     207     * Uses `<atom:id>`, `<guid>`, `<dc:identifier>` or the `about` attribute
     208     * for RDF. If none of these are supplied (or `$hash` is true), creates an
     209     * MD5 hash based on the permalink, title and content.
     210     *
     211     * @since Beta 2
     212     * @param boolean $hash Should we force using a hash instead of the supplied ID?
     213     * @param string|false $fn User-supplied function to generate an hash
     214     * @return string|null
     215     */
     216    public function get_id($hash = false, $fn = 'md5')
     217    {
     218        if (!$hash) {
     219            if ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'id')) {
     220                return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     221            } elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'id')) {
     222                return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     223            } elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_20, 'guid')) {
     224                return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     225            } elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_11, 'identifier')) {
     226                return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     227            } elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_10, 'identifier')) {
     228                return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     229            } elseif (isset($this->data['attribs'][\SimplePie\SimplePie::NAMESPACE_RDF]['about'])) {
     230                return $this->sanitize($this->data['attribs'][\SimplePie\SimplePie::NAMESPACE_RDF]['about'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     231            }
     232        }
     233        if ($fn === false) {
     234            return null;
     235        } elseif (!is_callable($fn)) {
     236            trigger_error('User-supplied function $fn must be callable', E_USER_WARNING);
     237            $fn = 'md5';
     238        }
     239        return call_user_func(
     240            $fn,
     241            $this->get_permalink().$this->get_title().$this->get_content()
     242        );
     243    }
     244
     245    /**
     246     * Get the title of the item
     247     *
     248     * Uses `<atom:title>`, `<title>` or `<dc:title>`
     249     *
     250     * @since Beta 2 (previously called `get_item_title` since 0.8)
     251     * @return string|null
     252     */
     253    public function get_title()
     254    {
     255        if (!isset($this->data['title'])) {
     256            if ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'title')) {
     257                $this->data['title'] = $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_10_construct_type', [$return[0]['attribs']]), $this->get_base($return[0]));
     258            } elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'title')) {
     259                $this->data['title'] = $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_03_construct_type', [$return[0]['attribs']]), $this->get_base($return[0]));
     260            } elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_10, 'title')) {
     261                $this->data['title'] = $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
     262            } elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_090, 'title')) {
     263                $this->data['title'] = $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
     264            } elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_20, 'title')) {
     265                $this->data['title'] = $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
     266            } elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_11, 'title')) {
     267                $this->data['title'] = $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     268            } elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_10, 'title')) {
     269                $this->data['title'] = $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     270            } else {
     271                $this->data['title'] = null;
     272            }
     273        }
     274        return $this->data['title'];
     275    }
     276
     277    /**
     278     * Get the content for the item
     279     *
     280     * Prefers summaries over full content , but will return full content if a
     281     * summary does not exist.
     282     *
     283     * To prefer full content instead, use {@see get_content}
     284     *
     285     * Uses `<atom:summary>`, `<description>`, `<dc:description>` or
     286     * `<itunes:subtitle>`
     287     *
     288     * @since 0.8
     289     * @param boolean $description_only Should we avoid falling back to the content?
     290     * @return string|null
     291     */
     292    public function get_description($description_only = false)
     293    {
     294        if (($tags = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'summary')) &&
     295            ($return = $this->sanitize($tags[0]['data'], $this->registry->call(Misc::class, 'atom_10_construct_type', [$tags[0]['attribs']]), $this->get_base($tags[0])))) {
     296            return $return;
     297        } elseif (($tags = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'summary')) &&
     298                ($return = $this->sanitize($tags[0]['data'], $this->registry->call(Misc::class, 'atom_03_construct_type', [$tags[0]['attribs']]), $this->get_base($tags[0])))) {
     299            return $return;
     300        } elseif (($tags = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_10, 'description')) &&
     301                ($return = $this->sanitize($tags[0]['data'], \SimplePie\SimplePie::CONSTRUCT_MAYBE_HTML, $this->get_base($tags[0])))) {
     302            return $return;
     303        } elseif (($tags = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_20, 'description')) &&
     304                ($return = $this->sanitize($tags[0]['data'], \SimplePie\SimplePie::CONSTRUCT_HTML, $this->get_base($tags[0])))) {
     305            return $return;
     306        } elseif (($tags = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_11, 'description')) &&
     307                ($return = $this->sanitize($tags[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT))) {
     308            return $return;
     309        } elseif (($tags = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_10, 'description')) &&
     310                ($return = $this->sanitize($tags[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT))) {
     311            return $return;
     312        } elseif (($tags = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'summary')) &&
     313                ($return = $this->sanitize($tags[0]['data'], \SimplePie\SimplePie::CONSTRUCT_HTML, $this->get_base($tags[0])))) {
     314            return $return;
     315        } elseif (($tags = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'subtitle')) &&
     316                ($return = $this->sanitize($tags[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT))) {
     317            return $return;
     318        } elseif (($tags = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_090, 'description')) &&
     319                ($return = $this->sanitize($tags[0]['data'], \SimplePie\SimplePie::CONSTRUCT_HTML))) {
     320            return $return;
     321        } elseif (!$description_only) {
     322            return $this->get_content(true);
     323        }
     324
     325        return null;
     326    }
     327
     328    /**
     329     * Get the content for the item
     330     *
     331     * Prefers full content over summaries, but will return a summary if full
     332     * content does not exist.
     333     *
     334     * To prefer summaries instead, use {@see get_description}
     335     *
     336     * Uses `<atom:content>` or `<content:encoded>` (RSS 1.0 Content Module)
     337     *
     338     * @since 1.0
     339     * @param boolean $content_only Should we avoid falling back to the description?
     340     * @return string|null
     341     */
     342    public function get_content($content_only = false)
     343    {
     344        if (($tags = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'content')) &&
     345            ($return = $this->sanitize($tags[0]['data'], $this->registry->call(Misc::class, 'atom_10_content_construct_type', [$tags[0]['attribs']]), $this->get_base($tags[0])))) {
     346            return $return;
     347        } elseif (($tags = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'content')) &&
     348                ($return = $this->sanitize($tags[0]['data'], $this->registry->call(Misc::class, 'atom_03_construct_type', [$tags[0]['attribs']]), $this->get_base($tags[0])))) {
     349            return $return;
     350        } elseif (($tags = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_10_MODULES_CONTENT, 'encoded')) &&
     351                ($return = $this->sanitize($tags[0]['data'], \SimplePie\SimplePie::CONSTRUCT_HTML, $this->get_base($tags[0])))) {
     352            return $return;
     353        } elseif (!$content_only) {
     354            return $this->get_description(true);
     355        }
     356
     357        return null;
     358    }
     359
     360    /**
     361     * Get the media:thumbnail of the item
     362     *
     363     * Uses `<media:thumbnail>`
     364     *
     365     *
     366     * @return array|null
     367     */
     368    public function get_thumbnail()
     369    {
     370        if (!isset($this->data['thumbnail'])) {
     371            if ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'thumbnail')) {
     372                $thumbnail = $return[0]['attribs'][''];
     373                if (empty($thumbnail['url'])) {
     374                    $this->data['thumbnail'] = null;
     375                } else {
     376                    $thumbnail['url'] = $this->sanitize($thumbnail['url'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($return[0]));
     377                    $this->data['thumbnail'] = $thumbnail;
     378                }
     379            } else {
     380                $this->data['thumbnail'] = null;
     381            }
     382        }
     383        return $this->data['thumbnail'];
     384    }
     385
     386    /**
     387     * Get a category for the item
     388     *
     389     * @since Beta 3 (previously called `get_categories()` since Beta 2)
     390     * @param int $key The category that you want to return.  Remember that arrays begin with 0, not 1
     391     * @return \SimplePie\Category|null
     392     */
     393    public function get_category($key = 0)
     394    {
     395        $categories = $this->get_categories();
     396        if (isset($categories[$key])) {
     397            return $categories[$key];
     398        }
     399
     400        return null;
     401    }
     402
     403    /**
     404     * Get all categories for the item
     405     *
     406     * Uses `<atom:category>`, `<category>` or `<dc:subject>`
     407     *
     408     * @since Beta 3
     409     * @return \SimplePie\Category[]|null List of {@see \SimplePie\Category} objects
     410     */
     411    public function get_categories()
     412    {
     413        $categories = [];
     414
     415        $type = 'category';
     416        foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, $type) as $category) {
     417            $term = null;
     418            $scheme = null;
     419            $label = null;
     420            if (isset($category['attribs']['']['term'])) {
     421                $term = $this->sanitize($category['attribs']['']['term'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     422            }
     423            if (isset($category['attribs']['']['scheme'])) {
     424                $scheme = $this->sanitize($category['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     425            }
     426            if (isset($category['attribs']['']['label'])) {
     427                $label = $this->sanitize($category['attribs']['']['label'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     428            }
     429            $categories[] = $this->registry->create(Category::class, [$term, $scheme, $label, $type]);
     430        }
     431        foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_20, $type) as $category) {
     432            // This is really the label, but keep this as the term also for BC.
     433            // Label will also work on retrieving because that falls back to term.
     434            $term = $this->sanitize($category['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     435            if (isset($category['attribs']['']['domain'])) {
     436                $scheme = $this->sanitize($category['attribs']['']['domain'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     437            } else {
     438                $scheme = null;
     439            }
     440            $categories[] = $this->registry->create(Category::class, [$term, $scheme, null, $type]);
     441        }
     442
     443        $type = 'subject';
     444        foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_11, $type) as $category) {
     445            $categories[] = $this->registry->create(Category::class, [$this->sanitize($category['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT), null, null, $type]);
     446        }
     447        foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_10, $type) as $category) {
     448            $categories[] = $this->registry->create(Category::class, [$this->sanitize($category['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT), null, null, $type]);
     449        }
     450
     451        if (!empty($categories)) {
     452            return array_unique($categories);
     453        }
     454
     455        return null;
     456    }
     457
     458    /**
     459     * Get an author for the item
     460     *
     461     * @since Beta 2
     462     * @param int $key The author that you want to return.  Remember that arrays begin with 0, not 1
     463     * @return \SimplePie\Author|null
     464     */
     465    public function get_author($key = 0)
     466    {
     467        $authors = $this->get_authors();
     468        if (isset($authors[$key])) {
     469            return $authors[$key];
     470        }
     471
     472        return null;
     473    }
     474
     475    /**
     476     * Get a contributor for the item
     477     *
     478     * @since 1.1
     479     * @param int $key The contrbutor that you want to return.  Remember that arrays begin with 0, not 1
     480     * @return \SimplePie\Author|null
     481     */
     482    public function get_contributor($key = 0)
     483    {
     484        $contributors = $this->get_contributors();
     485        if (isset($contributors[$key])) {
     486            return $contributors[$key];
     487        }
     488
     489        return null;
     490    }
     491
     492    /**
     493     * Get all contributors for the item
     494     *
     495     * Uses `<atom:contributor>`
     496     *
     497     * @since 1.1
     498     * @return \SimplePie\Author[]|null List of {@see \SimplePie\Author} objects
     499     */
     500    public function get_contributors()
     501    {
     502        $contributors = [];
     503        foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'contributor') as $contributor) {
     504            $name = null;
     505            $uri = null;
     506            $email = null;
     507            if (isset($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['name'][0]['data'])) {
     508                $name = $this->sanitize($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['name'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     509            }
     510            if (isset($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['uri'][0]['data'])) {
     511                $uri = $this->sanitize($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['uri'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['uri'][0]));
     512            }
     513            if (isset($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['email'][0]['data'])) {
     514                $email = $this->sanitize($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['email'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     515            }
     516            if ($name !== null || $email !== null || $uri !== null) {
     517                $contributors[] = $this->registry->create(Author::class, [$name, $uri, $email]);
     518            }
     519        }
     520        foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'contributor') as $contributor) {
     521            $name = null;
     522            $url = null;
     523            $email = null;
     524            if (isset($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['name'][0]['data'])) {
     525                $name = $this->sanitize($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['name'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     526            }
     527            if (isset($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['url'][0]['data'])) {
     528                $url = $this->sanitize($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['url'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['url'][0]));
     529            }
     530            if (isset($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['email'][0]['data'])) {
     531                $email = $this->sanitize($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['email'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     532            }
     533            if ($name !== null || $email !== null || $url !== null) {
     534                $contributors[] = $this->registry->create(Author::class, [$name, $url, $email]);
     535            }
     536        }
     537
     538        if (!empty($contributors)) {
     539            return array_unique($contributors);
     540        }
     541
     542        return null;
     543    }
     544
     545    /**
     546     * Get all authors for the item
     547     *
     548     * Uses `<atom:author>`, `<author>`, `<dc:creator>` or `<itunes:author>`
     549     *
     550     * @since Beta 2
     551     * @return \SimplePie\Author[]|null List of {@see \SimplePie\Author} objects
     552     */
     553    public function get_authors()
     554    {
     555        $authors = [];
     556        foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'author') as $author) {
     557            $name = null;
     558            $uri = null;
     559            $email = null;
     560            if (isset($author['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['name'][0]['data'])) {
     561                $name = $this->sanitize($author['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['name'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     562            }
     563            if (isset($author['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['uri'][0]['data'])) {
     564                $uri = $this->sanitize($author['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['uri'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($author['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['uri'][0]));
     565            }
     566            if (isset($author['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['email'][0]['data'])) {
     567                $email = $this->sanitize($author['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['email'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     568            }
     569            if ($name !== null || $email !== null || $uri !== null) {
     570                $authors[] = $this->registry->create(Author::class, [$name, $uri, $email]);
     571            }
     572        }
     573        if ($author = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'author')) {
     574            $name = null;
     575            $url = null;
     576            $email = null;
     577            if (isset($author[0]['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['name'][0]['data'])) {
     578                $name = $this->sanitize($author[0]['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['name'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     579            }
     580            if (isset($author[0]['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['url'][0]['data'])) {
     581                $url = $this->sanitize($author[0]['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['url'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($author[0]['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['url'][0]));
     582            }
     583            if (isset($author[0]['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['email'][0]['data'])) {
     584                $email = $this->sanitize($author[0]['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['email'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     585            }
     586            if ($name !== null || $email !== null || $url !== null) {
     587                $authors[] = $this->registry->create(Author::class, [$name, $url, $email]);
     588            }
     589        }
     590        if ($author = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_20, 'author')) {
     591            $authors[] = $this->registry->create(Author::class, [null, null, $this->sanitize($author[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT)]);
     592        }
     593        foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_11, 'creator') as $author) {
     594            $authors[] = $this->registry->create(Author::class, [$this->sanitize($author['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT), null, null]);
     595        }
     596        foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_10, 'creator') as $author) {
     597            $authors[] = $this->registry->create(Author::class, [$this->sanitize($author['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT), null, null]);
     598        }
     599        foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'author') as $author) {
     600            $authors[] = $this->registry->create(Author::class, [$this->sanitize($author['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT), null, null]);
     601        }
     602
     603        if (!empty($authors)) {
     604            return array_unique($authors);
     605        } elseif (($source = $this->get_source()) && ($authors = $source->get_authors())) {
     606            return $authors;
     607        } elseif ($authors = $this->feed->get_authors()) {
     608            return $authors;
     609        }
     610
     611        return null;
     612    }
     613
     614    /**
     615     * Get the copyright info for the item
     616     *
     617     * Uses `<atom:rights>` or `<dc:rights>`
     618     *
     619     * @since 1.1
     620     * @return string
     621     */
     622    public function get_copyright()
     623    {
     624        if ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'rights')) {
     625            return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_10_construct_type', [$return[0]['attribs']]), $this->get_base($return[0]));
     626        } elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_11, 'rights')) {
     627            return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     628        } elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_10, 'rights')) {
     629            return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     630        }
     631
     632        return null;
     633    }
     634
     635    /**
     636     * Get the posting date/time for the item
     637     *
     638     * Uses `<atom:published>`, `<atom:updated>`, `<atom:issued>`,
     639     * `<atom:modified>`, `<pubDate>` or `<dc:date>`
     640     *
     641     * Note: obeys PHP's timezone setting. To get a UTC date/time, use
     642     * {@see get_gmdate}
     643     *
     644     * @since Beta 2 (previously called `get_item_date` since 0.8)
     645     *
     646     * @param string $date_format Supports any PHP date format from {@see http://php.net/date} (empty for the raw data)
     647     * @return int|string|null
     648     */
     649    public function get_date($date_format = 'j F Y, g:i a')
     650    {
     651        if (!isset($this->data['date'])) {
     652            if ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'published')) {
     653                $this->data['date']['raw'] = $return[0]['data'];
     654            } elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_20, 'pubDate')) {
     655                $this->data['date']['raw'] = $return[0]['data'];
     656            } elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_11, 'date')) {
     657                $this->data['date']['raw'] = $return[0]['data'];
     658            } elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_10, 'date')) {
     659                $this->data['date']['raw'] = $return[0]['data'];
     660            } elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'updated')) {
     661                $this->data['date']['raw'] = $return[0]['data'];
     662            } elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'issued')) {
     663                $this->data['date']['raw'] = $return[0]['data'];
     664            } elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'created')) {
     665                $this->data['date']['raw'] = $return[0]['data'];
     666            } elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'modified')) {
     667                $this->data['date']['raw'] = $return[0]['data'];
     668            }
     669
     670            if (!empty($this->data['date']['raw'])) {
     671                $parser = $this->registry->call(Parse\Date::class, 'get');
     672                $this->data['date']['parsed'] = $parser->parse($this->data['date']['raw']) ?: null;
     673            } else {
     674                $this->data['date'] = null;
     675            }
     676        }
     677        if ($this->data['date']) {
     678            $date_format = (string) $date_format;
     679            switch ($date_format) {
     680                case '':
     681                    return $this->sanitize($this->data['date']['raw'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     682
     683                case 'U':
     684                    return $this->data['date']['parsed'];
     685
     686                default:
     687                    return date($date_format, $this->data['date']['parsed']);
     688            }
     689        }
     690
     691        return null;
     692    }
     693
     694    /**
     695     * Get the update date/time for the item
     696     *
     697     * Uses `<atom:updated>`
     698     *
     699     * Note: obeys PHP's timezone setting. To get a UTC date/time, use
     700     * {@see get_gmdate}
     701     *
     702     * @param string $date_format Supports any PHP date format from {@see http://php.net/date} (empty for the raw data)
     703     * @return int|string|null
     704     */
     705    public function get_updated_date($date_format = 'j F Y, g:i a')
     706    {
     707        if (!isset($this->data['updated'])) {
     708            if ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'updated')) {
     709                $this->data['updated']['raw'] = $return[0]['data'];
     710            }
     711
     712            if (!empty($this->data['updated']['raw'])) {
     713                $parser = $this->registry->call(Parse\Date::class, 'get');
     714                $this->data['updated']['parsed'] = $parser->parse($this->data['updated']['raw']) ?: null;
     715            } else {
     716                $this->data['updated'] = null;
     717            }
     718        }
     719        if ($this->data['updated']) {
     720            $date_format = (string) $date_format;
     721            switch ($date_format) {
     722                case '':
     723                    return $this->sanitize($this->data['updated']['raw'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     724
     725                case 'U':
     726                    return $this->data['updated']['parsed'];
     727
     728                default:
     729                    return date($date_format, $this->data['updated']['parsed']);
     730            }
     731        }
     732
     733        return null;
     734    }
     735
     736    /**
     737     * Get the localized posting date/time for the item
     738     *
     739     * Returns the date formatted in the localized language. To display in
     740     * languages other than the server's default, you need to change the locale
     741     * with {@link http://php.net/setlocale setlocale()}. The available
     742     * localizations depend on which ones are installed on your web server.
     743     *
     744     * @since 1.0
     745     *
     746     * @param string $date_format Supports any PHP date format from {@see http://php.net/strftime} (empty for the raw data)
     747     * @return int|string|null
     748     */
     749    public function get_local_date($date_format = '%c')
     750    {
     751        if (!$date_format) {
     752            return $this->sanitize($this->get_date(''), \SimplePie\SimplePie::CONSTRUCT_TEXT);
     753        } elseif (($date = $this->get_date('U')) !== null && $date !== false) {
     754            return strftime($date_format, $date);
     755        }
     756
     757        return null;
     758    }
     759
     760    /**
     761     * Get the posting date/time for the item (UTC time)
     762     *
     763     * @see get_date
     764     * @param string $date_format Supports any PHP date format from {@see http://php.net/date}
     765     * @return int|string|null
     766     */
     767    public function get_gmdate($date_format = 'j F Y, g:i a')
     768    {
     769        $date = $this->get_date('U');
     770        if ($date === null) {
     771            return null;
     772        }
     773
     774        return gmdate($date_format, $date);
     775    }
     776
     777    /**
     778     * Get the update date/time for the item (UTC time)
     779     *
     780     * @see get_updated_date
     781     * @param string $date_format Supports any PHP date format from {@see http://php.net/date}
     782     * @return int|string|null
     783     */
     784    public function get_updated_gmdate($date_format = 'j F Y, g:i a')
     785    {
     786        $date = $this->get_updated_date('U');
     787        if ($date === null) {
     788            return null;
     789        }
     790
     791        return gmdate($date_format, $date);
     792    }
     793
     794    /**
     795     * Get the permalink for the item
     796     *
     797     * Returns the first link available with a relationship of "alternate".
     798     * Identical to {@see get_link()} with key 0
     799     *
     800     * @see get_link
     801     * @since 0.8
     802     * @return string|null Permalink URL
     803     */
     804    public function get_permalink()
     805    {
     806        $link = $this->get_link();
     807        $enclosure = $this->get_enclosure(0);
     808        if ($link !== null) {
     809            return $link;
     810        } elseif ($enclosure !== null) {
     811            return $enclosure->get_link();
     812        }
     813
     814        return null;
     815    }
     816
     817    /**
     818     * Get a single link for the item
     819     *
     820     * @since Beta 3
     821     * @param int $key The link that you want to return.  Remember that arrays begin with 0, not 1
     822     * @param string $rel The relationship of the link to return
     823     * @return string|null Link URL
     824     */
     825    public function get_link($key = 0, $rel = 'alternate')
     826    {
     827        $links = $this->get_links($rel);
     828        if ($links && $links[$key] !== null) {
     829            return $links[$key];
     830        }
     831
     832        return null;
     833    }
     834
     835    /**
     836     * Get all links for the item
     837     *
     838     * Uses `<atom:link>`, `<link>` or `<guid>`
     839     *
     840     * @since Beta 2
     841     * @param string $rel The relationship of links to return
     842     * @return array|null Links found for the item (strings)
     843     */
     844    public function get_links($rel = 'alternate')
     845    {
     846        if (!isset($this->data['links'])) {
     847            $this->data['links'] = [];
     848            foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'link') as $link) {
     849                if (isset($link['attribs']['']['href'])) {
     850                    $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
     851                    $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($link));
     852                }
     853            }
     854            foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'link') as $link) {
     855                if (isset($link['attribs']['']['href'])) {
     856                    $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
     857                    $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($link));
     858                }
     859            }
     860            if ($links = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_10, 'link')) {
     861                $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($links[0]));
     862            }
     863            if ($links = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_090, 'link')) {
     864                $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($links[0]));
     865            }
     866            if ($links = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_20, 'link')) {
     867                $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($links[0]));
     868            }
     869            if ($links = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_20, 'guid')) {
     870                if (!isset($links[0]['attribs']['']['isPermaLink']) || strtolower(trim($links[0]['attribs']['']['isPermaLink'])) === 'true') {
     871                    $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($links[0]));
     872                }
     873            }
     874
     875            $keys = array_keys($this->data['links']);
     876            foreach ($keys as $key) {
     877                if ($this->registry->call(Misc::class, 'is_isegment_nz_nc', [$key])) {
     878                    if (isset($this->data['links'][\SimplePie\SimplePie::IANA_LINK_RELATIONS_REGISTRY . $key])) {
     879                        $this->data['links'][\SimplePie\SimplePie::IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][\SimplePie\SimplePie::IANA_LINK_RELATIONS_REGISTRY . $key]);
     880                        $this->data['links'][$key] = &$this->data['links'][\SimplePie\SimplePie::IANA_LINK_RELATIONS_REGISTRY . $key];
     881                    } else {
     882                        $this->data['links'][\SimplePie\SimplePie::IANA_LINK_RELATIONS_REGISTRY . $key] = &$this->data['links'][$key];
     883                    }
     884                } elseif (substr($key, 0, 41) === \SimplePie\SimplePie::IANA_LINK_RELATIONS_REGISTRY) {
     885                    $this->data['links'][substr($key, 41)] = &$this->data['links'][$key];
     886                }
     887                $this->data['links'][$key] = array_unique($this->data['links'][$key]);
     888            }
     889        }
     890        if (isset($this->data['links'][$rel])) {
     891            return $this->data['links'][$rel];
     892        }
     893
     894        return null;
     895    }
     896
     897    /**
     898     * Get an enclosure from the item
     899     *
     900     * Supports the <enclosure> RSS tag, as well as Media RSS and iTunes RSS.
     901     *
     902     * @since Beta 2
     903     * @todo Add ability to prefer one type of content over another (in a media group).
     904     * @param int $key The enclosure that you want to return.  Remember that arrays begin with 0, not 1
     905     * @return \SimplePie\Enclosure|null
     906     */
     907    public function get_enclosure($key = 0, $prefer = null)
     908    {
     909        $enclosures = $this->get_enclosures();
     910        if (isset($enclosures[$key])) {
     911            return $enclosures[$key];
     912        }
     913
     914        return null;
     915    }
     916
     917    /**
     918     * Get all available enclosures (podcasts, etc.)
     919     *
     920     * Supports the <enclosure> RSS tag, as well as Media RSS and iTunes RSS.
     921     *
     922     * At this point, we're pretty much assuming that all enclosures for an item
     923     * are the same content.  Anything else is too complicated to
     924     * properly support.
     925     *
     926     * @since Beta 2
     927     * @todo Add support for end-user defined sorting of enclosures by type/handler (so we can prefer the faster-loading FLV over MP4).
     928     * @todo If an element exists at a level, but its value is empty, we should fall back to the value from the parent (if it exists).
     929     * @return \SimplePie\Enclosure[]|null List of \SimplePie\Enclosure items
     930     */
     931    public function get_enclosures()
     932    {
     933        if (!isset($this->data['enclosures'])) {
     934            $this->data['enclosures'] = [];
     935
     936            // Elements
     937            $captions_parent = null;
     938            $categories_parent = null;
     939            $copyrights_parent = null;
     940            $credits_parent = null;
     941            $description_parent = null;
     942            $duration_parent = null;
     943            $hashes_parent = null;
     944            $keywords_parent = null;
     945            $player_parent = null;
     946            $ratings_parent = null;
     947            $restrictions_parent = null;
     948            $thumbnails_parent = null;
     949            $title_parent = null;
     950
     951            // Let's do the channel and item-level ones first, and just re-use them if we need to.
     952            $parent = $this->get_feed();
     953
     954            // CAPTIONS
     955            if ($captions = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'text')) {
     956                foreach ($captions as $caption) {
     957                    $caption_type = null;
     958                    $caption_lang = null;
     959                    $caption_startTime = null;
     960                    $caption_endTime = null;
     961                    $caption_text = null;
     962                    if (isset($caption['attribs']['']['type'])) {
     963                        $caption_type = $this->sanitize($caption['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     964                    }
     965                    if (isset($caption['attribs']['']['lang'])) {
     966                        $caption_lang = $this->sanitize($caption['attribs']['']['lang'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     967                    }
     968                    if (isset($caption['attribs']['']['start'])) {
     969                        $caption_startTime = $this->sanitize($caption['attribs']['']['start'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     970                    }
     971                    if (isset($caption['attribs']['']['end'])) {
     972                        $caption_endTime = $this->sanitize($caption['attribs']['']['end'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     973                    }
     974                    if (isset($caption['data'])) {
     975                        $caption_text = $this->sanitize($caption['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     976                    }
     977                    $captions_parent[] = $this->registry->create(Caption::class, [$caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text]);
     978                }
     979            } elseif ($captions = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'text')) {
     980                foreach ($captions as $caption) {
     981                    $caption_type = null;
     982                    $caption_lang = null;
     983                    $caption_startTime = null;
     984                    $caption_endTime = null;
     985                    $caption_text = null;
     986                    if (isset($caption['attribs']['']['type'])) {
     987                        $caption_type = $this->sanitize($caption['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     988                    }
     989                    if (isset($caption['attribs']['']['lang'])) {
     990                        $caption_lang = $this->sanitize($caption['attribs']['']['lang'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     991                    }
     992                    if (isset($caption['attribs']['']['start'])) {
     993                        $caption_startTime = $this->sanitize($caption['attribs']['']['start'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     994                    }
     995                    if (isset($caption['attribs']['']['end'])) {
     996                        $caption_endTime = $this->sanitize($caption['attribs']['']['end'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     997                    }
     998                    if (isset($caption['data'])) {
     999                        $caption_text = $this->sanitize($caption['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1000                    }
     1001                    $captions_parent[] = $this->registry->create(Caption::class, [$caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text]);
     1002                }
     1003            }
     1004            if (is_array($captions_parent)) {
     1005                $captions_parent = array_values(array_unique($captions_parent));
     1006            }
     1007
     1008            // CATEGORIES
     1009            foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'category') as $category) {
     1010                $term = null;
     1011                $scheme = null;
     1012                $label = null;
     1013                if (isset($category['data'])) {
     1014                    $term = $this->sanitize($category['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1015                }
     1016                if (isset($category['attribs']['']['scheme'])) {
     1017                    $scheme = $this->sanitize($category['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1018                } else {
     1019                    $scheme = 'http://search.yahoo.com/mrss/category_schema';
     1020                }
     1021                if (isset($category['attribs']['']['label'])) {
     1022                    $label = $this->sanitize($category['attribs']['']['label'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1023                }
     1024                $categories_parent[] = $this->registry->create(Category::class, [$term, $scheme, $label]);
     1025            }
     1026            foreach ((array) $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'category') as $category) {
     1027                $term = null;
     1028                $scheme = null;
     1029                $label = null;
     1030                if (isset($category['data'])) {
     1031                    $term = $this->sanitize($category['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1032                }
     1033                if (isset($category['attribs']['']['scheme'])) {
     1034                    $scheme = $this->sanitize($category['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1035                } else {
     1036                    $scheme = 'http://search.yahoo.com/mrss/category_schema';
     1037                }
     1038                if (isset($category['attribs']['']['label'])) {
     1039                    $label = $this->sanitize($category['attribs']['']['label'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1040                }
     1041                $categories_parent[] = $this->registry->create(Category::class, [$term, $scheme, $label]);
     1042            }
     1043            foreach ((array) $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'category') as $category) {
     1044                $term = null;
     1045                $scheme = 'http://www.itunes.com/dtds/podcast-1.0.dtd';
     1046                $label = null;
     1047                if (isset($category['attribs']['']['text'])) {
     1048                    $label = $this->sanitize($category['attribs']['']['text'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1049                }
     1050                $categories_parent[] = $this->registry->create(Category::class, [$term, $scheme, $label]);
     1051
     1052                if (isset($category['child'][\SimplePie\SimplePie::NAMESPACE_ITUNES]['category'])) {
     1053                    foreach ((array) $category['child'][\SimplePie\SimplePie::NAMESPACE_ITUNES]['category'] as $subcategory) {
     1054                        if (isset($subcategory['attribs']['']['text'])) {
     1055                            $label = $this->sanitize($subcategory['attribs']['']['text'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1056                        }
     1057                        $categories_parent[] = $this->registry->create(Category::class, [$term, $scheme, $label]);
     1058                    }
     1059                }
     1060            }
     1061            if (is_array($categories_parent)) {
     1062                $categories_parent = array_values(array_unique($categories_parent));
     1063            }
     1064
     1065            // COPYRIGHT
     1066            if ($copyright = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'copyright')) {
     1067                $copyright_url = null;
     1068                $copyright_label = null;
     1069                if (isset($copyright[0]['attribs']['']['url'])) {
     1070                    $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1071                }
     1072                if (isset($copyright[0]['data'])) {
     1073                    $copyright_label = $this->sanitize($copyright[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1074                }
     1075                $copyrights_parent = $this->registry->create(Copyright::class, [$copyright_url, $copyright_label]);
     1076            } elseif ($copyright = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'copyright')) {
     1077                $copyright_url = null;
     1078                $copyright_label = null;
     1079                if (isset($copyright[0]['attribs']['']['url'])) {
     1080                    $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1081                }
     1082                if (isset($copyright[0]['data'])) {
     1083                    $copyright_label = $this->sanitize($copyright[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1084                }
     1085                $copyrights_parent = $this->registry->create(Copyright::class, [$copyright_url, $copyright_label]);
     1086            }
     1087
     1088            // CREDITS
     1089            if ($credits = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'credit')) {
     1090                foreach ($credits as $credit) {
     1091                    $credit_role = null;
     1092                    $credit_scheme = null;
     1093                    $credit_name = null;
     1094                    if (isset($credit['attribs']['']['role'])) {
     1095                        $credit_role = $this->sanitize($credit['attribs']['']['role'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1096                    }
     1097                    if (isset($credit['attribs']['']['scheme'])) {
     1098                        $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1099                    } else {
     1100                        $credit_scheme = 'urn:ebu';
     1101                    }
     1102                    if (isset($credit['data'])) {
     1103                        $credit_name = $this->sanitize($credit['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1104                    }
     1105                    $credits_parent[] = $this->registry->create(Credit::class, [$credit_role, $credit_scheme, $credit_name]);
     1106                }
     1107            } elseif ($credits = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'credit')) {
     1108                foreach ($credits as $credit) {
     1109                    $credit_role = null;
     1110                    $credit_scheme = null;
     1111                    $credit_name = null;
     1112                    if (isset($credit['attribs']['']['role'])) {
     1113                        $credit_role = $this->sanitize($credit['attribs']['']['role'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1114                    }
     1115                    if (isset($credit['attribs']['']['scheme'])) {
     1116                        $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1117                    } else {
     1118                        $credit_scheme = 'urn:ebu';
     1119                    }
     1120                    if (isset($credit['data'])) {
     1121                        $credit_name = $this->sanitize($credit['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1122                    }
     1123                    $credits_parent[] = $this->registry->create(Credit::class, [$credit_role, $credit_scheme, $credit_name]);
     1124                }
     1125            }
     1126            if (is_array($credits_parent)) {
     1127                $credits_parent = array_values(array_unique($credits_parent));
     1128            }
     1129
     1130            // DESCRIPTION
     1131            if ($description_parent = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'description')) {
     1132                if (isset($description_parent[0]['data'])) {
     1133                    $description_parent = $this->sanitize($description_parent[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1134                }
     1135            } elseif ($description_parent = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'description')) {
     1136                if (isset($description_parent[0]['data'])) {
     1137                    $description_parent = $this->sanitize($description_parent[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1138                }
     1139            }
     1140
     1141            // DURATION
     1142            if ($duration_parent = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'duration')) {
     1143                $seconds = null;
     1144                $minutes = null;
     1145                $hours = null;
     1146                if (isset($duration_parent[0]['data'])) {
     1147                    $temp = explode(':', $this->sanitize($duration_parent[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT));
     1148                    if (sizeof($temp) > 0) {
     1149                        $seconds = (int) array_pop($temp);
     1150                    }
     1151                    if (sizeof($temp) > 0) {
     1152                        $minutes = (int) array_pop($temp);
     1153                        $seconds += $minutes * 60;
     1154                    }
     1155                    if (sizeof($temp) > 0) {
     1156                        $hours = (int) array_pop($temp);
     1157                        $seconds += $hours * 3600;
     1158                    }
     1159                    unset($temp);
     1160                    $duration_parent = $seconds;
     1161                }
     1162            }
     1163
     1164            // HASHES
     1165            if ($hashes_iterator = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'hash')) {
     1166                foreach ($hashes_iterator as $hash) {
     1167                    $value = null;
     1168                    $algo = null;
     1169                    if (isset($hash['data'])) {
     1170                        $value = $this->sanitize($hash['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1171                    }
     1172                    if (isset($hash['attribs']['']['algo'])) {
     1173                        $algo = $this->sanitize($hash['attribs']['']['algo'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1174                    } else {
     1175                        $algo = 'md5';
     1176                    }
     1177                    $hashes_parent[] = $algo.':'.$value;
     1178                }
     1179            } elseif ($hashes_iterator = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'hash')) {
     1180                foreach ($hashes_iterator as $hash) {
     1181                    $value = null;
     1182                    $algo = null;
     1183                    if (isset($hash['data'])) {
     1184                        $value = $this->sanitize($hash['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1185                    }
     1186                    if (isset($hash['attribs']['']['algo'])) {
     1187                        $algo = $this->sanitize($hash['attribs']['']['algo'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1188                    } else {
     1189                        $algo = 'md5';
     1190                    }
     1191                    $hashes_parent[] = $algo.':'.$value;
     1192                }
     1193            }
     1194            if (is_array($hashes_parent)) {
     1195                $hashes_parent = array_values(array_unique($hashes_parent));
     1196            }
     1197
     1198            // KEYWORDS
     1199            if ($keywords = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'keywords')) {
     1200                if (isset($keywords[0]['data'])) {
     1201                    $temp = explode(',', $this->sanitize($keywords[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT));
     1202                    foreach ($temp as $word) {
     1203                        $keywords_parent[] = trim($word);
     1204                    }
     1205                }
     1206                unset($temp);
     1207            } elseif ($keywords = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'keywords')) {
     1208                if (isset($keywords[0]['data'])) {
     1209                    $temp = explode(',', $this->sanitize($keywords[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT));
     1210                    foreach ($temp as $word) {
     1211                        $keywords_parent[] = trim($word);
     1212                    }
     1213                }
     1214                unset($temp);
     1215            } elseif ($keywords = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'keywords')) {
     1216                if (isset($keywords[0]['data'])) {
     1217                    $temp = explode(',', $this->sanitize($keywords[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT));
     1218                    foreach ($temp as $word) {
     1219                        $keywords_parent[] = trim($word);
     1220                    }
     1221                }
     1222                unset($temp);
     1223            } elseif ($keywords = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'keywords')) {
     1224                if (isset($keywords[0]['data'])) {
     1225                    $temp = explode(',', $this->sanitize($keywords[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT));
     1226                    foreach ($temp as $word) {
     1227                        $keywords_parent[] = trim($word);
     1228                    }
     1229                }
     1230                unset($temp);
     1231            }
     1232            if (is_array($keywords_parent)) {
     1233                $keywords_parent = array_values(array_unique($keywords_parent));
     1234            }
     1235
     1236            // PLAYER
     1237            if ($player_parent = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'player')) {
     1238                if (isset($player_parent[0]['attribs']['']['url'])) {
     1239                    $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI);
     1240                }
     1241            } elseif ($player_parent = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'player')) {
     1242                if (isset($player_parent[0]['attribs']['']['url'])) {
     1243                    $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI);
     1244                }
     1245            }
     1246
     1247            // RATINGS
     1248            if ($ratings = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'rating')) {
     1249                foreach ($ratings as $rating) {
     1250                    $rating_scheme = null;
     1251                    $rating_value = null;
     1252                    if (isset($rating['attribs']['']['scheme'])) {
     1253                        $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1254                    } else {
     1255                        $rating_scheme = 'urn:simple';
     1256                    }
     1257                    if (isset($rating['data'])) {
     1258                        $rating_value = $this->sanitize($rating['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1259                    }
     1260                    $ratings_parent[] = $this->registry->create(Rating::class, [$rating_scheme, $rating_value]);
     1261                }
     1262            } elseif ($ratings = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'explicit')) {
     1263                foreach ($ratings as $rating) {
     1264                    $rating_scheme = 'urn:itunes';
     1265                    $rating_value = null;
     1266                    if (isset($rating['data'])) {
     1267                        $rating_value = $this->sanitize($rating['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1268                    }
     1269                    $ratings_parent[] = $this->registry->create(Rating::class, [$rating_scheme, $rating_value]);
     1270                }
     1271            } elseif ($ratings = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'rating')) {
     1272                foreach ($ratings as $rating) {
     1273                    $rating_scheme = null;
     1274                    $rating_value = null;
     1275                    if (isset($rating['attribs']['']['scheme'])) {
     1276                        $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1277                    } else {
     1278                        $rating_scheme = 'urn:simple';
     1279                    }
     1280                    if (isset($rating['data'])) {
     1281                        $rating_value = $this->sanitize($rating['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1282                    }
     1283                    $ratings_parent[] = $this->registry->create(Rating::class, [$rating_scheme, $rating_value]);
     1284                }
     1285            } elseif ($ratings = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'explicit')) {
     1286                foreach ($ratings as $rating) {
     1287                    $rating_scheme = 'urn:itunes';
     1288                    $rating_value = null;
     1289                    if (isset($rating['data'])) {
     1290                        $rating_value = $this->sanitize($rating['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1291                    }
     1292                    $ratings_parent[] = $this->registry->create(Rating::class, [$rating_scheme, $rating_value]);
     1293                }
     1294            }
     1295            if (is_array($ratings_parent)) {
     1296                $ratings_parent = array_values(array_unique($ratings_parent));
     1297            }
     1298
     1299            // RESTRICTIONS
     1300            if ($restrictions = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'restriction')) {
     1301                foreach ($restrictions as $restriction) {
     1302                    $restriction_relationship = null;
     1303                    $restriction_type = null;
     1304                    $restriction_value = null;
     1305                    if (isset($restriction['attribs']['']['relationship'])) {
     1306                        $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1307                    }
     1308                    if (isset($restriction['attribs']['']['type'])) {
     1309                        $restriction_type = $this->sanitize($restriction['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1310                    }
     1311                    if (isset($restriction['data'])) {
     1312                        $restriction_value = $this->sanitize($restriction['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1313                    }
     1314                    $restrictions_parent[] = $this->registry->create(Restriction::class, [$restriction_relationship, $restriction_type, $restriction_value]);
     1315                }
     1316            } elseif ($restrictions = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'block')) {
     1317                foreach ($restrictions as $restriction) {
     1318                    $restriction_relationship = 'allow';
     1319                    $restriction_type = null;
     1320                    $restriction_value = 'itunes';
     1321                    if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes') {
     1322                        $restriction_relationship = 'deny';
     1323                    }
     1324                    $restrictions_parent[] = $this->registry->create(Restriction::class, [$restriction_relationship, $restriction_type, $restriction_value]);
     1325                }
     1326            } elseif ($restrictions = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'restriction')) {
     1327                foreach ($restrictions as $restriction) {
     1328                    $restriction_relationship = null;
     1329                    $restriction_type = null;
     1330                    $restriction_value = null;
     1331                    if (isset($restriction['attribs']['']['relationship'])) {
     1332                        $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1333                    }
     1334                    if (isset($restriction['attribs']['']['type'])) {
     1335                        $restriction_type = $this->sanitize($restriction['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1336                    }
     1337                    if (isset($restriction['data'])) {
     1338                        $restriction_value = $this->sanitize($restriction['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1339                    }
     1340                    $restrictions_parent[] = $this->registry->create(Restriction::class, [$restriction_relationship, $restriction_type, $restriction_value]);
     1341                }
     1342            } elseif ($restrictions = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'block')) {
     1343                foreach ($restrictions as $restriction) {
     1344                    $restriction_relationship = 'allow';
     1345                    $restriction_type = null;
     1346                    $restriction_value = 'itunes';
     1347                    if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes') {
     1348                        $restriction_relationship = 'deny';
     1349                    }
     1350                    $restrictions_parent[] = $this->registry->create(Restriction::class, [$restriction_relationship, $restriction_type, $restriction_value]);
     1351                }
     1352            }
     1353            if (is_array($restrictions_parent)) {
     1354                $restrictions_parent = array_values(array_unique($restrictions_parent));
     1355            } else {
     1356                $restrictions_parent = [new \SimplePie\Restriction('allow', null, 'default')];
     1357            }
     1358
     1359            // THUMBNAILS
     1360            if ($thumbnails = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'thumbnail')) {
     1361                foreach ($thumbnails as $thumbnail) {
     1362                    if (isset($thumbnail['attribs']['']['url'])) {
     1363                        $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI);
     1364                    }
     1365                }
     1366            } elseif ($thumbnails = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'thumbnail')) {
     1367                foreach ($thumbnails as $thumbnail) {
     1368                    if (isset($thumbnail['attribs']['']['url'])) {
     1369                        $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI);
     1370                    }
     1371                }
     1372            }
     1373
     1374            // TITLES
     1375            if ($title_parent = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'title')) {
     1376                if (isset($title_parent[0]['data'])) {
     1377                    $title_parent = $this->sanitize($title_parent[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1378                }
     1379            } elseif ($title_parent = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'title')) {
     1380                if (isset($title_parent[0]['data'])) {
     1381                    $title_parent = $this->sanitize($title_parent[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1382                }
     1383            }
     1384
     1385            // Clear the memory
     1386            unset($parent);
     1387
     1388            // Attributes
     1389            $bitrate = null;
     1390            $channels = null;
     1391            $duration = null;
     1392            $expression = null;
     1393            $framerate = null;
     1394            $height = null;
     1395            $javascript = null;
     1396            $lang = null;
     1397            $length = null;
     1398            $medium = null;
     1399            $samplingrate = null;
     1400            $type = null;
     1401            $url = null;
     1402            $width = null;
     1403
     1404            // Elements
     1405            $captions = null;
     1406            $categories = null;
     1407            $copyrights = null;
     1408            $credits = null;
     1409            $description = null;
     1410            $hashes = null;
     1411            $keywords = null;
     1412            $player = null;
     1413            $ratings = null;
     1414            $restrictions = null;
     1415            $thumbnails = null;
     1416            $title = null;
     1417
     1418            // If we have media:group tags, loop through them.
     1419            foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'group') as $group) {
     1420                if (isset($group['child']) && isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['content'])) {
     1421                    // If we have media:content tags, loop through them.
     1422                    foreach ((array) $group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['content'] as $content) {
     1423                        if (isset($content['attribs']['']['url'])) {
     1424                            // Attributes
     1425                            $bitrate = null;
     1426                            $channels = null;
     1427                            $duration = null;
     1428                            $expression = null;
     1429                            $framerate = null;
     1430                            $height = null;
     1431                            $javascript = null;
     1432                            $lang = null;
     1433                            $length = null;
     1434                            $medium = null;
     1435                            $samplingrate = null;
     1436                            $type = null;
     1437                            $url = null;
     1438                            $width = null;
     1439
     1440                            // Elements
     1441                            $captions = null;
     1442                            $categories = null;
     1443                            $copyrights = null;
     1444                            $credits = null;
     1445                            $description = null;
     1446                            $hashes = null;
     1447                            $keywords = null;
     1448                            $player = null;
     1449                            $ratings = null;
     1450                            $restrictions = null;
     1451                            $thumbnails = null;
     1452                            $title = null;
     1453
     1454                            // Start checking the attributes of media:content
     1455                            if (isset($content['attribs']['']['bitrate'])) {
     1456                                $bitrate = $this->sanitize($content['attribs']['']['bitrate'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1457                            }
     1458                            if (isset($content['attribs']['']['channels'])) {
     1459                                $channels = $this->sanitize($content['attribs']['']['channels'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1460                            }
     1461                            if (isset($content['attribs']['']['duration'])) {
     1462                                $duration = $this->sanitize($content['attribs']['']['duration'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1463                            } else {
     1464                                $duration = $duration_parent;
     1465                            }
     1466                            if (isset($content['attribs']['']['expression'])) {
     1467                                $expression = $this->sanitize($content['attribs']['']['expression'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1468                            }
     1469                            if (isset($content['attribs']['']['framerate'])) {
     1470                                $framerate = $this->sanitize($content['attribs']['']['framerate'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1471                            }
     1472                            if (isset($content['attribs']['']['height'])) {
     1473                                $height = $this->sanitize($content['attribs']['']['height'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1474                            }
     1475                            if (isset($content['attribs']['']['lang'])) {
     1476                                $lang = $this->sanitize($content['attribs']['']['lang'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1477                            }
     1478                            if (isset($content['attribs']['']['fileSize'])) {
     1479                                $length = intval($content['attribs']['']['fileSize']);
     1480                            }
     1481                            if (isset($content['attribs']['']['medium'])) {
     1482                                $medium = $this->sanitize($content['attribs']['']['medium'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1483                            }
     1484                            if (isset($content['attribs']['']['samplingrate'])) {
     1485                                $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1486                            }
     1487                            if (isset($content['attribs']['']['type'])) {
     1488                                $type = $this->sanitize($content['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1489                            }
     1490                            if (isset($content['attribs']['']['width'])) {
     1491                                $width = $this->sanitize($content['attribs']['']['width'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1492                            }
     1493                            $url = $this->sanitize($content['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI);
     1494
     1495                            // Checking the other optional media: elements. Priority: media:content, media:group, item, channel
     1496
     1497                            // CAPTIONS
     1498                            if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['text'])) {
     1499                                foreach ($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['text'] as $caption) {
     1500                                    $caption_type = null;
     1501                                    $caption_lang = null;
     1502                                    $caption_startTime = null;
     1503                                    $caption_endTime = null;
     1504                                    $caption_text = null;
     1505                                    if (isset($caption['attribs']['']['type'])) {
     1506                                        $caption_type = $this->sanitize($caption['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1507                                    }
     1508                                    if (isset($caption['attribs']['']['lang'])) {
     1509                                        $caption_lang = $this->sanitize($caption['attribs']['']['lang'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1510                                    }
     1511                                    if (isset($caption['attribs']['']['start'])) {
     1512                                        $caption_startTime = $this->sanitize($caption['attribs']['']['start'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1513                                    }
     1514                                    if (isset($caption['attribs']['']['end'])) {
     1515                                        $caption_endTime = $this->sanitize($caption['attribs']['']['end'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1516                                    }
     1517                                    if (isset($caption['data'])) {
     1518                                        $caption_text = $this->sanitize($caption['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1519                                    }
     1520                                    $captions[] = $this->registry->create(Caption::class, [$caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text]);
     1521                                }
     1522                                if (is_array($captions)) {
     1523                                    $captions = array_values(array_unique($captions));
     1524                                }
     1525                            } elseif (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['text'])) {
     1526                                foreach ($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['text'] as $caption) {
     1527                                    $caption_type = null;
     1528                                    $caption_lang = null;
     1529                                    $caption_startTime = null;
     1530                                    $caption_endTime = null;
     1531                                    $caption_text = null;
     1532                                    if (isset($caption['attribs']['']['type'])) {
     1533                                        $caption_type = $this->sanitize($caption['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1534                                    }
     1535                                    if (isset($caption['attribs']['']['lang'])) {
     1536                                        $caption_lang = $this->sanitize($caption['attribs']['']['lang'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1537                                    }
     1538                                    if (isset($caption['attribs']['']['start'])) {
     1539                                        $caption_startTime = $this->sanitize($caption['attribs']['']['start'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1540                                    }
     1541                                    if (isset($caption['attribs']['']['end'])) {
     1542                                        $caption_endTime = $this->sanitize($caption['attribs']['']['end'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1543                                    }
     1544                                    if (isset($caption['data'])) {
     1545                                        $caption_text = $this->sanitize($caption['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1546                                    }
     1547                                    $captions[] = $this->registry->create(Caption::class, [$caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text]);
     1548                                }
     1549                                if (is_array($captions)) {
     1550                                    $captions = array_values(array_unique($captions));
     1551                                }
     1552                            } else {
     1553                                $captions = $captions_parent;
     1554                            }
     1555
     1556                            // CATEGORIES
     1557                            if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['category'])) {
     1558                                foreach ((array) $content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['category'] as $category) {
     1559                                    $term = null;
     1560                                    $scheme = null;
     1561                                    $label = null;
     1562                                    if (isset($category['data'])) {
     1563                                        $term = $this->sanitize($category['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1564                                    }
     1565                                    if (isset($category['attribs']['']['scheme'])) {
     1566                                        $scheme = $this->sanitize($category['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1567                                    } else {
     1568                                        $scheme = 'http://search.yahoo.com/mrss/category_schema';
     1569                                    }
     1570                                    if (isset($category['attribs']['']['label'])) {
     1571                                        $label = $this->sanitize($category['attribs']['']['label'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1572                                    }
     1573                                    $categories[] = $this->registry->create(Category::class, [$term, $scheme, $label]);
     1574                                }
     1575                            }
     1576                            if (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['category'])) {
     1577                                foreach ((array) $group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['category'] as $category) {
     1578                                    $term = null;
     1579                                    $scheme = null;
     1580                                    $label = null;
     1581                                    if (isset($category['data'])) {
     1582                                        $term = $this->sanitize($category['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1583                                    }
     1584                                    if (isset($category['attribs']['']['scheme'])) {
     1585                                        $scheme = $this->sanitize($category['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1586                                    } else {
     1587                                        $scheme = 'http://search.yahoo.com/mrss/category_schema';
     1588                                    }
     1589                                    if (isset($category['attribs']['']['label'])) {
     1590                                        $label = $this->sanitize($category['attribs']['']['label'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1591                                    }
     1592                                    $categories[] = $this->registry->create(Category::class, [$term, $scheme, $label]);
     1593                                }
     1594                            }
     1595                            if (is_array($categories) && is_array($categories_parent)) {
     1596                                $categories = array_values(array_unique(array_merge($categories, $categories_parent)));
     1597                            } elseif (is_array($categories)) {
     1598                                $categories = array_values(array_unique($categories));
     1599                            } elseif (is_array($categories_parent)) {
     1600                                $categories = array_values(array_unique($categories_parent));
     1601                            }
     1602
     1603                            // COPYRIGHTS
     1604                            if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'])) {
     1605                                $copyright_url = null;
     1606                                $copyright_label = null;
     1607                                if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) {
     1608                                    $copyright_url = $this->sanitize($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1609                                }
     1610                                if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'][0]['data'])) {
     1611                                    $copyright_label = $this->sanitize($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1612                                }
     1613                                $copyrights = $this->registry->create(Copyright::class, [$copyright_url, $copyright_label]);
     1614                            } elseif (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'])) {
     1615                                $copyright_url = null;
     1616                                $copyright_label = null;
     1617                                if (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) {
     1618                                    $copyright_url = $this->sanitize($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1619                                }
     1620                                if (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'][0]['data'])) {
     1621                                    $copyright_label = $this->sanitize($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1622                                }
     1623                                $copyrights = $this->registry->create(Copyright::class, [$copyright_url, $copyright_label]);
     1624                            } else {
     1625                                $copyrights = $copyrights_parent;
     1626                            }
     1627
     1628                            // CREDITS
     1629                            if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['credit'])) {
     1630                                foreach ($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['credit'] as $credit) {
     1631                                    $credit_role = null;
     1632                                    $credit_scheme = null;
     1633                                    $credit_name = null;
     1634                                    if (isset($credit['attribs']['']['role'])) {
     1635                                        $credit_role = $this->sanitize($credit['attribs']['']['role'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1636                                    }
     1637                                    if (isset($credit['attribs']['']['scheme'])) {
     1638                                        $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1639                                    } else {
     1640                                        $credit_scheme = 'urn:ebu';
     1641                                    }
     1642                                    if (isset($credit['data'])) {
     1643                                        $credit_name = $this->sanitize($credit['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1644                                    }
     1645                                    $credits[] = $this->registry->create(Credit::class, [$credit_role, $credit_scheme, $credit_name]);
     1646                                }
     1647                                if (is_array($credits)) {
     1648                                    $credits = array_values(array_unique($credits));
     1649                                }
     1650                            } elseif (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['credit'])) {
     1651                                foreach ($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['credit'] as $credit) {
     1652                                    $credit_role = null;
     1653                                    $credit_scheme = null;
     1654                                    $credit_name = null;
     1655                                    if (isset($credit['attribs']['']['role'])) {
     1656                                        $credit_role = $this->sanitize($credit['attribs']['']['role'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1657                                    }
     1658                                    if (isset($credit['attribs']['']['scheme'])) {
     1659                                        $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1660                                    } else {
     1661                                        $credit_scheme = 'urn:ebu';
     1662                                    }
     1663                                    if (isset($credit['data'])) {
     1664                                        $credit_name = $this->sanitize($credit['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1665                                    }
     1666                                    $credits[] = $this->registry->create(Credit::class, [$credit_role, $credit_scheme, $credit_name]);
     1667                                }
     1668                                if (is_array($credits)) {
     1669                                    $credits = array_values(array_unique($credits));
     1670                                }
     1671                            } else {
     1672                                $credits = $credits_parent;
     1673                            }
     1674
     1675                            // DESCRIPTION
     1676                            if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['description'])) {
     1677                                $description = $this->sanitize($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['description'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1678                            } elseif (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['description'])) {
     1679                                $description = $this->sanitize($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['description'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1680                            } else {
     1681                                $description = $description_parent;
     1682                            }
     1683
     1684                            // HASHES
     1685                            if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['hash'])) {
     1686                                foreach ($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['hash'] as $hash) {
     1687                                    $value = null;
     1688                                    $algo = null;
     1689                                    if (isset($hash['data'])) {
     1690                                        $value = $this->sanitize($hash['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1691                                    }
     1692                                    if (isset($hash['attribs']['']['algo'])) {
     1693                                        $algo = $this->sanitize($hash['attribs']['']['algo'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1694                                    } else {
     1695                                        $algo = 'md5';
     1696                                    }
     1697                                    $hashes[] = $algo.':'.$value;
     1698                                }
     1699                                if (is_array($hashes)) {
     1700                                    $hashes = array_values(array_unique($hashes));
     1701                                }
     1702                            } elseif (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['hash'])) {
     1703                                foreach ($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['hash'] as $hash) {
     1704                                    $value = null;
     1705                                    $algo = null;
     1706                                    if (isset($hash['data'])) {
     1707                                        $value = $this->sanitize($hash['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1708                                    }
     1709                                    if (isset($hash['attribs']['']['algo'])) {
     1710                                        $algo = $this->sanitize($hash['attribs']['']['algo'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1711                                    } else {
     1712                                        $algo = 'md5';
     1713                                    }
     1714                                    $hashes[] = $algo.':'.$value;
     1715                                }
     1716                                if (is_array($hashes)) {
     1717                                    $hashes = array_values(array_unique($hashes));
     1718                                }
     1719                            } else {
     1720                                $hashes = $hashes_parent;
     1721                            }
     1722
     1723                            // KEYWORDS
     1724                            if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['keywords'])) {
     1725                                if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['keywords'][0]['data'])) {
     1726                                    $temp = explode(',', $this->sanitize($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['keywords'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT));
     1727                                    foreach ($temp as $word) {
     1728                                        $keywords[] = trim($word);
     1729                                    }
     1730                                    unset($temp);
     1731                                }
     1732                                if (is_array($keywords)) {
     1733                                    $keywords = array_values(array_unique($keywords));
     1734                                }
     1735                            } elseif (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['keywords'])) {
     1736                                if (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['keywords'][0]['data'])) {
     1737                                    $temp = explode(',', $this->sanitize($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['keywords'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT));
     1738                                    foreach ($temp as $word) {
     1739                                        $keywords[] = trim($word);
     1740                                    }
     1741                                    unset($temp);
     1742                                }
     1743                                if (is_array($keywords)) {
     1744                                    $keywords = array_values(array_unique($keywords));
     1745                                }
     1746                            } else {
     1747                                $keywords = $keywords_parent;
     1748                            }
     1749
     1750                            // PLAYER
     1751                            if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['player'])) {
     1752                                $player = $this->sanitize($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI);
     1753                            } elseif (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['player'])) {
     1754                                $player = $this->sanitize($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI);
     1755                            } else {
     1756                                $player = $player_parent;
     1757                            }
     1758
     1759                            // RATINGS
     1760                            if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['rating'])) {
     1761                                foreach ($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['rating'] as $rating) {
     1762                                    $rating_scheme = null;
     1763                                    $rating_value = null;
     1764                                    if (isset($rating['attribs']['']['scheme'])) {
     1765                                        $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1766                                    } else {
     1767                                        $rating_scheme = 'urn:simple';
     1768                                    }
     1769                                    if (isset($rating['data'])) {
     1770                                        $rating_value = $this->sanitize($rating['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1771                                    }
     1772                                    $ratings[] = $this->registry->create(Rating::class, [$rating_scheme, $rating_value]);
     1773                                }
     1774                                if (is_array($ratings)) {
     1775                                    $ratings = array_values(array_unique($ratings));
     1776                                }
     1777                            } elseif (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['rating'])) {
     1778                                foreach ($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['rating'] as $rating) {
     1779                                    $rating_scheme = null;
     1780                                    $rating_value = null;
     1781                                    if (isset($rating['attribs']['']['scheme'])) {
     1782                                        $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1783                                    } else {
     1784                                        $rating_scheme = 'urn:simple';
     1785                                    }
     1786                                    if (isset($rating['data'])) {
     1787                                        $rating_value = $this->sanitize($rating['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1788                                    }
     1789                                    $ratings[] = $this->registry->create(Rating::class, [$rating_scheme, $rating_value]);
     1790                                }
     1791                                if (is_array($ratings)) {
     1792                                    $ratings = array_values(array_unique($ratings));
     1793                                }
     1794                            } else {
     1795                                $ratings = $ratings_parent;
     1796                            }
     1797
     1798                            // RESTRICTIONS
     1799                            if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['restriction'])) {
     1800                                foreach ($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['restriction'] as $restriction) {
     1801                                    $restriction_relationship = null;
     1802                                    $restriction_type = null;
     1803                                    $restriction_value = null;
     1804                                    if (isset($restriction['attribs']['']['relationship'])) {
     1805                                        $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1806                                    }
     1807                                    if (isset($restriction['attribs']['']['type'])) {
     1808                                        $restriction_type = $this->sanitize($restriction['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1809                                    }
     1810                                    if (isset($restriction['data'])) {
     1811                                        $restriction_value = $this->sanitize($restriction['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1812                                    }
     1813                                    $restrictions[] = $this->registry->create(Restriction::class, [$restriction_relationship, $restriction_type, $restriction_value]);
     1814                                }
     1815                                if (is_array($restrictions)) {
     1816                                    $restrictions = array_values(array_unique($restrictions));
     1817                                }
     1818                            } elseif (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['restriction'])) {
     1819                                foreach ($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['restriction'] as $restriction) {
     1820                                    $restriction_relationship = null;
     1821                                    $restriction_type = null;
     1822                                    $restriction_value = null;
     1823                                    if (isset($restriction['attribs']['']['relationship'])) {
     1824                                        $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1825                                    }
     1826                                    if (isset($restriction['attribs']['']['type'])) {
     1827                                        $restriction_type = $this->sanitize($restriction['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1828                                    }
     1829                                    if (isset($restriction['data'])) {
     1830                                        $restriction_value = $this->sanitize($restriction['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1831                                    }
     1832                                    $restrictions[] = $this->registry->create(Restriction::class, [$restriction_relationship, $restriction_type, $restriction_value]);
     1833                                }
     1834                                if (is_array($restrictions)) {
     1835                                    $restrictions = array_values(array_unique($restrictions));
     1836                                }
     1837                            } else {
     1838                                $restrictions = $restrictions_parent;
     1839                            }
     1840
     1841                            // THUMBNAILS
     1842                            if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['thumbnail'])) {
     1843                                foreach ($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) {
     1844                                    $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI);
     1845                                }
     1846                                if (is_array($thumbnails)) {
     1847                                    $thumbnails = array_values(array_unique($thumbnails));
     1848                                }
     1849                            } elseif (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['thumbnail'])) {
     1850                                foreach ($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) {
     1851                                    $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI);
     1852                                }
     1853                                if (is_array($thumbnails)) {
     1854                                    $thumbnails = array_values(array_unique($thumbnails));
     1855                                }
     1856                            } else {
     1857                                $thumbnails = $thumbnails_parent;
     1858                            }
     1859
     1860                            // TITLES
     1861                            if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['title'])) {
     1862                                $title = $this->sanitize($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['title'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1863                            } elseif (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['title'])) {
     1864                                $title = $this->sanitize($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['title'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1865                            } else {
     1866                                $title = $title_parent;
     1867                            }
     1868
     1869                            $this->data['enclosures'][] = $this->registry->create(Enclosure::class, [$url, $type, $length, null, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width]);
     1870                        }
     1871                    }
     1872                }
     1873            }
     1874
     1875            // If we have standalone media:content tags, loop through them.
     1876            if (isset($this->data['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['content'])) {
     1877                foreach ((array) $this->data['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['content'] as $content) {
     1878                    if (isset($content['attribs']['']['url']) || isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['player'])) {
     1879                        // Attributes
     1880                        $bitrate = null;
     1881                        $channels = null;
     1882                        $duration = null;
     1883                        $expression = null;
     1884                        $framerate = null;
     1885                        $height = null;
     1886                        $javascript = null;
     1887                        $lang = null;
     1888                        $length = null;
     1889                        $medium = null;
     1890                        $samplingrate = null;
     1891                        $type = null;
     1892                        $url = null;
     1893                        $width = null;
     1894
     1895                        // Elements
     1896                        $captions = null;
     1897                        $categories = null;
     1898                        $copyrights = null;
     1899                        $credits = null;
     1900                        $description = null;
     1901                        $hashes = null;
     1902                        $keywords = null;
     1903                        $player = null;
     1904                        $ratings = null;
     1905                        $restrictions = null;
     1906                        $thumbnails = null;
     1907                        $title = null;
     1908
     1909                        // Start checking the attributes of media:content
     1910                        if (isset($content['attribs']['']['bitrate'])) {
     1911                            $bitrate = $this->sanitize($content['attribs']['']['bitrate'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1912                        }
     1913                        if (isset($content['attribs']['']['channels'])) {
     1914                            $channels = $this->sanitize($content['attribs']['']['channels'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1915                        }
     1916                        if (isset($content['attribs']['']['duration'])) {
     1917                            $duration = $this->sanitize($content['attribs']['']['duration'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1918                        } else {
     1919                            $duration = $duration_parent;
     1920                        }
     1921                        if (isset($content['attribs']['']['expression'])) {
     1922                            $expression = $this->sanitize($content['attribs']['']['expression'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1923                        }
     1924                        if (isset($content['attribs']['']['framerate'])) {
     1925                            $framerate = $this->sanitize($content['attribs']['']['framerate'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1926                        }
     1927                        if (isset($content['attribs']['']['height'])) {
     1928                            $height = $this->sanitize($content['attribs']['']['height'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1929                        }
     1930                        if (isset($content['attribs']['']['lang'])) {
     1931                            $lang = $this->sanitize($content['attribs']['']['lang'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1932                        }
     1933                        if (isset($content['attribs']['']['fileSize'])) {
     1934                            $length = intval($content['attribs']['']['fileSize']);
     1935                        }
     1936                        if (isset($content['attribs']['']['medium'])) {
     1937                            $medium = $this->sanitize($content['attribs']['']['medium'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1938                        }
     1939                        if (isset($content['attribs']['']['samplingrate'])) {
     1940                            $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1941                        }
     1942                        if (isset($content['attribs']['']['type'])) {
     1943                            $type = $this->sanitize($content['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1944                        }
     1945                        if (isset($content['attribs']['']['width'])) {
     1946                            $width = $this->sanitize($content['attribs']['']['width'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1947                        }
     1948                        if (isset($content['attribs']['']['url'])) {
     1949                            $url = $this->sanitize($content['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI);
     1950                        }
     1951                        // Checking the other optional media: elements. Priority: media:content, media:group, item, channel
     1952
     1953                        // CAPTIONS
     1954                        if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['text'])) {
     1955                            foreach ($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['text'] as $caption) {
     1956                                $caption_type = null;
     1957                                $caption_lang = null;
     1958                                $caption_startTime = null;
     1959                                $caption_endTime = null;
     1960                                $caption_text = null;
     1961                                if (isset($caption['attribs']['']['type'])) {
     1962                                    $caption_type = $this->sanitize($caption['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1963                                }
     1964                                if (isset($caption['attribs']['']['lang'])) {
     1965                                    $caption_lang = $this->sanitize($caption['attribs']['']['lang'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1966                                }
     1967                                if (isset($caption['attribs']['']['start'])) {
     1968                                    $caption_startTime = $this->sanitize($caption['attribs']['']['start'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1969                                }
     1970                                if (isset($caption['attribs']['']['end'])) {
     1971                                    $caption_endTime = $this->sanitize($caption['attribs']['']['end'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1972                                }
     1973                                if (isset($caption['data'])) {
     1974                                    $caption_text = $this->sanitize($caption['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1975                                }
     1976                                $captions[] = $this->registry->create(Caption::class, [$caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text]);
     1977                            }
     1978                            if (is_array($captions)) {
     1979                                $captions = array_values(array_unique($captions));
     1980                            }
     1981                        } else {
     1982                            $captions = $captions_parent;
     1983                        }
     1984
     1985                        // CATEGORIES
     1986                        if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['category'])) {
     1987                            foreach ((array) $content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['category'] as $category) {
     1988                                $term = null;
     1989                                $scheme = null;
     1990                                $label = null;
     1991                                if (isset($category['data'])) {
     1992                                    $term = $this->sanitize($category['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1993                                }
     1994                                if (isset($category['attribs']['']['scheme'])) {
     1995                                    $scheme = $this->sanitize($category['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     1996                                } else {
     1997                                    $scheme = 'http://search.yahoo.com/mrss/category_schema';
     1998                                }
     1999                                if (isset($category['attribs']['']['label'])) {
     2000                                    $label = $this->sanitize($category['attribs']['']['label'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     2001                                }
     2002                                $categories[] = $this->registry->create(Category::class, [$term, $scheme, $label]);
     2003                            }
     2004                        }
     2005                        if (is_array($categories) && is_array($categories_parent)) {
     2006                            $categories = array_values(array_unique(array_merge($categories, $categories_parent)));
     2007                        } elseif (is_array($categories)) {
     2008                            $categories = array_values(array_unique($categories));
     2009                        } elseif (is_array($categories_parent)) {
     2010                            $categories = array_values(array_unique($categories_parent));
     2011                        } else {
     2012                            $categories = null;
     2013                        }
     2014
     2015                        // COPYRIGHTS
     2016                        if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'])) {
     2017                            $copyright_url = null;
     2018                            $copyright_label = null;
     2019                            if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) {
     2020                                $copyright_url = $this->sanitize($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     2021                            }
     2022                            if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'][0]['data'])) {
     2023                                $copyright_label = $this->sanitize($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     2024                            }
     2025                            $copyrights = $this->registry->create(Copyright::class, [$copyright_url, $copyright_label]);
     2026                        } else {
     2027                            $copyrights = $copyrights_parent;
     2028                        }
     2029
     2030                        // CREDITS
     2031                        if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['credit'])) {
     2032                            foreach ($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['credit'] as $credit) {
     2033                                $credit_role = null;
     2034                                $credit_scheme = null;
     2035                                $credit_name = null;
     2036                                if (isset($credit['attribs']['']['role'])) {
     2037                                    $credit_role = $this->sanitize($credit['attribs']['']['role'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     2038                                }
     2039                                if (isset($credit['attribs']['']['scheme'])) {
     2040                                    $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     2041                                } else {
     2042                                    $credit_scheme = 'urn:ebu';
     2043                                }
     2044                                if (isset($credit['data'])) {
     2045                                    $credit_name = $this->sanitize($credit['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     2046                                }
     2047                                $credits[] = $this->registry->create(Credit::class, [$credit_role, $credit_scheme, $credit_name]);
     2048                            }
     2049                            if (is_array($credits)) {
     2050                                $credits = array_values(array_unique($credits));
     2051                            }
     2052                        } else {
     2053                            $credits = $credits_parent;
     2054                        }
     2055
     2056                        // DESCRIPTION
     2057                        if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['description'])) {
     2058                            $description = $this->sanitize($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['description'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     2059                        } else {
     2060                            $description = $description_parent;
     2061                        }
     2062
     2063                        // HASHES
     2064                        if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['hash'])) {
     2065                            foreach ($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['hash'] as $hash) {
     2066                                $value = null;
     2067                                $algo = null;
     2068                                if (isset($hash['data'])) {
     2069                                    $value = $this->sanitize($hash['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     2070                                }
     2071                                if (isset($hash['attribs']['']['algo'])) {
     2072                                    $algo = $this->sanitize($hash['attribs']['']['algo'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     2073                                } else {
     2074                                    $algo = 'md5';
     2075                                }
     2076                                $hashes[] = $algo.':'.$value;
     2077                            }
     2078                            if (is_array($hashes)) {
     2079                                $hashes = array_values(array_unique($hashes));
     2080                            }
     2081                        } else {
     2082                            $hashes = $hashes_parent;
     2083                        }
     2084
     2085                        // KEYWORDS
     2086                        if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['keywords'])) {
     2087                            if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['keywords'][0]['data'])) {
     2088                                $temp = explode(',', $this->sanitize($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['keywords'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT));
     2089                                foreach ($temp as $word) {
     2090                                    $keywords[] = trim($word);
     2091                                }
     2092                                unset($temp);
     2093                            }
     2094                            if (is_array($keywords)) {
     2095                                $keywords = array_values(array_unique($keywords));
     2096                            }
     2097                        } else {
     2098                            $keywords = $keywords_parent;
     2099                        }
     2100
     2101                        // PLAYER
     2102                        if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['player'])) {
     2103                            if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'])) {
     2104                                $player = $this->sanitize($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI);
     2105                            }
     2106                        } else {
     2107                            $player = $player_parent;
     2108                        }
     2109
     2110                        // RATINGS
     2111                        if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['rating'])) {
     2112                            foreach ($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['rating'] as $rating) {
     2113                                $rating_scheme = null;
     2114                                $rating_value = null;
     2115                                if (isset($rating['attribs']['']['scheme'])) {
     2116                                    $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     2117                                } else {
     2118                                    $rating_scheme = 'urn:simple';
     2119                                }
     2120                                if (isset($rating['data'])) {
     2121                                    $rating_value = $this->sanitize($rating['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     2122                                }
     2123                                $ratings[] = $this->registry->create(Rating::class, [$rating_scheme, $rating_value]);
     2124                            }
     2125                            if (is_array($ratings)) {
     2126                                $ratings = array_values(array_unique($ratings));
     2127                            }
     2128                        } else {
     2129                            $ratings = $ratings_parent;
     2130                        }
     2131
     2132                        // RESTRICTIONS
     2133                        if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['restriction'])) {
     2134                            foreach ($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['restriction'] as $restriction) {
     2135                                $restriction_relationship = null;
     2136                                $restriction_type = null;
     2137                                $restriction_value = null;
     2138                                if (isset($restriction['attribs']['']['relationship'])) {
     2139                                    $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     2140                                }
     2141                                if (isset($restriction['attribs']['']['type'])) {
     2142                                    $restriction_type = $this->sanitize($restriction['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     2143                                }
     2144                                if (isset($restriction['data'])) {
     2145                                    $restriction_value = $this->sanitize($restriction['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     2146                                }
     2147                                $restrictions[] = $this->registry->create(Restriction::class, [$restriction_relationship, $restriction_type, $restriction_value]);
     2148                            }
     2149                            if (is_array($restrictions)) {
     2150                                $restrictions = array_values(array_unique($restrictions));
     2151                            }
     2152                        } else {
     2153                            $restrictions = $restrictions_parent;
     2154                        }
     2155
     2156                        // THUMBNAILS
     2157                        if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['thumbnail'])) {
     2158                            foreach ($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) {
     2159                                if (isset($thumbnail['attribs']['']['url'])) {
     2160                                    $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI);
     2161                                }
     2162                            }
     2163                            if (is_array($thumbnails)) {
     2164                                $thumbnails = array_values(array_unique($thumbnails));
     2165                            }
     2166                        } else {
     2167                            $thumbnails = $thumbnails_parent;
     2168                        }
     2169
     2170                        // TITLES
     2171                        if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['title'])) {
     2172                            $title = $this->sanitize($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['title'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     2173                        } else {
     2174                            $title = $title_parent;
     2175                        }
     2176
     2177                        $this->data['enclosures'][] = $this->registry->create(Enclosure::class, [$url, $type, $length, null, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width]);
     2178                    }
     2179                }
     2180            }
     2181
     2182            foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'link') as $link) {
     2183                if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure') {
     2184                    // Attributes
     2185                    $bitrate = null;
     2186                    $channels = null;
     2187                    $duration = null;
     2188                    $expression = null;
     2189                    $framerate = null;
     2190                    $height = null;
     2191                    $javascript = null;
     2192                    $lang = null;
     2193                    $length = null;
     2194                    $medium = null;
     2195                    $samplingrate = null;
     2196                    $type = null;
     2197                    $url = null;
     2198                    $width = null;
     2199
     2200                    $url = $this->sanitize($link['attribs']['']['href'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($link));
     2201                    if (isset($link['attribs']['']['type'])) {
     2202                        $type = $this->sanitize($link['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     2203                    }
     2204                    if (isset($link['attribs']['']['length'])) {
     2205                        $length = intval($link['attribs']['']['length']);
     2206                    }
     2207                    if (isset($link['attribs']['']['title'])) {
     2208                        $title = $this->sanitize($link['attribs']['']['title'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     2209                    } else {
     2210                        $title = $title_parent;
     2211                    }
     2212
     2213                    // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
     2214                    $this->data['enclosures'][] = $this->registry->create(Enclosure::class, [$url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title, $width]);
     2215                }
     2216            }
     2217
     2218            foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'link') as $link) {
     2219                if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure') {
     2220                    // Attributes
     2221                    $bitrate = null;
     2222                    $channels = null;
     2223                    $duration = null;
     2224                    $expression = null;
     2225                    $framerate = null;
     2226                    $height = null;
     2227                    $javascript = null;
     2228                    $lang = null;
     2229                    $length = null;
     2230                    $medium = null;
     2231                    $samplingrate = null;
     2232                    $type = null;
     2233                    $url = null;
     2234                    $width = null;
     2235
     2236                    $url = $this->sanitize($link['attribs']['']['href'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($link));
     2237                    if (isset($link['attribs']['']['type'])) {
     2238                        $type = $this->sanitize($link['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     2239                    }
     2240                    if (isset($link['attribs']['']['length'])) {
     2241                        $length = intval($link['attribs']['']['length']);
     2242                    }
     2243
     2244                    // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
     2245                    $this->data['enclosures'][] = $this->registry->create(Enclosure::class, [$url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width]);
     2246                }
     2247            }
     2248
     2249            foreach ($this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_20, 'enclosure') ?? [] as $enclosure) {
     2250                if (isset($enclosure['attribs']['']['url'])) {
     2251                    // Attributes
     2252                    $bitrate = null;
     2253                    $channels = null;
     2254                    $duration = null;
     2255                    $expression = null;
     2256                    $framerate = null;
     2257                    $height = null;
     2258                    $javascript = null;
     2259                    $lang = null;
     2260                    $length = null;
     2261                    $medium = null;
     2262                    $samplingrate = null;
     2263                    $type = null;
     2264                    $url = null;
     2265                    $width = null;
     2266
     2267                    $url = $this->sanitize($enclosure['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($enclosure));
     2268                    $url = $this->feed->sanitize->https_url($url);
     2269                    if (isset($enclosure['attribs']['']['type'])) {
     2270                        $type = $this->sanitize($enclosure['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
     2271                    }
     2272                    if (isset($enclosure['attribs']['']['length'])) {
     2273                        $length = intval($enclosure['attribs']['']['length']);
     2274                    }
     2275
     2276                    // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
     2277                    $this->data['enclosures'][] = $this->registry->create(Enclosure::class, [$url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width]);
     2278                }
     2279            }
     2280
     2281            if (sizeof($this->data['enclosures']) === 0 && ($url || $type || $length || $bitrate || $captions_parent || $categories_parent || $channels || $copyrights_parent || $credits_parent || $description_parent || $duration_parent || $expression || $framerate || $hashes_parent || $height || $keywords_parent || $lang || $medium || $player_parent || $ratings_parent || $restrictions_parent || $samplingrate || $thumbnails_parent || $title_parent || $width)) {
     2282                // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
     2283                $this->data['enclosures'][] = $this->registry->create(Enclosure::class, [$url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width]);
     2284            }
     2285
     2286            $this->data['enclosures'] = array_values(array_unique($this->data['enclosures']));
     2287        }
     2288        if (!empty($this->data['enclosures'])) {
     2289            return $this->data['enclosures'];
     2290        }
     2291
     2292        return null;
     2293    }
     2294
     2295    /**
     2296     * Get the latitude coordinates for the item
     2297     *
     2298     * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
     2299     *
     2300     * Uses `<geo:lat>` or `<georss:point>`
     2301     *
     2302     * @since 1.0
     2303     * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
     2304     * @link http://www.georss.org/ GeoRSS
     2305     * @return string|null
     2306     */
     2307    public function get_latitude()
     2308    {
     2309        if ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_W3C_BASIC_GEO, 'lat')) {
     2310            return (float) $return[0]['data'];
     2311        } elseif (($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) {
     2312            return (float) $match[1];
     2313        }
     2314
     2315        return null;
     2316    }
     2317
     2318    /**
     2319     * Get the longitude coordinates for the item
     2320     *
     2321     * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
     2322     *
     2323     * Uses `<geo:long>`, `<geo:lon>` or `<georss:point>`
     2324     *
     2325     * @since 1.0
     2326     * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
     2327     * @link http://www.georss.org/ GeoRSS
     2328     * @return string|null
     2329     */
     2330    public function get_longitude()
     2331    {
     2332        if ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_W3C_BASIC_GEO, 'long')) {
     2333            return (float) $return[0]['data'];
     2334        } elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_W3C_BASIC_GEO, 'lon')) {
     2335            return (float) $return[0]['data'];
     2336        } elseif (($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) {
     2337            return (float) $match[2];
     2338        }
     2339
     2340        return null;
     2341    }
     2342
     2343    /**
     2344     * Get the `<atom:source>` for the item
     2345     *
     2346     * @since 1.1
     2347     * @return \SimplePie\Source|null
     2348     */
     2349    public function get_source()
     2350    {
     2351        if ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'source')) {
     2352            return $this->registry->create(Source::class, [$this, $return[0]]);
     2353        }
     2354
     2355        return null;
     2356    }
    29672357}
     2358
     2359class_alias('SimplePie\Item', 'SimplePie_Item');
  • trunk/src/wp-includes/SimplePie/src/Locator.php

    r59140 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie;
     46
    4447/**
    4548 * Used for feed auto-discovery
    4649 *
    4750 *
    48  * This class can be overloaded with {@see SimplePie::set_locator_class()}
     51 * This class can be overloaded with {@see \SimplePie\SimplePie::set_locator_class()}
    4952 *
    5053 * @package SimplePie
    5154 */
    52 class SimplePie_Locator
     55class Locator implements RegistryAware
    5356{
    54     var $useragent;
    55     var $timeout;
    56     var $file;
    57     var $local = array();
    58     var $elsewhere = array();
    59     var $cached_entities = array();
    60     var $http_base;
    61     var $base;
    62     var $base_location = 0;
    63     var $checked_feeds = 0;
    64     var $max_checked_feeds = 10;
    65     var $force_fsockopen = false;
    66     var $curl_options = array();
    67     var $dom;
    68     protected $registry;
    69 
    70     public function __construct(SimplePie_File $file, $timeout = 10, $useragent = null, $max_checked_feeds = 10, $force_fsockopen = false, $curl_options = array())
    71     {
    72         $this->file = $file;
    73         $this->useragent = $useragent;
    74         $this->timeout = $timeout;
    75         $this->max_checked_feeds = $max_checked_feeds;
    76         $this->force_fsockopen = $force_fsockopen;
    77         $this->curl_options = $curl_options;
    78 
    79         if (class_exists('DOMDocument') && $this->file->body != '')
    80         {
    81             $this->dom = new DOMDocument();
    82 
    83             set_error_handler(array('SimplePie_Misc', 'silence_errors'));
    84             try
    85             {
    86                 $this->dom->loadHTML($this->file->body);
    87             }
    88             catch (Throwable $ex)
    89             {
    90                 $this->dom = null;
    91             }
    92             restore_error_handler();
    93         }
    94         else
    95         {
    96             $this->dom = null;
    97         }
    98     }
    99 
    100     public function set_registry(SimplePie_Registry $registry)
    101     {
    102         $this->registry = $registry;
    103     }
    104 
    105     public function find($type = SIMPLEPIE_LOCATOR_ALL, &$working = null)
    106     {
    107         if ($this->is_feed($this->file))
    108         {
    109             return $this->file;
    110         }
    111 
    112         if ($this->file->method & SIMPLEPIE_FILE_SOURCE_REMOTE)
    113         {
    114             $sniffer = $this->registry->create('Content_Type_Sniffer', array($this->file));
    115             if ($sniffer->get_type() !== 'text/html')
    116             {
    117                 return null;
    118             }
    119         }
    120 
    121         if ($type & ~SIMPLEPIE_LOCATOR_NONE)
    122         {
    123             $this->get_base();
    124         }
    125 
    126         if ($type & SIMPLEPIE_LOCATOR_AUTODISCOVERY && $working = $this->autodiscovery())
    127         {
    128             return $working[0];
    129         }
    130 
    131         if ($type & (SIMPLEPIE_LOCATOR_LOCAL_EXTENSION | SIMPLEPIE_LOCATOR_LOCAL_BODY | SIMPLEPIE_LOCATOR_REMOTE_EXTENSION | SIMPLEPIE_LOCATOR_REMOTE_BODY) && $this->get_links())
    132         {
    133             if ($type & SIMPLEPIE_LOCATOR_LOCAL_EXTENSION && $working = $this->extension($this->local))
    134             {
    135                 return $working[0];
    136             }
    137 
    138             if ($type & SIMPLEPIE_LOCATOR_LOCAL_BODY && $working = $this->body($this->local))
    139             {
    140                 return $working[0];
    141             }
    142 
    143             if ($type & SIMPLEPIE_LOCATOR_REMOTE_EXTENSION && $working = $this->extension($this->elsewhere))
    144             {
    145                 return $working[0];
    146             }
    147 
    148             if ($type & SIMPLEPIE_LOCATOR_REMOTE_BODY && $working = $this->body($this->elsewhere))
    149             {
    150                 return $working[0];
    151             }
    152         }
    153         return null;
    154     }
    155 
    156     public function is_feed($file, $check_html = false)
    157     {
    158         if ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE)
    159         {
    160             $sniffer = $this->registry->create('Content_Type_Sniffer', array($file));
    161             $sniffed = $sniffer->get_type();
    162             $mime_types = array('application/rss+xml', 'application/rdf+xml',
    163                                 'text/rdf', 'application/atom+xml', 'text/xml',
    164                                 'application/xml', 'application/x-rss+xml');
    165             if ($check_html)
    166             {
    167                 $mime_types[] = 'text/html';
    168             }
    169 
    170             return in_array($sniffed, $mime_types);
    171         }
    172         elseif ($file->method & SIMPLEPIE_FILE_SOURCE_LOCAL)
    173         {
    174             return true;
    175         }
    176         else
    177         {
    178             return false;
    179         }
    180     }
    181 
    182     public function get_base()
    183     {
    184         if ($this->dom === null)
    185         {
    186             throw new SimplePie_Exception('DOMDocument not found, unable to use locator');
    187         }
    188         $this->http_base = $this->file->url;
    189         $this->base = $this->http_base;
    190         $elements = $this->dom->getElementsByTagName('base');
    191         foreach ($elements as $element)
    192         {
    193             if ($element->hasAttribute('href'))
    194             {
    195                 $base = $this->registry->call('Misc', 'absolutize_url', array(trim($element->getAttribute('href')), $this->http_base));
    196                 if ($base === false)
    197                 {
    198                     continue;
    199                 }
    200                 $this->base = $base;
    201                 $this->base_location = method_exists($element, 'getLineNo') ? $element->getLineNo() : 0;
    202                 break;
    203             }
    204         }
    205     }
    206 
    207     public function autodiscovery()
    208     {
    209         $done = array();
    210         $feeds = array();
    211         $feeds = array_merge($feeds, $this->search_elements_by_tag('link', $done, $feeds));
    212         $feeds = array_merge($feeds, $this->search_elements_by_tag('a', $done, $feeds));
    213         $feeds = array_merge($feeds, $this->search_elements_by_tag('area', $done, $feeds));
    214 
    215         if (!empty($feeds))
    216         {
    217             return array_values($feeds);
    218         }
    219 
    220         return null;
    221     }
    222 
    223     protected function search_elements_by_tag($name, &$done, $feeds)
    224     {
    225         if ($this->dom === null)
    226         {
    227             throw new SimplePie_Exception('DOMDocument not found, unable to use locator');
    228         }
    229 
    230         $links = $this->dom->getElementsByTagName($name);
    231         foreach ($links as $link)
    232         {
    233             if ($this->checked_feeds === $this->max_checked_feeds)
    234             {
    235                 break;
    236             }
    237             if ($link->hasAttribute('href') && $link->hasAttribute('rel'))
    238             {
    239                 $rel = array_unique($this->registry->call('Misc', 'space_separated_tokens', array(strtolower($link->getAttribute('rel')))));
    240                 $line = method_exists($link, 'getLineNo') ? $link->getLineNo() : 1;
    241 
    242                 if ($this->base_location < $line)
    243                 {
    244                     $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->base));
    245                 }
    246                 else
    247                 {
    248                     $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->http_base));
    249                 }
    250                 if ($href === false)
    251                 {
    252                     continue;
    253                 }
    254 
    255                 if (!in_array($href, $done) && in_array('feed', $rel) || (in_array('alternate', $rel) && !in_array('stylesheet', $rel) && $link->hasAttribute('type') && in_array(strtolower($this->registry->call('Misc', 'parse_mime', array($link->getAttribute('type')))), array('text/html', 'application/rss+xml', 'application/atom+xml'))) && !isset($feeds[$href]))
    256                 {
    257                     $this->checked_feeds++;
    258                     $headers = array(
    259                         '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',
    260                     );
    261                     $feed = $this->registry->create('File', array($href, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options));
    262                     if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed, true))
    263                     {
    264                         $feeds[$href] = $feed;
    265                     }
    266                 }
    267                 $done[] = $href;
    268             }
    269         }
    270 
    271         return $feeds;
    272     }
    273 
    274     public function get_links()
    275     {
    276         if ($this->dom === null)
    277         {
    278             throw new SimplePie_Exception('DOMDocument not found, unable to use locator');
    279         }
    280 
    281         $links = $this->dom->getElementsByTagName('a');
    282         foreach ($links as $link)
    283         {
    284             if ($link->hasAttribute('href'))
    285             {
    286                 $href = trim($link->getAttribute('href'));
    287                 $parsed = $this->registry->call('Misc', 'parse_url', array($href));
    288                 if ($parsed['scheme'] === '' || preg_match('/^(https?|feed)?$/i', $parsed['scheme']))
    289                 {
    290                     if (method_exists($link, 'getLineNo') && $this->base_location < $link->getLineNo())
    291                     {
    292                         $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->base));
    293                     }
    294                     else
    295                     {
    296                         $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->http_base));
    297                     }
    298                     if ($href === false)
    299                     {
    300                         continue;
    301                     }
    302 
    303                     $current = $this->registry->call('Misc', 'parse_url', array($this->file->url));
    304 
    305                     if ($parsed['authority'] === '' || $parsed['authority'] === $current['authority'])
    306                     {
    307                         $this->local[] = $href;
    308                     }
    309                     else
    310                     {
    311                         $this->elsewhere[] = $href;
    312                     }
    313                 }
    314             }
    315         }
    316         $this->local = array_unique($this->local);
    317         $this->elsewhere = array_unique($this->elsewhere);
    318         if (!empty($this->local) || !empty($this->elsewhere))
    319         {
    320             return true;
    321         }
    322         return null;
    323     }
    324 
    325     public function get_rel_link($rel)
    326     {
    327         if ($this->dom === null)
    328         {
    329             throw new SimplePie_Exception('DOMDocument not found, unable to use '.
    330                                           'locator');
    331         }
    332         if (!class_exists('DOMXpath'))
    333         {
    334             throw new SimplePie_Exception('DOMXpath not found, unable to use '.
    335                                           'get_rel_link');
    336         }
    337 
    338         $xpath = new DOMXpath($this->dom);
    339         $query = '//a[@rel and @href] | //link[@rel and @href]';
    340         foreach ($xpath->query($query) as $link)
    341         {
    342             $href = trim($link->getAttribute('href'));
    343             $parsed = $this->registry->call('Misc', 'parse_url', array($href));
    344             if ($parsed['scheme'] === '' ||
    345                 preg_match('/^https?$/i', $parsed['scheme']))
    346             {
    347                 if (method_exists($link, 'getLineNo') &&
    348                     $this->base_location < $link->getLineNo())
    349                 {
    350                     $href =
    351                         $this->registry->call('Misc', 'absolutize_url',
    352                                               array(trim($link->getAttribute('href')),
    353                                                     $this->base));
    354                 }
    355                 else
    356                 {
    357                     $href =
    358                         $this->registry->call('Misc', 'absolutize_url',
    359                                               array(trim($link->getAttribute('href')),
    360                                                     $this->http_base));
    361                 }
    362                 if ($href === false)
    363                 {
    364                     return null;
    365                 }
    366                 $rel_values = explode(' ', strtolower($link->getAttribute('rel')));
    367                 if (in_array($rel, $rel_values))
    368                 {
    369                     return $href;
    370                 }
    371             }
    372         }
    373         return null;
    374     }
    375 
    376     public function extension(&$array)
    377     {
    378         foreach ($array as $key => $value)
    379         {
    380             if ($this->checked_feeds === $this->max_checked_feeds)
    381             {
    382                 break;
    383             }
    384             if (in_array(strtolower(strrchr($value, '.')), array('.rss', '.rdf', '.atom', '.xml')))
    385             {
    386                 $this->checked_feeds++;
    387 
    388                 $headers = array(
    389                     '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',
    390                 );
    391                 $feed = $this->registry->create('File', array($value, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options));
    392                 if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed))
    393                 {
    394                     return array($feed);
    395                 }
    396                 else
    397                 {
    398                     unset($array[$key]);
    399                 }
    400             }
    401         }
    402         return null;
    403     }
    404 
    405     public function body(&$array)
    406     {
    407         foreach ($array as $key => $value)
    408         {
    409             if ($this->checked_feeds === $this->max_checked_feeds)
    410             {
    411                 break;
    412             }
    413             if (preg_match('/(feed|rss|rdf|atom|xml)/i', $value))
    414             {
    415                 $this->checked_feeds++;
    416                 $headers = array(
    417                     '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',
    418                 );
    419                 $feed = $this->registry->create('File', array($value, $this->timeout, 5, null, $this->useragent, $this->force_fsockopen, $this->curl_options));
    420                 if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed))
    421                 {
    422                     return array($feed);
    423                 }
    424                 else
    425                 {
    426                     unset($array[$key]);
    427                 }
    428             }
    429         }
    430         return null;
    431     }
     57    public $useragent;
     58    public $timeout;
     59    public $file;
     60    public $local = [];
     61    public $elsewhere = [];
     62    public $cached_entities = [];
     63    public $http_base;
     64    public $base;
     65    public $base_location = 0;
     66    public $checked_feeds = 0;
     67    public $max_checked_feeds = 10;
     68    public $force_fsockopen = false;
     69    public $curl_options = [];
     70    public $dom;
     71    protected $registry;
     72
     73    public function __construct(\SimplePie\File $file, $timeout = 10, $useragent = null, $max_checked_feeds = 10, $force_fsockopen = false, $curl_options = [])
     74    {
     75        $this->file = $file;
     76        $this->useragent = $useragent;
     77        $this->timeout = $timeout;
     78        $this->max_checked_feeds = $max_checked_feeds;
     79        $this->force_fsockopen = $force_fsockopen;
     80        $this->curl_options = $curl_options;
     81
     82        if (class_exists('DOMDocument') && $this->file->body != '') {
     83            $this->dom = new \DOMDocument();
     84
     85            set_error_handler(['SimplePie\Misc', 'silence_errors']);
     86            try {
     87                $this->dom->loadHTML($this->file->body);
     88            } catch (\Throwable $ex) {
     89                $this->dom = null;
     90            }
     91            restore_error_handler();
     92        } else {
     93            $this->dom = null;
     94        }
     95    }
     96
     97    public function set_registry(\SimplePie\Registry $registry)/* : void */
     98    {
     99        $this->registry = $registry;
     100    }
     101
     102    public function find($type = \SimplePie\SimplePie::LOCATOR_ALL, &$working = null)
     103    {
     104        if ($this->is_feed($this->file)) {
     105            return $this->file;
     106        }
     107
     108        if ($this->file->method & \SimplePie\SimplePie::FILE_SOURCE_REMOTE) {
     109            $sniffer = $this->registry->create(Content\Type\Sniffer::class, [$this->file]);
     110            if ($sniffer->get_type() !== 'text/html') {
     111                return null;
     112            }
     113        }
     114
     115        if ($type & ~\SimplePie\SimplePie::LOCATOR_NONE) {
     116            $this->get_base();
     117        }
     118
     119        if ($type & \SimplePie\SimplePie::LOCATOR_AUTODISCOVERY && $working = $this->autodiscovery()) {
     120            return $working[0];
     121        }
     122
     123        if ($type & (\SimplePie\SimplePie::LOCATOR_LOCAL_EXTENSION | \SimplePie\SimplePie::LOCATOR_LOCAL_BODY | \SimplePie\SimplePie::LOCATOR_REMOTE_EXTENSION | \SimplePie\SimplePie::LOCATOR_REMOTE_BODY) && $this->get_links()) {
     124            if ($type & \SimplePie\SimplePie::LOCATOR_LOCAL_EXTENSION && $working = $this->extension($this->local)) {
     125                return $working[0];
     126            }
     127
     128            if ($type & \SimplePie\SimplePie::LOCATOR_LOCAL_BODY && $working = $this->body($this->local)) {
     129                return $working[0];
     130            }
     131
     132            if ($type & \SimplePie\SimplePie::LOCATOR_REMOTE_EXTENSION && $working = $this->extension($this->elsewhere)) {
     133                return $working[0];
     134            }
     135
     136            if ($type & \SimplePie\SimplePie::LOCATOR_REMOTE_BODY && $working = $this->body($this->elsewhere)) {
     137                return $working[0];
     138            }
     139        }
     140        return null;
     141    }
     142
     143    public function is_feed($file, $check_html = false)
     144    {
     145        if ($file->method & \SimplePie\SimplePie::FILE_SOURCE_REMOTE) {
     146            $sniffer = $this->registry->create(Content\Type\Sniffer::class, [$file]);
     147            $sniffed = $sniffer->get_type();
     148            $mime_types = ['application/rss+xml', 'application/rdf+xml',
     149                                'text/rdf', 'application/atom+xml', 'text/xml',
     150                                'application/xml', 'application/x-rss+xml'];
     151            if ($check_html) {
     152                $mime_types[] = 'text/html';
     153            }
     154
     155            return in_array($sniffed, $mime_types);
     156        } elseif ($file->method & \SimplePie\SimplePie::FILE_SOURCE_LOCAL) {
     157            return true;
     158        } else {
     159            return false;
     160        }
     161    }
     162
     163    public function get_base()
     164    {
     165        if ($this->dom === null) {
     166            throw new \SimplePie\Exception('DOMDocument not found, unable to use locator');
     167        }
     168        $this->http_base = $this->file->url;
     169        $this->base = $this->http_base;
     170        $elements = $this->dom->getElementsByTagName('base');
     171        foreach ($elements as $element) {
     172            if ($element->hasAttribute('href')) {
     173                $base = $this->registry->call(Misc::class, 'absolutize_url', [trim($element->getAttribute('href')), $this->http_base]);
     174                if ($base === false) {
     175                    continue;
     176                }
     177                $this->base = $base;
     178                $this->base_location = method_exists($element, 'getLineNo') ? $element->getLineNo() : 0;
     179                break;
     180            }
     181        }
     182    }
     183
     184    public function autodiscovery()
     185    {
     186        $done = [];
     187        $feeds = [];
     188        $feeds = array_merge($feeds, $this->search_elements_by_tag('link', $done, $feeds));
     189        $feeds = array_merge($feeds, $this->search_elements_by_tag('a', $done, $feeds));
     190        $feeds = array_merge($feeds, $this->search_elements_by_tag('area', $done, $feeds));
     191
     192        if (!empty($feeds)) {
     193            return array_values($feeds);
     194        }
     195
     196        return null;
     197    }
     198
     199    protected function search_elements_by_tag($name, &$done, $feeds)
     200    {
     201        if ($this->dom === null) {
     202            throw new \SimplePie\Exception('DOMDocument not found, unable to use locator');
     203        }
     204
     205        $links = $this->dom->getElementsByTagName($name);
     206        foreach ($links as $link) {
     207            if ($this->checked_feeds === $this->max_checked_feeds) {
     208                break;
     209            }
     210            if ($link->hasAttribute('href') && $link->hasAttribute('rel')) {
     211                $rel = array_unique($this->registry->call(Misc::class, 'space_separated_tokens', [strtolower($link->getAttribute('rel'))]));
     212                $line = method_exists($link, 'getLineNo') ? $link->getLineNo() : 1;
     213
     214                if ($this->base_location < $line) {
     215                    $href = $this->registry->call(Misc::class, 'absolutize_url', [trim($link->getAttribute('href')), $this->base]);
     216                } else {
     217                    $href = $this->registry->call(Misc::class, 'absolutize_url', [trim($link->getAttribute('href')), $this->http_base]);
     218                }
     219                if ($href === false) {
     220                    continue;
     221                }
     222
     223                if (!in_array($href, $done) && in_array('feed', $rel) || (in_array('alternate', $rel) && !in_array('stylesheet', $rel) && $link->hasAttribute('type') && in_array(strtolower($this->registry->call(Misc::class, 'parse_mime', [$link->getAttribute('type')])), ['text/html', 'application/rss+xml', 'application/atom+xml'])) && !isset($feeds[$href])) {
     224                    $this->checked_feeds++;
     225                    $headers = [
     226                        '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',
     227                    ];
     228                    $feed = $this->registry->create(File::class, [$href, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options]);
     229                    if ($feed->success && ($feed->method & \SimplePie\SimplePie::FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed, true)) {
     230                        $feeds[$href] = $feed;
     231                    }
     232                }
     233                $done[] = $href;
     234            }
     235        }
     236
     237        return $feeds;
     238    }
     239
     240    public function get_links()
     241    {
     242        if ($this->dom === null) {
     243            throw new \SimplePie\Exception('DOMDocument not found, unable to use locator');
     244        }
     245
     246        $links = $this->dom->getElementsByTagName('a');
     247        foreach ($links as $link) {
     248            if ($link->hasAttribute('href')) {
     249                $href = trim($link->getAttribute('href'));
     250                $parsed = $this->registry->call(Misc::class, 'parse_url', [$href]);
     251                if ($parsed['scheme'] === '' || preg_match('/^(https?|feed)?$/i', $parsed['scheme'])) {
     252                    if (method_exists($link, 'getLineNo') && $this->base_location < $link->getLineNo()) {
     253                        $href = $this->registry->call(Misc::class, 'absolutize_url', [trim($link->getAttribute('href')), $this->base]);
     254                    } else {
     255                        $href = $this->registry->call(Misc::class, 'absolutize_url', [trim($link->getAttribute('href')), $this->http_base]);
     256                    }
     257                    if ($href === false) {
     258                        continue;
     259                    }
     260
     261                    $current = $this->registry->call(Misc::class, 'parse_url', [$this->file->url]);
     262
     263                    if ($parsed['authority'] === '' || $parsed['authority'] === $current['authority']) {
     264                        $this->local[] = $href;
     265                    } else {
     266                        $this->elsewhere[] = $href;
     267                    }
     268                }
     269            }
     270        }
     271        $this->local = array_unique($this->local);
     272        $this->elsewhere = array_unique($this->elsewhere);
     273        if (!empty($this->local) || !empty($this->elsewhere)) {
     274            return true;
     275        }
     276        return null;
     277    }
     278
     279    public function get_rel_link($rel)
     280    {
     281        if ($this->dom === null) {
     282            throw new \SimplePie\Exception('DOMDocument not found, unable to use '.
     283                                          'locator');
     284        }
     285        if (!class_exists('DOMXpath')) {
     286            throw new \SimplePie\Exception('DOMXpath not found, unable to use '.
     287                                          'get_rel_link');
     288        }
     289
     290        $xpath = new \DOMXpath($this->dom);
     291        $query = '//a[@rel and @href] | //link[@rel and @href]';
     292        foreach ($xpath->query($query) as $link) {
     293            $href = trim($link->getAttribute('href'));
     294            $parsed = $this->registry->call(Misc::class, 'parse_url', [$href]);
     295            if ($parsed['scheme'] === '' ||
     296                preg_match('/^https?$/i', $parsed['scheme'])) {
     297                if (method_exists($link, 'getLineNo') &&
     298                    $this->base_location < $link->getLineNo()) {
     299                    $href = $this->registry->call(
     300                        Misc::class,
     301                        'absolutize_url',
     302                        [trim($link->getAttribute('href')), $this->base]
     303                    );
     304                } else {
     305                    $href = $this->registry->call(
     306                        Misc::class,
     307                        'absolutize_url',
     308                        [trim($link->getAttribute('href')), $this->http_base]
     309                    );
     310                }
     311                if ($href === false) {
     312                    return null;
     313                }
     314                $rel_values = explode(' ', strtolower($link->getAttribute('rel')));
     315                if (in_array($rel, $rel_values)) {
     316                    return $href;
     317                }
     318            }
     319        }
     320        return null;
     321    }
     322
     323    public function extension(&$array)
     324    {
     325        foreach ($array as $key => $value) {
     326            if ($this->checked_feeds === $this->max_checked_feeds) {
     327                break;
     328            }
     329            if (in_array(strtolower(strrchr($value, '.')), ['.rss', '.rdf', '.atom', '.xml'])) {
     330                $this->checked_feeds++;
     331
     332                $headers = [
     333                    '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',
     334                ];
     335                $feed = $this->registry->create(File::class, [$value, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options]);
     336                if ($feed->success && ($feed->method & \SimplePie\SimplePie::FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed)) {
     337                    return [$feed];
     338                } else {
     339                    unset($array[$key]);
     340                }
     341            }
     342        }
     343        return null;
     344    }
     345
     346    public function body(&$array)
     347    {
     348        foreach ($array as $key => $value) {
     349            if ($this->checked_feeds === $this->max_checked_feeds) {
     350                break;
     351            }
     352            if (preg_match('/(feed|rss|rdf|atom|xml)/i', $value)) {
     353                $this->checked_feeds++;
     354                $headers = [
     355                    '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',
     356                ];
     357                $feed = $this->registry->create(File::class, [$value, $this->timeout, 5, null, $this->useragent, $this->force_fsockopen, $this->curl_options]);
     358                if ($feed->success && ($feed->method & \SimplePie\SimplePie::FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed)) {
     359                    return [$feed];
     360                } else {
     361                    unset($array[$key]);
     362                }
     363            }
     364        }
     365        return null;
     366    }
    432367}
     368
     369class_alias('SimplePie\Locator', 'SimplePie_Locator', false);
  • trunk/src/wp-includes/SimplePie/src/Misc.php

    r59140 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie;
     46
     47use SimplePie\XML\Declaration\Parser;
     48
    4449/**
    45  * Miscellanous utilities
     50 * Miscellaneous utilities
    4651 *
    4752 * @package SimplePie
    4853 */
    49 class SimplePie_Misc
     54class Misc
    5055{
    51     public static function time_hms($seconds)
    52     {
    53         $time = '';
    54 
    55         $hours = floor($seconds / 3600);
    56         $remainder = $seconds % 3600;
    57         if ($hours > 0)
    58         {
    59             $time .= $hours.':';
    60         }
    61 
    62         $minutes = floor($remainder / 60);
    63         $seconds = $remainder % 60;
    64         if ($minutes < 10 && $hours > 0)
    65         {
    66             $minutes = '0' . $minutes;
    67         }
    68         if ($seconds < 10)
    69         {
    70             $seconds = '0' . $seconds;
    71         }
    72 
    73         $time .= $minutes.':';
    74         $time .= $seconds;
    75 
    76         return $time;
    77     }
    78 
    79     public static function absolutize_url($relative, $base)
    80     {
    81         $iri = SimplePie_IRI::absolutize(new SimplePie_IRI($base), $relative);
    82         if ($iri === false)
    83         {
    84             return false;
    85         }
    86         return $iri->get_uri();
    87     }
    88 
    89     /**
    90      * Get a HTML/XML element from a HTML string
    91      *
    92      * @deprecated Use DOMDocument instead (parsing HTML with regex is bad!)
    93      * @param string $realname Element name (including namespace prefix if applicable)
    94      * @param string $string HTML document
    95      * @return array
    96      */
    97     public static function get_element($realname, $string)
    98     {
    99         $return = array();
    100         $name = preg_quote($realname, '/');
    101         if (preg_match_all("/<($name)" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$name>|(\/)?>)/siU", $string, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE))
    102         {
    103             for ($i = 0, $total_matches = count($matches); $i < $total_matches; $i++)
    104             {
    105                 $return[$i]['tag'] = $realname;
    106                 $return[$i]['full'] = $matches[$i][0][0];
    107                 $return[$i]['offset'] = $matches[$i][0][1];
    108                 if (strlen($matches[$i][3][0]) <= 2)
    109                 {
    110                     $return[$i]['self_closing'] = true;
    111                 }
    112                 else
    113                 {
    114                     $return[$i]['self_closing'] = false;
    115                     $return[$i]['content'] = $matches[$i][4][0];
    116                 }
    117                 $return[$i]['attribs'] = array();
    118                 if (isset($matches[$i][2][0]) && preg_match_all('/[\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]*)?))?/', ' ' . $matches[$i][2][0] . ' ', $attribs, PREG_SET_ORDER))
    119                 {
    120                     for ($j = 0, $total_attribs = count($attribs); $j < $total_attribs; $j++)
    121                     {
    122                         if (count($attribs[$j]) === 2)
    123                         {
    124                             $attribs[$j][2] = $attribs[$j][1];
    125                         }
    126                         $return[$i]['attribs'][strtolower($attribs[$j][1])]['data'] = SimplePie_Misc::entities_decode(end($attribs[$j]));
    127                     }
    128                 }
    129             }
    130         }
    131         return $return;
    132     }
    133 
    134     public static function element_implode($element)
    135     {
    136         $full = "<$element[tag]";
    137         foreach ($element['attribs'] as $key => $value)
    138         {
    139             $key = strtolower($key);
    140             $full .= " $key=\"" . htmlspecialchars($value['data'], ENT_COMPAT, 'UTF-8') . '"';
    141         }
    142         if ($element['self_closing'])
    143         {
    144             $full .= ' />';
    145         }
    146         else
    147         {
    148             $full .= ">$element[content]</$element[tag]>";
    149         }
    150         return $full;
    151     }
    152 
    153     public static function error($message, $level, $file, $line)
    154     {
    155         if ((ini_get('error_reporting') & $level) > 0)
    156         {
    157             switch ($level)
    158             {
    159                 case E_USER_ERROR:
    160                     $note = 'PHP Error';
    161                     break;
    162                 case E_USER_WARNING:
    163                     $note = 'PHP Warning';
    164                     break;
    165                 case E_USER_NOTICE:
    166                     $note = 'PHP Notice';
    167                     break;
    168                 default:
    169                     $note = 'Unknown Error';
    170                     break;
    171             }
    172 
    173             $log_error = true;
    174             if (!function_exists('error_log'))
    175             {
    176                 $log_error = false;
    177             }
    178 
    179             $log_file = @ini_get('error_log');
    180             if (!empty($log_file) && ('syslog' !== $log_file) && !@is_writable($log_file))
    181             {
    182                 $log_error = false;
    183             }
    184 
    185             if ($log_error)
    186             {
    187                 @error_log("$note: $message in $file on line $line", 0);
    188             }
    189         }
    190 
    191         return $message;
    192     }
    193 
    194     public static function fix_protocol($url, $http = 1)
    195     {
    196         $url = SimplePie_Misc::normalize_url($url);
    197         $parsed = SimplePie_Misc::parse_url($url);
    198         if ($parsed['scheme'] !== '' && $parsed['scheme'] !== 'http' && $parsed['scheme'] !== 'https')
    199         {
    200             return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['authority'], $parsed['path'], $parsed['query'], $parsed['fragment']), $http);
    201         }
    202 
    203         if ($parsed['scheme'] === '' && $parsed['authority'] === '' && !file_exists($url))
    204         {
    205             return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['path'], '', $parsed['query'], $parsed['fragment']), $http);
    206         }
    207 
    208         if ($http === 2 && $parsed['scheme'] !== '')
    209         {
    210             return "feed:$url";
    211         }
    212         elseif ($http === 3 && strtolower($parsed['scheme']) === 'http')
    213         {
    214             return substr_replace($url, 'podcast', 0, 4);
    215         }
    216         elseif ($http === 4 && strtolower($parsed['scheme']) === 'http')
    217         {
    218             return substr_replace($url, 'itpc', 0, 4);
    219         }
    220 
    221         return $url;
    222     }
    223 
    224     public static function array_merge_recursive($array1, $array2)
    225     {
    226         foreach ($array2 as $key => $value)
    227         {
    228             if (is_array($value))
    229             {
    230                 $array1[$key] = SimplePie_Misc::array_merge_recursive($array1[$key], $value);
    231             }
    232             else
    233             {
    234                 $array1[$key] = $value;
    235             }
    236         }
    237 
    238         return $array1;
    239     }
    240 
    241     public static function parse_url($url)
    242     {
    243         $iri = new SimplePie_IRI($url);
    244         return array(
    245             'scheme' => (string) $iri->scheme,
    246             'authority' => (string) $iri->authority,
    247             'path' => (string) $iri->path,
    248             'query' => (string) $iri->query,
    249             'fragment' => (string) $iri->fragment
    250         );
    251     }
    252 
    253     public static function compress_parse_url($scheme = '', $authority = '', $path = '', $query = '', $fragment = '')
    254     {
    255         $iri = new SimplePie_IRI('');
    256         $iri->scheme = $scheme;
    257         $iri->authority = $authority;
    258         $iri->path = $path;
    259         $iri->query = $query;
    260         $iri->fragment = $fragment;
    261         return $iri->get_uri();
    262     }
    263 
    264     public static function normalize_url($url)
    265     {
    266         $iri = new SimplePie_IRI($url);
    267         return $iri->get_uri();
    268     }
    269 
    270     public static function percent_encoding_normalization($match)
    271     {
    272         $integer = hexdec($match[1]);
    273         if ($integer >= 0x41 && $integer <= 0x5A || $integer >= 0x61 && $integer <= 0x7A || $integer >= 0x30 && $integer <= 0x39 || $integer === 0x2D || $integer === 0x2E || $integer === 0x5F || $integer === 0x7E)
    274         {
    275             return chr($integer);
    276         }
    277 
    278         return strtoupper($match[0]);
    279     }
    280 
    281     /**
    282      * Converts a Windows-1252 encoded string to a UTF-8 encoded string
    283      *
    284      * @static
    285      * @param string $string Windows-1252 encoded string
    286      * @return string UTF-8 encoded string
    287      */
    288     public static function windows_1252_to_utf8($string)
    289     {
    290         static $convert_table = array("\x80" => "\xE2\x82\xAC", "\x81" => "\xEF\xBF\xBD", "\x82" => "\xE2\x80\x9A", "\x83" => "\xC6\x92", "\x84" => "\xE2\x80\x9E", "\x85" => "\xE2\x80\xA6", "\x86" => "\xE2\x80\xA0", "\x87" => "\xE2\x80\xA1", "\x88" => "\xCB\x86", "\x89" => "\xE2\x80\xB0", "\x8A" => "\xC5\xA0", "\x8B" => "\xE2\x80\xB9", "\x8C" => "\xC5\x92", "\x8D" => "\xEF\xBF\xBD", "\x8E" => "\xC5\xBD", "\x8F" => "\xEF\xBF\xBD", "\x90" => "\xEF\xBF\xBD", "\x91" => "\xE2\x80\x98", "\x92" => "\xE2\x80\x99", "\x93" => "\xE2\x80\x9C", "\x94" => "\xE2\x80\x9D", "\x95" => "\xE2\x80\xA2", "\x96" => "\xE2\x80\x93", "\x97" => "\xE2\x80\x94", "\x98" => "\xCB\x9C", "\x99" => "\xE2\x84\xA2", "\x9A" => "\xC5\xA1", "\x9B" => "\xE2\x80\xBA", "\x9C" => "\xC5\x93", "\x9D" => "\xEF\xBF\xBD", "\x9E" => "\xC5\xBE", "\x9F" => "\xC5\xB8", "\xA0" => "\xC2\xA0", "\xA1" => "\xC2\xA1", "\xA2" => "\xC2\xA2", "\xA3" => "\xC2\xA3", "\xA4" => "\xC2\xA4", "\xA5" => "\xC2\xA5", "\xA6" => "\xC2\xA6", "\xA7" => "\xC2\xA7", "\xA8" => "\xC2\xA8", "\xA9" => "\xC2\xA9", "\xAA" => "\xC2\xAA", "\xAB" => "\xC2\xAB", "\xAC" => "\xC2\xAC", "\xAD" => "\xC2\xAD", "\xAE" => "\xC2\xAE", "\xAF" => "\xC2\xAF", "\xB0" => "\xC2\xB0", "\xB1" => "\xC2\xB1", "\xB2" => "\xC2\xB2", "\xB3" => "\xC2\xB3", "\xB4" => "\xC2\xB4", "\xB5" => "\xC2\xB5", "\xB6" => "\xC2\xB6", "\xB7" => "\xC2\xB7", "\xB8" => "\xC2\xB8", "\xB9" => "\xC2\xB9", "\xBA" => "\xC2\xBA", "\xBB" => "\xC2\xBB", "\xBC" => "\xC2\xBC", "\xBD" => "\xC2\xBD", "\xBE" => "\xC2\xBE", "\xBF" => "\xC2\xBF", "\xC0" => "\xC3\x80", "\xC1" => "\xC3\x81", "\xC2" => "\xC3\x82", "\xC3" => "\xC3\x83", "\xC4" => "\xC3\x84", "\xC5" => "\xC3\x85", "\xC6" => "\xC3\x86", "\xC7" => "\xC3\x87", "\xC8" => "\xC3\x88", "\xC9" => "\xC3\x89", "\xCA" => "\xC3\x8A", "\xCB" => "\xC3\x8B", "\xCC" => "\xC3\x8C", "\xCD" => "\xC3\x8D", "\xCE" => "\xC3\x8E", "\xCF" => "\xC3\x8F", "\xD0" => "\xC3\x90", "\xD1" => "\xC3\x91", "\xD2" => "\xC3\x92", "\xD3" => "\xC3\x93", "\xD4" => "\xC3\x94", "\xD5" => "\xC3\x95", "\xD6" => "\xC3\x96", "\xD7" => "\xC3\x97", "\xD8" => "\xC3\x98", "\xD9" => "\xC3\x99", "\xDA" => "\xC3\x9A", "\xDB" => "\xC3\x9B", "\xDC" => "\xC3\x9C", "\xDD" => "\xC3\x9D", "\xDE" => "\xC3\x9E", "\xDF" => "\xC3\x9F", "\xE0" => "\xC3\xA0", "\xE1" => "\xC3\xA1", "\xE2" => "\xC3\xA2", "\xE3" => "\xC3\xA3", "\xE4" => "\xC3\xA4", "\xE5" => "\xC3\xA5", "\xE6" => "\xC3\xA6", "\xE7" => "\xC3\xA7", "\xE8" => "\xC3\xA8", "\xE9" => "\xC3\xA9", "\xEA" => "\xC3\xAA", "\xEB" => "\xC3\xAB", "\xEC" => "\xC3\xAC", "\xED" => "\xC3\xAD", "\xEE" => "\xC3\xAE", "\xEF" => "\xC3\xAF", "\xF0" => "\xC3\xB0", "\xF1" => "\xC3\xB1", "\xF2" => "\xC3\xB2", "\xF3" => "\xC3\xB3", "\xF4" => "\xC3\xB4", "\xF5" => "\xC3\xB5", "\xF6" => "\xC3\xB6", "\xF7" => "\xC3\xB7", "\xF8" => "\xC3\xB8", "\xF9" => "\xC3\xB9", "\xFA" => "\xC3\xBA", "\xFB" => "\xC3\xBB", "\xFC" => "\xC3\xBC", "\xFD" => "\xC3\xBD", "\xFE" => "\xC3\xBE", "\xFF" => "\xC3\xBF");
    291 
    292         return strtr($string, $convert_table);
    293     }
    294 
    295     /**
    296      * Change a string from one encoding to another
    297      *
    298      * @param string $data Raw data in $input encoding
    299      * @param string $input Encoding of $data
    300      * @param string $output Encoding you want
    301      * @return string|boolean False if we can't convert it
    302      */
    303     public static function change_encoding($data, $input, $output)
    304     {
    305         $input = SimplePie_Misc::encoding($input);
    306         $output = SimplePie_Misc::encoding($output);
    307 
    308         // We fail to fail on non US-ASCII bytes
    309         if ($input === 'US-ASCII')
    310         {
    311             static $non_ascii_octects = '';
    312             if (!$non_ascii_octects)
    313             {
    314                 for ($i = 0x80; $i <= 0xFF; $i++)
    315                 {
    316                     $non_ascii_octects .= chr($i);
    317                 }
    318             }
    319             $data = substr($data, 0, strcspn($data, $non_ascii_octects));
    320         }
    321 
    322         // This is first, as behaviour of this is completely predictable
    323         if ($input === 'windows-1252' && $output === 'UTF-8')
    324         {
    325             return SimplePie_Misc::windows_1252_to_utf8($data);
    326         }
    327         // This is second, as behaviour of this varies only with PHP version (the middle part of this expression checks the encoding is supported).
    328         elseif (function_exists('mb_convert_encoding') && ($return = SimplePie_Misc::change_encoding_mbstring($data, $input, $output)))
    329         {
    330             return $return;
    331         }
    332         // This is third, as behaviour of this varies with OS userland and PHP version
    333         elseif (function_exists('iconv') && ($return = SimplePie_Misc::change_encoding_iconv($data, $input, $output)))
    334         {
    335             return $return;
    336         }
    337         // This is last, as behaviour of this varies with OS userland and PHP version
    338         elseif (class_exists('\UConverter') && ($return = SimplePie_Misc::change_encoding_uconverter($data, $input, $output)))
    339         {
    340             return $return;
    341         }
    342 
    343         // If we can't do anything, just fail
    344         return false;
    345     }
    346 
    347     protected static function change_encoding_mbstring($data, $input, $output)
    348     {
    349         if ($input === 'windows-949')
    350         {
    351             $input = 'EUC-KR';
    352         }
    353         if ($output === 'windows-949')
    354         {
    355             $output = 'EUC-KR';
    356         }
    357         if ($input === 'Windows-31J')
    358         {
    359             $input = 'SJIS';
    360         }
    361         if ($output === 'Windows-31J')
    362         {
    363             $output = 'SJIS';
    364         }
    365 
    366         // Check that the encoding is supported
    367         if (!in_array($input, mb_list_encodings()))
    368         {
    369             return false;
    370         }
    371 
    372         if (@mb_convert_encoding("\x80", 'UTF-16BE', $input) === "\x00\x80")
    373         {
    374             return false;
    375         }
    376 
    377         // Let's do some conversion
    378         if ($return = @mb_convert_encoding($data, $output, $input))
    379         {
    380             return $return;
    381         }
    382 
    383         return false;
    384     }
    385 
    386     protected static function change_encoding_iconv($data, $input, $output)
    387     {
    388         return @iconv($input, $output, $data);
    389     }
    390 
    391     /**
    392      * @param string $data
    393      * @param string $input
    394      * @param string $output
    395      * @return string|false
    396      */
    397     protected static function change_encoding_uconverter($data, $input, $output)
    398     {
    399         return @\UConverter::transcode($data, $output, $input);
    400     }
    401 
    402     /**
    403      * Normalize an encoding name
    404      *
    405      * This is automatically generated by create.php
    406      *
    407      * To generate it, run `php create.php` on the command line, and copy the
    408      * output to replace this function.
    409      *
    410      * @param string $charset Character set to standardise
    411      * @return string Standardised name
    412      */
    413     public static function encoding($charset)
    414     {
    415         // Normalization from UTS #22
    416         switch (strtolower(preg_replace('/(?:[^a-zA-Z0-9]+|([^0-9])0+)/', '\1', $charset)))
    417         {
    418             case 'adobestandardencoding':
    419             case 'csadobestandardencoding':
    420                 return 'Adobe-Standard-Encoding';
    421 
    422             case 'adobesymbolencoding':
    423             case 'cshppsmath':
    424                 return 'Adobe-Symbol-Encoding';
    425 
    426             case 'ami1251':
    427             case 'amiga1251':
    428                 return 'Amiga-1251';
    429 
    430             case 'ansix31101983':
    431             case 'csat5001983':
    432             case 'csiso99naplps':
    433             case 'isoir99':
    434             case 'naplps':
    435                 return 'ANSI_X3.110-1983';
    436 
    437             case 'arabic7':
    438             case 'asmo449':
    439             case 'csiso89asmo449':
    440             case 'iso9036':
    441             case 'isoir89':
    442                 return 'ASMO_449';
    443 
    444             case 'big5':
    445             case 'csbig5':
    446                 return 'Big5';
    447 
    448             case 'big5hkscs':
    449                 return 'Big5-HKSCS';
    450 
    451             case 'bocu1':
    452             case 'csbocu1':
    453                 return 'BOCU-1';
    454 
    455             case 'brf':
    456             case 'csbrf':
    457                 return 'BRF';
    458 
    459             case 'bs4730':
    460             case 'csiso4unitedkingdom':
    461             case 'gb':
    462             case 'iso646gb':
    463             case 'isoir4':
    464             case 'uk':
    465                 return 'BS_4730';
    466 
    467             case 'bsviewdata':
    468             case 'csiso47bsviewdata':
    469             case 'isoir47':
    470                 return 'BS_viewdata';
    471 
    472             case 'cesu8':
    473             case 'cscesu8':
    474                 return 'CESU-8';
    475 
    476             case 'ca':
    477             case 'csa71':
    478             case 'csaz243419851':
    479             case 'csiso121canadian1':
    480             case 'iso646ca':
    481             case 'isoir121':
    482                 return 'CSA_Z243.4-1985-1';
    483 
    484             case 'csa72':
    485             case 'csaz243419852':
    486             case 'csiso122canadian2':
    487             case 'iso646ca2':
    488             case 'isoir122':
    489                 return 'CSA_Z243.4-1985-2';
    490 
    491             case 'csaz24341985gr':
    492             case 'csiso123csaz24341985gr':
    493             case 'isoir123':
    494                 return 'CSA_Z243.4-1985-gr';
    495 
    496             case 'csiso139csn369103':
    497             case 'csn369103':
    498             case 'isoir139':
    499                 return 'CSN_369103';
    500 
    501             case 'csdecmcs':
    502             case 'dec':
    503             case 'decmcs':
    504                 return 'DEC-MCS';
    505 
    506             case 'csiso21german':
    507             case 'de':
    508             case 'din66003':
    509             case 'iso646de':
    510             case 'isoir21':
    511                 return 'DIN_66003';
    512 
    513             case 'csdkus':
    514             case 'dkus':
    515                 return 'dk-us';
    516 
    517             case 'csiso646danish':
    518             case 'dk':
    519             case 'ds2089':
    520             case 'iso646dk':
    521                 return 'DS_2089';
    522 
    523             case 'csibmebcdicatde':
    524             case 'ebcdicatde':
    525                 return 'EBCDIC-AT-DE';
    526 
    527             case 'csebcdicatdea':
    528             case 'ebcdicatdea':
    529                 return 'EBCDIC-AT-DE-A';
    530 
    531             case 'csebcdiccafr':
    532             case 'ebcdiccafr':
    533                 return 'EBCDIC-CA-FR';
    534 
    535             case 'csebcdicdkno':
    536             case 'ebcdicdkno':
    537                 return 'EBCDIC-DK-NO';
    538 
    539             case 'csebcdicdknoa':
    540             case 'ebcdicdknoa':
    541                 return 'EBCDIC-DK-NO-A';
    542 
    543             case 'csebcdices':
    544             case 'ebcdices':
    545                 return 'EBCDIC-ES';
    546 
    547             case 'csebcdicesa':
    548             case 'ebcdicesa':
    549                 return 'EBCDIC-ES-A';
    550 
    551             case 'csebcdicess':
    552             case 'ebcdicess':
    553                 return 'EBCDIC-ES-S';
    554 
    555             case 'csebcdicfise':
    556             case 'ebcdicfise':
    557                 return 'EBCDIC-FI-SE';
    558 
    559             case 'csebcdicfisea':
    560             case 'ebcdicfisea':
    561                 return 'EBCDIC-FI-SE-A';
    562 
    563             case 'csebcdicfr':
    564             case 'ebcdicfr':
    565                 return 'EBCDIC-FR';
    566 
    567             case 'csebcdicit':
    568             case 'ebcdicit':
    569                 return 'EBCDIC-IT';
    570 
    571             case 'csebcdicpt':
    572             case 'ebcdicpt':
    573                 return 'EBCDIC-PT';
    574 
    575             case 'csebcdicuk':
    576             case 'ebcdicuk':
    577                 return 'EBCDIC-UK';
    578 
    579             case 'csebcdicus':
    580             case 'ebcdicus':
    581                 return 'EBCDIC-US';
    582 
    583             case 'csiso111ecmacyrillic':
    584             case 'ecmacyrillic':
    585             case 'isoir111':
    586             case 'koi8e':
    587                 return 'ECMA-cyrillic';
    588 
    589             case 'csiso17spanish':
    590             case 'es':
    591             case 'iso646es':
    592             case 'isoir17':
    593                 return 'ES';
    594 
    595             case 'csiso85spanish2':
    596             case 'es2':
    597             case 'iso646es2':
    598             case 'isoir85':
    599                 return 'ES2';
    600 
    601             case 'cseucpkdfmtjapanese':
    602             case 'eucjp':
    603             case 'extendedunixcodepackedformatforjapanese':
    604                 return 'EUC-JP';
    605 
    606             case 'cseucfixwidjapanese':
    607             case 'extendedunixcodefixedwidthforjapanese':
    608                 return 'Extended_UNIX_Code_Fixed_Width_for_Japanese';
    609 
    610             case 'gb18030':
    611                 return 'GB18030';
    612 
    613             case 'chinese':
    614             case 'cp936':
    615             case 'csgb2312':
    616             case 'csiso58gb231280':
    617             case 'gb2312':
    618             case 'gb231280':
    619             case 'gbk':
    620             case 'isoir58':
    621             case 'ms936':
    622             case 'windows936':
    623                 return 'GBK';
    624 
    625             case 'cn':
    626             case 'csiso57gb1988':
    627             case 'gb198880':
    628             case 'iso646cn':
    629             case 'isoir57':
    630                 return 'GB_1988-80';
    631 
    632             case 'csiso153gost1976874':
    633             case 'gost1976874':
    634             case 'isoir153':
    635             case 'stsev35888':
    636                 return 'GOST_19768-74';
    637 
    638             case 'csiso150':
    639             case 'csiso150greekccitt':
    640             case 'greekccitt':
    641             case 'isoir150':
    642                 return 'greek-ccitt';
    643 
    644             case 'csiso88greek7':
    645             case 'greek7':
    646             case 'isoir88':
    647                 return 'greek7';
    648 
    649             case 'csiso18greek7old':
    650             case 'greek7old':
    651             case 'isoir18':
    652                 return 'greek7-old';
    653 
    654             case 'cshpdesktop':
    655             case 'hpdesktop':
    656                 return 'HP-DeskTop';
    657 
    658             case 'cshplegal':
    659             case 'hplegal':
    660                 return 'HP-Legal';
    661 
    662             case 'cshpmath8':
    663             case 'hpmath8':
    664                 return 'HP-Math8';
    665 
    666             case 'cshppifont':
    667             case 'hppifont':
    668                 return 'HP-Pi-font';
    669 
    670             case 'cshproman8':
    671             case 'hproman8':
    672             case 'r8':
    673             case 'roman8':
    674                 return 'hp-roman8';
    675 
    676             case 'hzgb2312':
    677                 return 'HZ-GB-2312';
    678 
    679             case 'csibmsymbols':
    680             case 'ibmsymbols':
    681                 return 'IBM-Symbols';
    682 
    683             case 'csibmthai':
    684             case 'ibmthai':
    685                 return 'IBM-Thai';
    686 
    687             case 'cp37':
    688             case 'csibm37':
    689             case 'ebcdiccpca':
    690             case 'ebcdiccpnl':
    691             case 'ebcdiccpus':
    692             case 'ebcdiccpwt':
    693             case 'ibm37':
    694                 return 'IBM037';
    695 
    696             case 'cp38':
    697             case 'csibm38':
    698             case 'ebcdicint':
    699             case 'ibm38':
    700                 return 'IBM038';
    701 
    702             case 'cp273':
    703             case 'csibm273':
    704             case 'ibm273':
    705                 return 'IBM273';
    706 
    707             case 'cp274':
    708             case 'csibm274':
    709             case 'ebcdicbe':
    710             case 'ibm274':
    711                 return 'IBM274';
    712 
    713             case 'cp275':
    714             case 'csibm275':
    715             case 'ebcdicbr':
    716             case 'ibm275':
    717                 return 'IBM275';
    718 
    719             case 'csibm277':
    720             case 'ebcdiccpdk':
    721             case 'ebcdiccpno':
    722             case 'ibm277':
    723                 return 'IBM277';
    724 
    725             case 'cp278':
    726             case 'csibm278':
    727             case 'ebcdiccpfi':
    728             case 'ebcdiccpse':
    729             case 'ibm278':
    730                 return 'IBM278';
    731 
    732             case 'cp280':
    733             case 'csibm280':
    734             case 'ebcdiccpit':
    735             case 'ibm280':
    736                 return 'IBM280';
    737 
    738             case 'cp281':
    739             case 'csibm281':
    740             case 'ebcdicjpe':
    741             case 'ibm281':
    742                 return 'IBM281';
    743 
    744             case 'cp284':
    745             case 'csibm284':
    746             case 'ebcdiccpes':
    747             case 'ibm284':
    748                 return 'IBM284';
    749 
    750             case 'cp285':
    751             case 'csibm285':
    752             case 'ebcdiccpgb':
    753             case 'ibm285':
    754                 return 'IBM285';
    755 
    756             case 'cp290':
    757             case 'csibm290':
    758             case 'ebcdicjpkana':
    759             case 'ibm290':
    760                 return 'IBM290';
    761 
    762             case 'cp297':
    763             case 'csibm297':
    764             case 'ebcdiccpfr':
    765             case 'ibm297':
    766                 return 'IBM297';
    767 
    768             case 'cp420':
    769             case 'csibm420':
    770             case 'ebcdiccpar1':
    771             case 'ibm420':
    772                 return 'IBM420';
    773 
    774             case 'cp423':
    775             case 'csibm423':
    776             case 'ebcdiccpgr':
    777             case 'ibm423':
    778                 return 'IBM423';
    779 
    780             case 'cp424':
    781             case 'csibm424':
    782             case 'ebcdiccphe':
    783             case 'ibm424':
    784                 return 'IBM424';
    785 
    786             case '437':
    787             case 'cp437':
    788             case 'cspc8codepage437':
    789             case 'ibm437':
    790                 return 'IBM437';
    791 
    792             case 'cp500':
    793             case 'csibm500':
    794             case 'ebcdiccpbe':
    795             case 'ebcdiccpch':
    796             case 'ibm500':
    797                 return 'IBM500';
    798 
    799             case 'cp775':
    800             case 'cspc775baltic':
    801             case 'ibm775':
    802                 return 'IBM775';
    803 
    804             case '850':
    805             case 'cp850':
    806             case 'cspc850multilingual':
    807             case 'ibm850':
    808                 return 'IBM850';
    809 
    810             case '851':
    811             case 'cp851':
    812             case 'csibm851':
    813             case 'ibm851':
    814                 return 'IBM851';
    815 
    816             case '852':
    817             case 'cp852':
    818             case 'cspcp852':
    819             case 'ibm852':
    820                 return 'IBM852';
    821 
    822             case '855':
    823             case 'cp855':
    824             case 'csibm855':
    825             case 'ibm855':
    826                 return 'IBM855';
    827 
    828             case '857':
    829             case 'cp857':
    830             case 'csibm857':
    831             case 'ibm857':
    832                 return 'IBM857';
    833 
    834             case 'ccsid858':
    835             case 'cp858':
    836             case 'ibm858':
    837             case 'pcmultilingual850euro':
    838                 return 'IBM00858';
    839 
    840             case '860':
    841             case 'cp860':
    842             case 'csibm860':
    843             case 'ibm860':
    844                 return 'IBM860';
    845 
    846             case '861':
    847             case 'cp861':
    848             case 'cpis':
    849             case 'csibm861':
    850             case 'ibm861':
    851                 return 'IBM861';
    852 
    853             case '862':
    854             case 'cp862':
    855             case 'cspc862latinhebrew':
    856             case 'ibm862':
    857                 return 'IBM862';
    858 
    859             case '863':
    860             case 'cp863':
    861             case 'csibm863':
    862             case 'ibm863':
    863                 return 'IBM863';
    864 
    865             case 'cp864':
    866             case 'csibm864':
    867             case 'ibm864':
    868                 return 'IBM864';
    869 
    870             case '865':
    871             case 'cp865':
    872             case 'csibm865':
    873             case 'ibm865':
    874                 return 'IBM865';
    875 
    876             case '866':
    877             case 'cp866':
    878             case 'csibm866':
    879             case 'ibm866':
    880                 return 'IBM866';
    881 
    882             case 'cp868':
    883             case 'cpar':
    884             case 'csibm868':
    885             case 'ibm868':
    886                 return 'IBM868';
    887 
    888             case '869':
    889             case 'cp869':
    890             case 'cpgr':
    891             case 'csibm869':
    892             case 'ibm869':
    893                 return 'IBM869';
    894 
    895             case 'cp870':
    896             case 'csibm870':
    897             case 'ebcdiccproece':
    898             case 'ebcdiccpyu':
    899             case 'ibm870':
    900                 return 'IBM870';
    901 
    902             case 'cp871':
    903             case 'csibm871':
    904             case 'ebcdiccpis':
    905             case 'ibm871':
    906                 return 'IBM871';
    907 
    908             case 'cp880':
    909             case 'csibm880':
    910             case 'ebcdiccyrillic':
    911             case 'ibm880':
    912                 return 'IBM880';
    913 
    914             case 'cp891':
    915             case 'csibm891':
    916             case 'ibm891':
    917                 return 'IBM891';
    918 
    919             case 'cp903':
    920             case 'csibm903':
    921             case 'ibm903':
    922                 return 'IBM903';
    923 
    924             case '904':
    925             case 'cp904':
    926             case 'csibbm904':
    927             case 'ibm904':
    928                 return 'IBM904';
    929 
    930             case 'cp905':
    931             case 'csibm905':
    932             case 'ebcdiccptr':
    933             case 'ibm905':
    934                 return 'IBM905';
    935 
    936             case 'cp918':
    937             case 'csibm918':
    938             case 'ebcdiccpar2':
    939             case 'ibm918':
    940                 return 'IBM918';
    941 
    942             case 'ccsid924':
    943             case 'cp924':
    944             case 'ebcdiclatin9euro':
    945             case 'ibm924':
    946                 return 'IBM00924';
    947 
    948             case 'cp1026':
    949             case 'csibm1026':
    950             case 'ibm1026':
    951                 return 'IBM1026';
    952 
    953             case 'ibm1047':
    954                 return 'IBM1047';
    955 
    956             case 'ccsid1140':
    957             case 'cp1140':
    958             case 'ebcdicus37euro':
    959             case 'ibm1140':
    960                 return 'IBM01140';
    961 
    962             case 'ccsid1141':
    963             case 'cp1141':
    964             case 'ebcdicde273euro':
    965             case 'ibm1141':
    966                 return 'IBM01141';
    967 
    968             case 'ccsid1142':
    969             case 'cp1142':
    970             case 'ebcdicdk277euro':
    971             case 'ebcdicno277euro':
    972             case 'ibm1142':
    973                 return 'IBM01142';
    974 
    975             case 'ccsid1143':
    976             case 'cp1143':
    977             case 'ebcdicfi278euro':
    978             case 'ebcdicse278euro':
    979             case 'ibm1143':
    980                 return 'IBM01143';
    981 
    982             case 'ccsid1144':
    983             case 'cp1144':
    984             case 'ebcdicit280euro':
    985             case 'ibm1144':
    986                 return 'IBM01144';
    987 
    988             case 'ccsid1145':
    989             case 'cp1145':
    990             case 'ebcdices284euro':
    991             case 'ibm1145':
    992                 return 'IBM01145';
    993 
    994             case 'ccsid1146':
    995             case 'cp1146':
    996             case 'ebcdicgb285euro':
    997             case 'ibm1146':
    998                 return 'IBM01146';
    999 
    1000             case 'ccsid1147':
    1001             case 'cp1147':
    1002             case 'ebcdicfr297euro':
    1003             case 'ibm1147':
    1004                 return 'IBM01147';
    1005 
    1006             case 'ccsid1148':
    1007             case 'cp1148':
    1008             case 'ebcdicinternational500euro':
    1009             case 'ibm1148':
    1010                 return 'IBM01148';
    1011 
    1012             case 'ccsid1149':
    1013             case 'cp1149':
    1014             case 'ebcdicis871euro':
    1015             case 'ibm1149':
    1016                 return 'IBM01149';
    1017 
    1018             case 'csiso143iecp271':
    1019             case 'iecp271':
    1020             case 'isoir143':
    1021                 return 'IEC_P27-1';
    1022 
    1023             case 'csiso49inis':
    1024             case 'inis':
    1025             case 'isoir49':
    1026                 return 'INIS';
    1027 
    1028             case 'csiso50inis8':
    1029             case 'inis8':
    1030             case 'isoir50':
    1031                 return 'INIS-8';
    1032 
    1033             case 'csiso51iniscyrillic':
    1034             case 'iniscyrillic':
    1035             case 'isoir51':
    1036                 return 'INIS-cyrillic';
    1037 
    1038             case 'csinvariant':
    1039             case 'invariant':
    1040                 return 'INVARIANT';
    1041 
    1042             case 'iso2022cn':
    1043                 return 'ISO-2022-CN';
    1044 
    1045             case 'iso2022cnext':
    1046                 return 'ISO-2022-CN-EXT';
    1047 
    1048             case 'csiso2022jp':
    1049             case 'iso2022jp':
    1050                 return 'ISO-2022-JP';
    1051 
    1052             case 'csiso2022jp2':
    1053             case 'iso2022jp2':
    1054                 return 'ISO-2022-JP-2';
    1055 
    1056             case 'csiso2022kr':
    1057             case 'iso2022kr':
    1058                 return 'ISO-2022-KR';
    1059 
    1060             case 'cswindows30latin1':
    1061             case 'iso88591windows30latin1':
    1062                 return 'ISO-8859-1-Windows-3.0-Latin-1';
    1063 
    1064             case 'cswindows31latin1':
    1065             case 'iso88591windows31latin1':
    1066                 return 'ISO-8859-1-Windows-3.1-Latin-1';
    1067 
    1068             case 'csisolatin2':
    1069             case 'iso88592':
    1070             case 'iso885921987':
    1071             case 'isoir101':
    1072             case 'l2':
    1073             case 'latin2':
    1074                 return 'ISO-8859-2';
    1075 
    1076             case 'cswindows31latin2':
    1077             case 'iso88592windowslatin2':
    1078                 return 'ISO-8859-2-Windows-Latin-2';
    1079 
    1080             case 'csisolatin3':
    1081             case 'iso88593':
    1082             case 'iso885931988':
    1083             case 'isoir109':
    1084             case 'l3':
    1085             case 'latin3':
    1086                 return 'ISO-8859-3';
    1087 
    1088             case 'csisolatin4':
    1089             case 'iso88594':
    1090             case 'iso885941988':
    1091             case 'isoir110':
    1092             case 'l4':
    1093             case 'latin4':
    1094                 return 'ISO-8859-4';
    1095 
    1096             case 'csisolatincyrillic':
    1097             case 'cyrillic':
    1098             case 'iso88595':
    1099             case 'iso885951988':
    1100             case 'isoir144':
    1101                 return 'ISO-8859-5';
    1102 
    1103             case 'arabic':
    1104             case 'asmo708':
    1105             case 'csisolatinarabic':
    1106             case 'ecma114':
    1107             case 'iso88596':
    1108             case 'iso885961987':
    1109             case 'isoir127':
    1110                 return 'ISO-8859-6';
    1111 
    1112             case 'csiso88596e':
    1113             case 'iso88596e':
    1114                 return 'ISO-8859-6-E';
    1115 
    1116             case 'csiso88596i':
    1117             case 'iso88596i':
    1118                 return 'ISO-8859-6-I';
    1119 
    1120             case 'csisolatingreek':
    1121             case 'ecma118':
    1122             case 'elot928':
    1123             case 'greek':
    1124             case 'greek8':
    1125             case 'iso88597':
    1126             case 'iso885971987':
    1127             case 'isoir126':
    1128                 return 'ISO-8859-7';
    1129 
    1130             case 'csisolatinhebrew':
    1131             case 'hebrew':
    1132             case 'iso88598':
    1133             case 'iso885981988':
    1134             case 'isoir138':
    1135                 return 'ISO-8859-8';
    1136 
    1137             case 'csiso88598e':
    1138             case 'iso88598e':
    1139                 return 'ISO-8859-8-E';
    1140 
    1141             case 'csiso88598i':
    1142             case 'iso88598i':
    1143                 return 'ISO-8859-8-I';
    1144 
    1145             case 'cswindows31latin5':
    1146             case 'iso88599windowslatin5':
    1147                 return 'ISO-8859-9-Windows-Latin-5';
    1148 
    1149             case 'csisolatin6':
    1150             case 'iso885910':
    1151             case 'iso8859101992':
    1152             case 'isoir157':
    1153             case 'l6':
    1154             case 'latin6':
    1155                 return 'ISO-8859-10';
    1156 
    1157             case 'iso885913':
    1158                 return 'ISO-8859-13';
    1159 
    1160             case 'iso885914':
    1161             case 'iso8859141998':
    1162             case 'isoceltic':
    1163             case 'isoir199':
    1164             case 'l8':
    1165             case 'latin8':
    1166                 return 'ISO-8859-14';
    1167 
    1168             case 'iso885915':
    1169             case 'latin9':
    1170                 return 'ISO-8859-15';
    1171 
    1172             case 'iso885916':
    1173             case 'iso8859162001':
    1174             case 'isoir226':
    1175             case 'l10':
    1176             case 'latin10':
    1177                 return 'ISO-8859-16';
    1178 
    1179             case 'iso10646j1':
    1180                 return 'ISO-10646-J-1';
    1181 
    1182             case 'csunicode':
    1183             case 'iso10646ucs2':
    1184                 return 'ISO-10646-UCS-2';
    1185 
    1186             case 'csucs4':
    1187             case 'iso10646ucs4':
    1188                 return 'ISO-10646-UCS-4';
    1189 
    1190             case 'csunicodeascii':
    1191             case 'iso10646ucsbasic':
    1192                 return 'ISO-10646-UCS-Basic';
    1193 
    1194             case 'csunicodelatin1':
    1195             case 'iso10646':
    1196             case 'iso10646unicodelatin1':
    1197                 return 'ISO-10646-Unicode-Latin1';
    1198 
    1199             case 'csiso10646utf1':
    1200             case 'iso10646utf1':
    1201                 return 'ISO-10646-UTF-1';
    1202 
    1203             case 'csiso115481':
    1204             case 'iso115481':
    1205             case 'isotr115481':
    1206                 return 'ISO-11548-1';
    1207 
    1208             case 'csiso90':
    1209             case 'isoir90':
    1210                 return 'iso-ir-90';
    1211 
    1212             case 'csunicodeibm1261':
    1213             case 'isounicodeibm1261':
    1214                 return 'ISO-Unicode-IBM-1261';
    1215 
    1216             case 'csunicodeibm1264':
    1217             case 'isounicodeibm1264':
    1218                 return 'ISO-Unicode-IBM-1264';
    1219 
    1220             case 'csunicodeibm1265':
    1221             case 'isounicodeibm1265':
    1222                 return 'ISO-Unicode-IBM-1265';
    1223 
    1224             case 'csunicodeibm1268':
    1225             case 'isounicodeibm1268':
    1226                 return 'ISO-Unicode-IBM-1268';
    1227 
    1228             case 'csunicodeibm1276':
    1229             case 'isounicodeibm1276':
    1230                 return 'ISO-Unicode-IBM-1276';
    1231 
    1232             case 'csiso646basic1983':
    1233             case 'iso646basic1983':
    1234             case 'ref':
    1235                 return 'ISO_646.basic:1983';
    1236 
    1237             case 'csiso2intlrefversion':
    1238             case 'irv':
    1239             case 'iso646irv1983':
    1240             case 'isoir2':
    1241                 return 'ISO_646.irv:1983';
    1242 
    1243             case 'csiso2033':
    1244             case 'e13b':
    1245             case 'iso20331983':
    1246             case 'isoir98':
    1247                 return 'ISO_2033-1983';
    1248 
    1249             case 'csiso5427cyrillic':
    1250             case 'iso5427':
    1251             case 'isoir37':
    1252                 return 'ISO_5427';
    1253 
    1254             case 'iso5427cyrillic1981':
    1255             case 'iso54271981':
    1256             case 'isoir54':
    1257                 return 'ISO_5427:1981';
    1258 
    1259             case 'csiso5428greek':
    1260             case 'iso54281980':
    1261             case 'isoir55':
    1262                 return 'ISO_5428:1980';
    1263 
    1264             case 'csiso6937add':
    1265             case 'iso6937225':
    1266             case 'isoir152':
    1267                 return 'ISO_6937-2-25';
    1268 
    1269             case 'csisotextcomm':
    1270             case 'iso69372add':
    1271             case 'isoir142':
    1272                 return 'ISO_6937-2-add';
    1273 
    1274             case 'csiso8859supp':
    1275             case 'iso8859supp':
    1276             case 'isoir154':
    1277             case 'latin125':
    1278                 return 'ISO_8859-supp';
    1279 
    1280             case 'csiso10367box':
    1281             case 'iso10367box':
    1282             case 'isoir155':
    1283                 return 'ISO_10367-box';
    1284 
    1285             case 'csiso15italian':
    1286             case 'iso646it':
    1287             case 'isoir15':
    1288             case 'it':
    1289                 return 'IT';
    1290 
    1291             case 'csiso13jisc6220jp':
    1292             case 'isoir13':
    1293             case 'jisc62201969':
    1294             case 'jisc62201969jp':
    1295             case 'katakana':
    1296             case 'x2017':
    1297                 return 'JIS_C6220-1969-jp';
    1298 
    1299             case 'csiso14jisc6220ro':
    1300             case 'iso646jp':
    1301             case 'isoir14':
    1302             case 'jisc62201969ro':
    1303             case 'jp':
    1304                 return 'JIS_C6220-1969-ro';
    1305 
    1306             case 'csiso42jisc62261978':
    1307             case 'isoir42':
    1308             case 'jisc62261978':
    1309                 return 'JIS_C6226-1978';
    1310 
    1311             case 'csiso87jisx208':
    1312             case 'isoir87':
    1313             case 'jisc62261983':
    1314             case 'jisx2081983':
    1315             case 'x208':
    1316                 return 'JIS_C6226-1983';
    1317 
    1318             case 'csiso91jisc62291984a':
    1319             case 'isoir91':
    1320             case 'jisc62291984a':
    1321             case 'jpocra':
    1322                 return 'JIS_C6229-1984-a';
    1323 
    1324             case 'csiso92jisc62991984b':
    1325             case 'iso646jpocrb':
    1326             case 'isoir92':
    1327             case 'jisc62291984b':
    1328             case 'jpocrb':
    1329                 return 'JIS_C6229-1984-b';
    1330 
    1331             case 'csiso93jis62291984badd':
    1332             case 'isoir93':
    1333             case 'jisc62291984badd':
    1334             case 'jpocrbadd':
    1335                 return 'JIS_C6229-1984-b-add';
    1336 
    1337             case 'csiso94jis62291984hand':
    1338             case 'isoir94':
    1339             case 'jisc62291984hand':
    1340             case 'jpocrhand':
    1341                 return 'JIS_C6229-1984-hand';
    1342 
    1343             case 'csiso95jis62291984handadd':
    1344             case 'isoir95':
    1345             case 'jisc62291984handadd':
    1346             case 'jpocrhandadd':
    1347                 return 'JIS_C6229-1984-hand-add';
    1348 
    1349             case 'csiso96jisc62291984kana':
    1350             case 'isoir96':
    1351             case 'jisc62291984kana':
    1352                 return 'JIS_C6229-1984-kana';
    1353 
    1354             case 'csjisencoding':
    1355             case 'jisencoding':
    1356                 return 'JIS_Encoding';
    1357 
    1358             case 'cshalfwidthkatakana':
    1359             case 'jisx201':
    1360             case 'x201':
    1361                 return 'JIS_X0201';
    1362 
    1363             case 'csiso159jisx2121990':
    1364             case 'isoir159':
    1365             case 'jisx2121990':
    1366             case 'x212':
    1367                 return 'JIS_X0212-1990';
    1368 
    1369             case 'csiso141jusib1002':
    1370             case 'iso646yu':
    1371             case 'isoir141':
    1372             case 'js':
    1373             case 'jusib1002':
    1374             case 'yu':
    1375                 return 'JUS_I.B1.002';
    1376 
    1377             case 'csiso147macedonian':
    1378             case 'isoir147':
    1379             case 'jusib1003mac':
    1380             case 'macedonian':
    1381                 return 'JUS_I.B1.003-mac';
    1382 
    1383             case 'csiso146serbian':
    1384             case 'isoir146':
    1385             case 'jusib1003serb':
    1386             case 'serbian':
    1387                 return 'JUS_I.B1.003-serb';
    1388 
    1389             case 'koi7switched':
    1390                 return 'KOI7-switched';
    1391 
    1392             case 'cskoi8r':
    1393             case 'koi8r':
    1394                 return 'KOI8-R';
    1395 
    1396             case 'koi8u':
    1397                 return 'KOI8-U';
    1398 
    1399             case 'csksc5636':
    1400             case 'iso646kr':
    1401             case 'ksc5636':
    1402                 return 'KSC5636';
    1403 
    1404             case 'cskz1048':
    1405             case 'kz1048':
    1406             case 'rk1048':
    1407             case 'strk10482002':
    1408                 return 'KZ-1048';
    1409 
    1410             case 'csiso19latingreek':
    1411             case 'isoir19':
    1412             case 'latingreek':
    1413                 return 'latin-greek';
    1414 
    1415             case 'csiso27latingreek1':
    1416             case 'isoir27':
    1417             case 'latingreek1':
    1418                 return 'Latin-greek-1';
    1419 
    1420             case 'csiso158lap':
    1421             case 'isoir158':
    1422             case 'lap':
    1423             case 'latinlap':
    1424                 return 'latin-lap';
    1425 
    1426             case 'csmacintosh':
    1427             case 'mac':
    1428             case 'macintosh':
    1429                 return 'macintosh';
    1430 
    1431             case 'csmicrosoftpublishing':
    1432             case 'microsoftpublishing':
    1433                 return 'Microsoft-Publishing';
    1434 
    1435             case 'csmnem':
    1436             case 'mnem':
    1437                 return 'MNEM';
    1438 
    1439             case 'csmnemonic':
    1440             case 'mnemonic':
    1441                 return 'MNEMONIC';
    1442 
    1443             case 'csiso86hungarian':
    1444             case 'hu':
    1445             case 'iso646hu':
    1446             case 'isoir86':
    1447             case 'msz77953':
    1448                 return 'MSZ_7795.3';
    1449 
    1450             case 'csnatsdano':
    1451             case 'isoir91':
    1452             case 'natsdano':
    1453                 return 'NATS-DANO';
    1454 
    1455             case 'csnatsdanoadd':
    1456             case 'isoir92':
    1457             case 'natsdanoadd':
    1458                 return 'NATS-DANO-ADD';
    1459 
    1460             case 'csnatssefi':
    1461             case 'isoir81':
    1462             case 'natssefi':
    1463                 return 'NATS-SEFI';
    1464 
    1465             case 'csnatssefiadd':
    1466             case 'isoir82':
    1467             case 'natssefiadd':
    1468                 return 'NATS-SEFI-ADD';
    1469 
    1470             case 'csiso151cuba':
    1471             case 'cuba':
    1472             case 'iso646cu':
    1473             case 'isoir151':
    1474             case 'ncnc1081':
    1475                 return 'NC_NC00-10:81';
    1476 
    1477             case 'csiso69french':
    1478             case 'fr':
    1479             case 'iso646fr':
    1480             case 'isoir69':
    1481             case 'nfz62010':
    1482                 return 'NF_Z_62-010';
    1483 
    1484             case 'csiso25french':
    1485             case 'iso646fr1':
    1486             case 'isoir25':
    1487             case 'nfz620101973':
    1488                 return 'NF_Z_62-010_(1973)';
    1489 
    1490             case 'csiso60danishnorwegian':
    1491             case 'csiso60norwegian1':
    1492             case 'iso646no':
    1493             case 'isoir60':
    1494             case 'no':
    1495             case 'ns45511':
    1496                 return 'NS_4551-1';
    1497 
    1498             case 'csiso61norwegian2':
    1499             case 'iso646no2':
    1500             case 'isoir61':
    1501             case 'no2':
    1502             case 'ns45512':
    1503                 return 'NS_4551-2';
    1504 
    1505             case 'osdebcdicdf3irv':
    1506                 return 'OSD_EBCDIC_DF03_IRV';
    1507 
    1508             case 'osdebcdicdf41':
    1509                 return 'OSD_EBCDIC_DF04_1';
    1510 
    1511             case 'osdebcdicdf415':
    1512                 return 'OSD_EBCDIC_DF04_15';
    1513 
    1514             case 'cspc8danishnorwegian':
    1515             case 'pc8danishnorwegian':
    1516                 return 'PC8-Danish-Norwegian';
    1517 
    1518             case 'cspc8turkish':
    1519             case 'pc8turkish':
    1520                 return 'PC8-Turkish';
    1521 
    1522             case 'csiso16portuguese':
    1523             case 'iso646pt':
    1524             case 'isoir16':
    1525             case 'pt':
    1526                 return 'PT';
    1527 
    1528             case 'csiso84portuguese2':
    1529             case 'iso646pt2':
    1530             case 'isoir84':
    1531             case 'pt2':
    1532                 return 'PT2';
    1533 
    1534             case 'cp154':
    1535             case 'csptcp154':
    1536             case 'cyrillicasian':
    1537             case 'pt154':
    1538             case 'ptcp154':
    1539                 return 'PTCP154';
    1540 
    1541             case 'scsu':
    1542                 return 'SCSU';
    1543 
    1544             case 'csiso10swedish':
    1545             case 'fi':
    1546             case 'iso646fi':
    1547             case 'iso646se':
    1548             case 'isoir10':
    1549             case 'se':
    1550             case 'sen850200b':
    1551                 return 'SEN_850200_B';
    1552 
    1553             case 'csiso11swedishfornames':
    1554             case 'iso646se2':
    1555             case 'isoir11':
    1556             case 'se2':
    1557             case 'sen850200c':
    1558                 return 'SEN_850200_C';
    1559 
    1560             case 'csiso102t617bit':
    1561             case 'isoir102':
    1562             case 't617bit':
    1563                 return 'T.61-7bit';
    1564 
    1565             case 'csiso103t618bit':
    1566             case 'isoir103':
    1567             case 't61':
    1568             case 't618bit':
    1569                 return 'T.61-8bit';
    1570 
    1571             case 'csiso128t101g2':
    1572             case 'isoir128':
    1573             case 't101g2':
    1574                 return 'T.101-G2';
    1575 
    1576             case 'cstscii':
    1577             case 'tscii':
    1578                 return 'TSCII';
    1579 
    1580             case 'csunicode11':
    1581             case 'unicode11':
    1582                 return 'UNICODE-1-1';
    1583 
    1584             case 'csunicode11utf7':
    1585             case 'unicode11utf7':
    1586                 return 'UNICODE-1-1-UTF-7';
    1587 
    1588             case 'csunknown8bit':
    1589             case 'unknown8bit':
    1590                 return 'UNKNOWN-8BIT';
    1591 
    1592             case 'ansix341968':
    1593             case 'ansix341986':
    1594             case 'ascii':
    1595             case 'cp367':
    1596             case 'csascii':
    1597             case 'ibm367':
    1598             case 'iso646irv1991':
    1599             case 'iso646us':
    1600             case 'isoir6':
    1601             case 'us':
    1602             case 'usascii':
    1603                 return 'US-ASCII';
    1604 
    1605             case 'csusdk':
    1606             case 'usdk':
    1607                 return 'us-dk';
    1608 
    1609             case 'utf7':
    1610                 return 'UTF-7';
    1611 
    1612             case 'utf8':
    1613                 return 'UTF-8';
    1614 
    1615             case 'utf16':
    1616                 return 'UTF-16';
    1617 
    1618             case 'utf16be':
    1619                 return 'UTF-16BE';
    1620 
    1621             case 'utf16le':
    1622                 return 'UTF-16LE';
    1623 
    1624             case 'utf32':
    1625                 return 'UTF-32';
    1626 
    1627             case 'utf32be':
    1628                 return 'UTF-32BE';
    1629 
    1630             case 'utf32le':
    1631                 return 'UTF-32LE';
    1632 
    1633             case 'csventurainternational':
    1634             case 'venturainternational':
    1635                 return 'Ventura-International';
    1636 
    1637             case 'csventuramath':
    1638             case 'venturamath':
    1639                 return 'Ventura-Math';
    1640 
    1641             case 'csventuraus':
    1642             case 'venturaus':
    1643                 return 'Ventura-US';
    1644 
    1645             case 'csiso70videotexsupp1':
    1646             case 'isoir70':
    1647             case 'videotexsuppl':
    1648                 return 'videotex-suppl';
    1649 
    1650             case 'csviqr':
    1651             case 'viqr':
    1652                 return 'VIQR';
    1653 
    1654             case 'csviscii':
    1655             case 'viscii':
    1656                 return 'VISCII';
    1657 
    1658             case 'csshiftjis':
    1659             case 'cswindows31j':
    1660             case 'mskanji':
    1661             case 'shiftjis':
    1662             case 'windows31j':
    1663                 return 'Windows-31J';
    1664 
    1665             case 'iso885911':
    1666             case 'tis620':
    1667                 return 'windows-874';
    1668 
    1669             case 'cseuckr':
    1670             case 'csksc56011987':
    1671             case 'euckr':
    1672             case 'isoir149':
    1673             case 'korean':
    1674             case 'ksc5601':
    1675             case 'ksc56011987':
    1676             case 'ksc56011989':
    1677             case 'windows949':
    1678                 return 'windows-949';
    1679 
    1680             case 'windows1250':
    1681                 return 'windows-1250';
    1682 
    1683             case 'windows1251':
    1684                 return 'windows-1251';
    1685 
    1686             case 'cp819':
    1687             case 'csisolatin1':
    1688             case 'ibm819':
    1689             case 'iso88591':
    1690             case 'iso885911987':
    1691             case 'isoir100':
    1692             case 'l1':
    1693             case 'latin1':
    1694             case 'windows1252':
    1695                 return 'windows-1252';
    1696 
    1697             case 'windows1253':
    1698                 return 'windows-1253';
    1699 
    1700             case 'csisolatin5':
    1701             case 'iso88599':
    1702             case 'iso885991989':
    1703             case 'isoir148':
    1704             case 'l5':
    1705             case 'latin5':
    1706             case 'windows1254':
    1707                 return 'windows-1254';
    1708 
    1709             case 'windows1255':
    1710                 return 'windows-1255';
    1711 
    1712             case 'windows1256':
    1713                 return 'windows-1256';
    1714 
    1715             case 'windows1257':
    1716                 return 'windows-1257';
    1717 
    1718             case 'windows1258':
    1719                 return 'windows-1258';
    1720 
    1721             default:
    1722                 return $charset;
    1723         }
    1724     }
    1725 
    1726     public static function get_curl_version()
    1727     {
    1728         if (is_array($curl = curl_version()))
    1729         {
    1730             $curl = $curl['version'];
    1731         }
    1732         elseif (substr($curl, 0, 5) === 'curl/')
    1733         {
    1734             $curl = substr($curl, 5, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 5));
    1735         }
    1736         elseif (substr($curl, 0, 8) === 'libcurl/')
    1737         {
    1738             $curl = substr($curl, 8, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 8));
    1739         }
    1740         else
    1741         {
    1742             $curl = 0;
    1743         }
    1744         return $curl;
    1745     }
    1746 
    1747     /**
    1748      * Strip HTML comments
    1749      *
    1750      * @param string $data Data to strip comments from
    1751      * @return string Comment stripped string
    1752      */
    1753     public static function strip_comments($data)
    1754     {
    1755         $output = '';
    1756         while (($start = strpos($data, '<!--')) !== false)
    1757         {
    1758             $output .= substr($data, 0, $start);
    1759             if (($end = strpos($data, '-->', $start)) !== false)
    1760             {
    1761                 $data = substr_replace($data, '', 0, $end + 3);
    1762             }
    1763             else
    1764             {
    1765                 $data = '';
    1766             }
    1767         }
    1768         return $output . $data;
    1769     }
    1770 
    1771     public static function parse_date($dt)
    1772     {
    1773         $parser = SimplePie_Parse_Date::get();
    1774         return $parser->parse($dt);
    1775     }
    1776 
    1777     /**
    1778      * Decode HTML entities
    1779      *
    1780      * @deprecated Use DOMDocument instead
    1781      * @param string $data Input data
    1782      * @return string Output data
    1783      */
    1784     public static function entities_decode($data)
    1785     {
    1786         $decoder = new SimplePie_Decode_HTML_Entities($data);
    1787         return $decoder->parse();
    1788     }
    1789 
    1790     /**
    1791      * Remove RFC822 comments
    1792      *
    1793      * @param string $data Data to strip comments from
    1794      * @return string Comment stripped string
    1795      */
    1796     public static function uncomment_rfc822($string)
    1797     {
    1798         $string = (string) $string;
    1799         $position = 0;
    1800         $length = strlen($string);
    1801         $depth = 0;
    1802 
    1803         $output = '';
    1804 
    1805         while ($position < $length && ($pos = strpos($string, '(', $position)) !== false)
    1806         {
    1807             $output .= substr($string, $position, $pos - $position);
    1808             $position = $pos + 1;
    1809             if ($string[$pos - 1] !== '\\')
    1810             {
    1811                 $depth++;
    1812                 while ($depth && $position < $length)
    1813                 {
    1814                     $position += strcspn($string, '()', $position);
    1815                     if ($string[$position - 1] === '\\')
    1816                     {
    1817                         $position++;
    1818                         continue;
    1819                     }
    1820                     elseif (isset($string[$position]))
    1821                     {
    1822                         switch ($string[$position])
    1823                         {
    1824                             case '(':
    1825                                 $depth++;
    1826                                 break;
    1827 
    1828                             case ')':
    1829                                 $depth--;
    1830                                 break;
    1831                         }
    1832                         $position++;
    1833                     }
    1834                     else
    1835                     {
    1836                         break;
    1837                     }
    1838                 }
    1839             }
    1840             else
    1841             {
    1842                 $output .= '(';
    1843             }
    1844         }
    1845         $output .= substr($string, $position);
    1846 
    1847         return $output;
    1848     }
    1849 
    1850     public static function parse_mime($mime)
    1851     {
    1852         if (($pos = strpos($mime, ';')) === false)
    1853         {
    1854             return trim($mime);
    1855         }
    1856 
    1857         return trim(substr($mime, 0, $pos));
    1858     }
    1859 
    1860     public static function atom_03_construct_type($attribs)
    1861     {
    1862         if (isset($attribs['']['mode']) && strtolower(trim($attribs['']['mode']) === 'base64'))
    1863         {
    1864             $mode = SIMPLEPIE_CONSTRUCT_BASE64;
    1865         }
    1866         else
    1867         {
    1868             $mode = SIMPLEPIE_CONSTRUCT_NONE;
    1869         }
    1870         if (isset($attribs['']['type']))
    1871         {
    1872             switch (strtolower(trim($attribs['']['type'])))
    1873             {
    1874                 case 'text':
    1875                 case 'text/plain':
    1876                     return SIMPLEPIE_CONSTRUCT_TEXT | $mode;
    1877 
    1878                 case 'html':
    1879                 case 'text/html':
    1880                     return SIMPLEPIE_CONSTRUCT_HTML | $mode;
    1881 
    1882                 case 'xhtml':
    1883                 case 'application/xhtml+xml':
    1884                     return SIMPLEPIE_CONSTRUCT_XHTML | $mode;
    1885 
    1886                 default:
    1887                     return SIMPLEPIE_CONSTRUCT_NONE | $mode;
    1888             }
    1889         }
    1890 
    1891         return SIMPLEPIE_CONSTRUCT_TEXT | $mode;
    1892     }
    1893 
    1894     public static function atom_10_construct_type($attribs)
    1895     {
    1896         if (isset($attribs['']['type']))
    1897         {
    1898             switch (strtolower(trim($attribs['']['type'])))
    1899             {
    1900                 case 'text':
    1901                     return SIMPLEPIE_CONSTRUCT_TEXT;
    1902 
    1903                 case 'html':
    1904                     return SIMPLEPIE_CONSTRUCT_HTML;
    1905 
    1906                 case 'xhtml':
    1907                     return SIMPLEPIE_CONSTRUCT_XHTML;
    1908 
    1909                 default:
    1910                     return SIMPLEPIE_CONSTRUCT_NONE;
    1911             }
    1912         }
    1913         return SIMPLEPIE_CONSTRUCT_TEXT;
    1914     }
    1915 
    1916     public static function atom_10_content_construct_type($attribs)
    1917     {
    1918         if (isset($attribs['']['type']))
    1919         {
    1920             $type = strtolower(trim($attribs['']['type']));
    1921             switch ($type)
    1922             {
    1923                 case 'text':
    1924                     return SIMPLEPIE_CONSTRUCT_TEXT;
    1925 
    1926                 case 'html':
    1927                     return SIMPLEPIE_CONSTRUCT_HTML;
    1928 
    1929                 case 'xhtml':
    1930                     return SIMPLEPIE_CONSTRUCT_XHTML;
    1931             }
    1932             if (in_array(substr($type, -4), array('+xml', '/xml')) || substr($type, 0, 5) === 'text/')
    1933             {
    1934                 return SIMPLEPIE_CONSTRUCT_NONE;
    1935             }
    1936             else
    1937             {
    1938                 return SIMPLEPIE_CONSTRUCT_BASE64;
    1939             }
    1940         }
    1941 
    1942         return SIMPLEPIE_CONSTRUCT_TEXT;
    1943     }
    1944 
    1945     public static function is_isegment_nz_nc($string)
    1946     {
    1947         return (bool) preg_match('/^([A-Za-z0-9\-._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!$&\'()*+,;=@]|(%[0-9ABCDEF]{2}))+$/u', $string);
    1948     }
    1949 
    1950     public static function space_separated_tokens($string)
    1951     {
    1952         $space_characters = "\x20\x09\x0A\x0B\x0C\x0D";
    1953         $string_length = strlen($string);
    1954 
    1955         $position = strspn($string, $space_characters);
    1956         $tokens = array();
    1957 
    1958         while ($position < $string_length)
    1959         {
    1960             $len = strcspn($string, $space_characters, $position);
    1961             $tokens[] = substr($string, $position, $len);
    1962             $position += $len;
    1963             $position += strspn($string, $space_characters, $position);
    1964         }
    1965 
    1966         return $tokens;
    1967     }
    1968 
    1969     /**
    1970      * Converts a unicode codepoint to a UTF-8 character
    1971      *
    1972      * @static
    1973      * @param int $codepoint Unicode codepoint
    1974      * @return string UTF-8 character
    1975      */
    1976     public static function codepoint_to_utf8($codepoint)
    1977     {
    1978         $codepoint = (int) $codepoint;
    1979         if ($codepoint < 0)
    1980         {
    1981             return false;
    1982         }
    1983         else if ($codepoint <= 0x7f)
    1984         {
    1985             return chr($codepoint);
    1986         }
    1987         else if ($codepoint <= 0x7ff)
    1988         {
    1989             return chr(0xc0 | ($codepoint >> 6)) . chr(0x80 | ($codepoint & 0x3f));
    1990         }
    1991         else if ($codepoint <= 0xffff)
    1992         {
    1993             return chr(0xe0 | ($codepoint >> 12)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f));
    1994         }
    1995         else if ($codepoint <= 0x10ffff)
    1996         {
    1997             return chr(0xf0 | ($codepoint >> 18)) . chr(0x80 | (($codepoint >> 12) & 0x3f)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f));
    1998         }
    1999 
    2000         // U+FFFD REPLACEMENT CHARACTER
    2001         return "\xEF\xBF\xBD";
    2002     }
    2003 
    2004     /**
    2005      * Similar to parse_str()
    2006      *
    2007      * Returns an associative array of name/value pairs, where the value is an
    2008      * array of values that have used the same name
    2009      *
    2010      * @static
    2011      * @param string $str The input string.
    2012      * @return array
    2013      */
    2014     public static function parse_str($str)
    2015     {
    2016         $return = array();
    2017         $str = explode('&', $str);
    2018 
    2019         foreach ($str as $section)
    2020         {
    2021             if (strpos($section, '=') !== false)
    2022             {
    2023                 list($name, $value) = explode('=', $section, 2);
    2024                 $return[urldecode($name)][] = urldecode($value);
    2025             }
    2026             else
    2027             {
    2028                 $return[urldecode($section)][] = null;
    2029             }
    2030         }
    2031 
    2032         return $return;
    2033     }
    2034 
    2035     /**
    2036      * Detect XML encoding, as per XML 1.0 Appendix F.1
    2037      *
    2038      * @todo Add support for EBCDIC
    2039      * @param string $data XML data
    2040      * @param SimplePie_Registry $registry Class registry
    2041      * @return array Possible encodings
    2042      */
    2043     public static function xml_encoding($data, $registry)
    2044     {
    2045         // UTF-32 Big Endian BOM
    2046         if (substr($data, 0, 4) === "\x00\x00\xFE\xFF")
    2047         {
    2048             $encoding[] = 'UTF-32BE';
    2049         }
    2050         // UTF-32 Little Endian BOM
    2051         elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00")
    2052         {
    2053             $encoding[] = 'UTF-32LE';
    2054         }
    2055         // UTF-16 Big Endian BOM
    2056         elseif (substr($data, 0, 2) === "\xFE\xFF")
    2057         {
    2058             $encoding[] = 'UTF-16BE';
    2059         }
    2060         // UTF-16 Little Endian BOM
    2061         elseif (substr($data, 0, 2) === "\xFF\xFE")
    2062         {
    2063             $encoding[] = 'UTF-16LE';
    2064         }
    2065         // UTF-8 BOM
    2066         elseif (substr($data, 0, 3) === "\xEF\xBB\xBF")
    2067         {
    2068             $encoding[] = 'UTF-8';
    2069         }
    2070         // UTF-32 Big Endian Without BOM
    2071         elseif (substr($data, 0, 20) === "\x00\x00\x00\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C")
    2072         {
    2073             if ($pos = strpos($data, "\x00\x00\x00\x3F\x00\x00\x00\x3E"))
    2074             {
    2075                 $parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32BE', 'UTF-8')));
    2076                 if ($parser->parse())
    2077                 {
    2078                     $encoding[] = $parser->encoding;
    2079                 }
    2080             }
    2081             $encoding[] = 'UTF-32BE';
    2082         }
    2083         // UTF-32 Little Endian Without BOM
    2084         elseif (substr($data, 0, 20) === "\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C\x00\x00\x00")
    2085         {
    2086             if ($pos = strpos($data, "\x3F\x00\x00\x00\x3E\x00\x00\x00"))
    2087             {
    2088                 $parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32LE', 'UTF-8')));
    2089                 if ($parser->parse())
    2090                 {
    2091                     $encoding[] = $parser->encoding;
    2092                 }
    2093             }
    2094             $encoding[] = 'UTF-32LE';
    2095         }
    2096         // UTF-16 Big Endian Without BOM
    2097         elseif (substr($data, 0, 10) === "\x00\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C")
    2098         {
    2099             if ($pos = strpos($data, "\x00\x3F\x00\x3E"))
    2100             {
    2101                 $parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16BE', 'UTF-8')));
    2102                 if ($parser->parse())
    2103                 {
    2104                     $encoding[] = $parser->encoding;
    2105                 }
    2106             }
    2107             $encoding[] = 'UTF-16BE';
    2108         }
    2109         // UTF-16 Little Endian Without BOM
    2110         elseif (substr($data, 0, 10) === "\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C\x00")
    2111         {
    2112             if ($pos = strpos($data, "\x3F\x00\x3E\x00"))
    2113             {
    2114                 $parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16LE', 'UTF-8')));
    2115                 if ($parser->parse())
    2116                 {
    2117                     $encoding[] = $parser->encoding;
    2118                 }
    2119             }
    2120             $encoding[] = 'UTF-16LE';
    2121         }
    2122         // US-ASCII (or superset)
    2123         elseif (substr($data, 0, 5) === "\x3C\x3F\x78\x6D\x6C")
    2124         {
    2125             if ($pos = strpos($data, "\x3F\x3E"))
    2126             {
    2127                 $parser = $registry->create('XML_Declaration_Parser', array(substr($data, 5, $pos - 5)));
    2128                 if ($parser->parse())
    2129                 {
    2130                     $encoding[] = $parser->encoding;
    2131                 }
    2132             }
    2133             $encoding[] = 'UTF-8';
    2134         }
    2135         // Fallback to UTF-8
    2136         else
    2137         {
    2138             $encoding[] = 'UTF-8';
    2139         }
    2140         return $encoding;
    2141     }
    2142 
    2143     public static function output_javascript()
    2144     {
    2145         if (function_exists('ob_gzhandler'))
    2146         {
    2147             ob_start('ob_gzhandler');
    2148         }
    2149         header('Content-type: text/javascript; charset: UTF-8');
    2150         header('Cache-Control: must-revalidate');
    2151         header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 604800) . ' GMT'); // 7 days
    2152         ?>
     56    private static $SIMPLEPIE_BUILD = null;
     57
     58    public static function time_hms($seconds)
     59    {
     60        $time = '';
     61
     62        $hours = floor($seconds / 3600);
     63        $remainder = $seconds % 3600;
     64        if ($hours > 0) {
     65            $time .= $hours.':';
     66        }
     67
     68        $minutes = floor($remainder / 60);
     69        $seconds = $remainder % 60;
     70        if ($minutes < 10 && $hours > 0) {
     71            $minutes = '0' . $minutes;
     72        }
     73        if ($seconds < 10) {
     74            $seconds = '0' . $seconds;
     75        }
     76
     77        $time .= $minutes.':';
     78        $time .= $seconds;
     79
     80        return $time;
     81    }
     82
     83    public static function absolutize_url($relative, $base)
     84    {
     85        $iri = \SimplePie\IRI::absolutize(new \SimplePie\IRI($base), $relative);
     86        if ($iri === false) {
     87            return false;
     88        }
     89        return $iri->get_uri();
     90    }
     91
     92    /**
     93     * Get a HTML/XML element from a HTML string
     94     *
     95     * @deprecated since SimplePie 1.3, use DOMDocument instead (parsing HTML with regex is bad!)
     96     * @param string $realname Element name (including namespace prefix if applicable)
     97     * @param string $string HTML document
     98     * @return array
     99     */
     100    public static function get_element($realname, $string)
     101    {
     102        // trigger_error(sprintf('Using method "' . __METHOD__ . '" is deprecated since SimplePie 1.3, use "DOMDocument" instead.'), \E_USER_DEPRECATED);
     103
     104        $return = [];
     105        $name = preg_quote($realname, '/');
     106        if (preg_match_all("/<($name)" . \SimplePie\SimplePie::PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$name>|(\/)?>)/siU", $string, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) {
     107            for ($i = 0, $total_matches = count($matches); $i < $total_matches; $i++) {
     108                $return[$i]['tag'] = $realname;
     109                $return[$i]['full'] = $matches[$i][0][0];
     110                $return[$i]['offset'] = $matches[$i][0][1];
     111                if (strlen($matches[$i][3][0]) <= 2) {
     112                    $return[$i]['self_closing'] = true;
     113                } else {
     114                    $return[$i]['self_closing'] = false;
     115                    $return[$i]['content'] = $matches[$i][4][0];
     116                }
     117                $return[$i]['attribs'] = [];
     118                if (isset($matches[$i][2][0]) && preg_match_all('/[\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]*)?))?/', ' ' . $matches[$i][2][0] . ' ', $attribs, PREG_SET_ORDER)) {
     119                    for ($j = 0, $total_attribs = count($attribs); $j < $total_attribs; $j++) {
     120                        if (count($attribs[$j]) === 2) {
     121                            $attribs[$j][2] = $attribs[$j][1];
     122                        }
     123                        $return[$i]['attribs'][strtolower($attribs[$j][1])]['data'] = Misc::entities_decode(end($attribs[$j]));
     124                    }
     125                }
     126            }
     127        }
     128        return $return;
     129    }
     130
     131    public static function element_implode($element)
     132    {
     133        $full = "<$element[tag]";
     134        foreach ($element['attribs'] as $key => $value) {
     135            $key = strtolower($key);
     136            $full .= " $key=\"" . htmlspecialchars($value['data'], ENT_COMPAT, 'UTF-8') . '"';
     137        }
     138        if ($element['self_closing']) {
     139            $full .= ' />';
     140        } else {
     141            $full .= ">$element[content]</$element[tag]>";
     142        }
     143        return $full;
     144    }
     145
     146    public static function error($message, $level, $file, $line)
     147    {
     148        if ((error_reporting() & $level) > 0) {
     149            switch ($level) {
     150                case E_USER_ERROR:
     151                    $note = 'PHP Error';
     152                    break;
     153                case E_USER_WARNING:
     154                    $note = 'PHP Warning';
     155                    break;
     156                case E_USER_NOTICE:
     157                    $note = 'PHP Notice';
     158                    break;
     159                default:
     160                    $note = 'Unknown Error';
     161                    break;
     162            }
     163
     164            $log_error = true;
     165            if (!function_exists('error_log')) {
     166                $log_error = false;
     167            }
     168
     169            $log_file = @ini_get('error_log');
     170            if (!empty($log_file) && ('syslog' !== $log_file) && !@is_writable($log_file)) {
     171                $log_error = false;
     172            }
     173
     174            if ($log_error) {
     175                @error_log("$note: $message in $file on line $line", 0);
     176            }
     177        }
     178
     179        return $message;
     180    }
     181
     182    public static function fix_protocol($url, $http = 1)
     183    {
     184        $url = Misc::normalize_url($url);
     185        $parsed = Misc::parse_url($url);
     186        if ($parsed['scheme'] !== '' && $parsed['scheme'] !== 'http' && $parsed['scheme'] !== 'https') {
     187            return Misc::fix_protocol(Misc::compress_parse_url('http', $parsed['authority'], $parsed['path'], $parsed['query'], $parsed['fragment']), $http);
     188        }
     189
     190        if ($parsed['scheme'] === '' && $parsed['authority'] === '' && !file_exists($url)) {
     191            return Misc::fix_protocol(Misc::compress_parse_url('http', $parsed['path'], '', $parsed['query'], $parsed['fragment']), $http);
     192        }
     193
     194        if ($http === 2 && $parsed['scheme'] !== '') {
     195            return "feed:$url";
     196        } elseif ($http === 3 && strtolower($parsed['scheme']) === 'http') {
     197            return substr_replace($url, 'podcast', 0, 4);
     198        } elseif ($http === 4 && strtolower($parsed['scheme']) === 'http') {
     199            return substr_replace($url, 'itpc', 0, 4);
     200        }
     201
     202        return $url;
     203    }
     204
     205    /**
     206     * @deprecated since SimplePie 1.8.0, use PHP native array_replace_recursive() instead.
     207     */
     208    public static function array_merge_recursive($array1, $array2)
     209    {
     210        foreach ($array2 as $key => $value) {
     211            if (is_array($value)) {
     212                $array1[$key] = Misc::array_merge_recursive($array1[$key], $value);
     213            } else {
     214                $array1[$key] = $value;
     215            }
     216        }
     217
     218        return $array1;
     219    }
     220
     221    public static function parse_url($url)
     222    {
     223        $iri = new \SimplePie\IRI($url);
     224        return [
     225            'scheme' => (string) $iri->scheme,
     226            'authority' => (string) $iri->authority,
     227            'path' => (string) $iri->path,
     228            'query' => (string) $iri->query,
     229            'fragment' => (string) $iri->fragment
     230        ];
     231    }
     232
     233    public static function compress_parse_url($scheme = '', $authority = '', $path = '', $query = '', $fragment = '')
     234    {
     235        $iri = new \SimplePie\IRI('');
     236        $iri->scheme = $scheme;
     237        $iri->authority = $authority;
     238        $iri->path = $path;
     239        $iri->query = $query;
     240        $iri->fragment = $fragment;
     241        return $iri->get_uri();
     242    }
     243
     244    public static function normalize_url($url)
     245    {
     246        $iri = new \SimplePie\IRI($url);
     247        return $iri->get_uri();
     248    }
     249
     250    public static function percent_encoding_normalization($match)
     251    {
     252        $integer = hexdec($match[1]);
     253        if ($integer >= 0x41 && $integer <= 0x5A || $integer >= 0x61 && $integer <= 0x7A || $integer >= 0x30 && $integer <= 0x39 || $integer === 0x2D || $integer === 0x2E || $integer === 0x5F || $integer === 0x7E) {
     254            return chr($integer);
     255        }
     256
     257        return strtoupper($match[0]);
     258    }
     259
     260    /**
     261     * Converts a Windows-1252 encoded string to a UTF-8 encoded string
     262     *
     263     * @static
     264     * @param string $string Windows-1252 encoded string
     265     * @return string UTF-8 encoded string
     266     */
     267    public static function windows_1252_to_utf8($string)
     268    {
     269        static $convert_table = ["\x80" => "\xE2\x82\xAC", "\x81" => "\xEF\xBF\xBD", "\x82" => "\xE2\x80\x9A", "\x83" => "\xC6\x92", "\x84" => "\xE2\x80\x9E", "\x85" => "\xE2\x80\xA6", "\x86" => "\xE2\x80\xA0", "\x87" => "\xE2\x80\xA1", "\x88" => "\xCB\x86", "\x89" => "\xE2\x80\xB0", "\x8A" => "\xC5\xA0", "\x8B" => "\xE2\x80\xB9", "\x8C" => "\xC5\x92", "\x8D" => "\xEF\xBF\xBD", "\x8E" => "\xC5\xBD", "\x8F" => "\xEF\xBF\xBD", "\x90" => "\xEF\xBF\xBD", "\x91" => "\xE2\x80\x98", "\x92" => "\xE2\x80\x99", "\x93" => "\xE2\x80\x9C", "\x94" => "\xE2\x80\x9D", "\x95" => "\xE2\x80\xA2", "\x96" => "\xE2\x80\x93", "\x97" => "\xE2\x80\x94", "\x98" => "\xCB\x9C", "\x99" => "\xE2\x84\xA2", "\x9A" => "\xC5\xA1", "\x9B" => "\xE2\x80\xBA", "\x9C" => "\xC5\x93", "\x9D" => "\xEF\xBF\xBD", "\x9E" => "\xC5\xBE", "\x9F" => "\xC5\xB8", "\xA0" => "\xC2\xA0", "\xA1" => "\xC2\xA1", "\xA2" => "\xC2\xA2", "\xA3" => "\xC2\xA3", "\xA4" => "\xC2\xA4", "\xA5" => "\xC2\xA5", "\xA6" => "\xC2\xA6", "\xA7" => "\xC2\xA7", "\xA8" => "\xC2\xA8", "\xA9" => "\xC2\xA9", "\xAA" => "\xC2\xAA", "\xAB" => "\xC2\xAB", "\xAC" => "\xC2\xAC", "\xAD" => "\xC2\xAD", "\xAE" => "\xC2\xAE", "\xAF" => "\xC2\xAF", "\xB0" => "\xC2\xB0", "\xB1" => "\xC2\xB1", "\xB2" => "\xC2\xB2", "\xB3" => "\xC2\xB3", "\xB4" => "\xC2\xB4", "\xB5" => "\xC2\xB5", "\xB6" => "\xC2\xB6", "\xB7" => "\xC2\xB7", "\xB8" => "\xC2\xB8", "\xB9" => "\xC2\xB9", "\xBA" => "\xC2\xBA", "\xBB" => "\xC2\xBB", "\xBC" => "\xC2\xBC", "\xBD" => "\xC2\xBD", "\xBE" => "\xC2\xBE", "\xBF" => "\xC2\xBF", "\xC0" => "\xC3\x80", "\xC1" => "\xC3\x81", "\xC2" => "\xC3\x82", "\xC3" => "\xC3\x83", "\xC4" => "\xC3\x84", "\xC5" => "\xC3\x85", "\xC6" => "\xC3\x86", "\xC7" => "\xC3\x87", "\xC8" => "\xC3\x88", "\xC9" => "\xC3\x89", "\xCA" => "\xC3\x8A", "\xCB" => "\xC3\x8B", "\xCC" => "\xC3\x8C", "\xCD" => "\xC3\x8D", "\xCE" => "\xC3\x8E", "\xCF" => "\xC3\x8F", "\xD0" => "\xC3\x90", "\xD1" => "\xC3\x91", "\xD2" => "\xC3\x92", "\xD3" => "\xC3\x93", "\xD4" => "\xC3\x94", "\xD5" => "\xC3\x95", "\xD6" => "\xC3\x96", "\xD7" => "\xC3\x97", "\xD8" => "\xC3\x98", "\xD9" => "\xC3\x99", "\xDA" => "\xC3\x9A", "\xDB" => "\xC3\x9B", "\xDC" => "\xC3\x9C", "\xDD" => "\xC3\x9D", "\xDE" => "\xC3\x9E", "\xDF" => "\xC3\x9F", "\xE0" => "\xC3\xA0", "\xE1" => "\xC3\xA1", "\xE2" => "\xC3\xA2", "\xE3" => "\xC3\xA3", "\xE4" => "\xC3\xA4", "\xE5" => "\xC3\xA5", "\xE6" => "\xC3\xA6", "\xE7" => "\xC3\xA7", "\xE8" => "\xC3\xA8", "\xE9" => "\xC3\xA9", "\xEA" => "\xC3\xAA", "\xEB" => "\xC3\xAB", "\xEC" => "\xC3\xAC", "\xED" => "\xC3\xAD", "\xEE" => "\xC3\xAE", "\xEF" => "\xC3\xAF", "\xF0" => "\xC3\xB0", "\xF1" => "\xC3\xB1", "\xF2" => "\xC3\xB2", "\xF3" => "\xC3\xB3", "\xF4" => "\xC3\xB4", "\xF5" => "\xC3\xB5", "\xF6" => "\xC3\xB6", "\xF7" => "\xC3\xB7", "\xF8" => "\xC3\xB8", "\xF9" => "\xC3\xB9", "\xFA" => "\xC3\xBA", "\xFB" => "\xC3\xBB", "\xFC" => "\xC3\xBC", "\xFD" => "\xC3\xBD", "\xFE" => "\xC3\xBE", "\xFF" => "\xC3\xBF"];
     270
     271        return strtr($string, $convert_table);
     272    }
     273
     274    /**
     275     * Change a string from one encoding to another
     276     *
     277     * @param string $data Raw data in $input encoding
     278     * @param string $input Encoding of $data
     279     * @param string $output Encoding you want
     280     * @return string|boolean False if we can't convert it
     281     */
     282    public static function change_encoding($data, $input, $output)
     283    {
     284        $input = Misc::encoding($input);
     285        $output = Misc::encoding($output);
     286
     287        // We fail to fail on non US-ASCII bytes
     288        if ($input === 'US-ASCII') {
     289            static $non_ascii_octects = '';
     290            if (!$non_ascii_octects) {
     291                for ($i = 0x80; $i <= 0xFF; $i++) {
     292                    $non_ascii_octects .= chr($i);
     293                }
     294            }
     295            $data = substr($data, 0, strcspn($data, $non_ascii_octects));
     296        }
     297
     298        // This is first, as behaviour of this is completely predictable
     299        if ($input === 'windows-1252' && $output === 'UTF-8') {
     300            return Misc::windows_1252_to_utf8($data);
     301        }
     302        // This is second, as behaviour of this varies only with PHP version (the middle part of this expression checks the encoding is supported).
     303        elseif (function_exists('mb_convert_encoding') && ($return = Misc::change_encoding_mbstring($data, $input, $output))) {
     304            return $return;
     305        }
     306        // This is third, as behaviour of this varies with OS userland and PHP version
     307        elseif (function_exists('iconv') && ($return = Misc::change_encoding_iconv($data, $input, $output))) {
     308            return $return;
     309        }
     310        // This is last, as behaviour of this varies with OS userland and PHP version
     311        elseif (class_exists('\UConverter') && ($return = Misc::change_encoding_uconverter($data, $input, $output))) {
     312            return $return;
     313        }
     314
     315        // If we can't do anything, just fail
     316        return false;
     317    }
     318
     319    protected static function change_encoding_mbstring($data, $input, $output)
     320    {
     321        if ($input === 'windows-949') {
     322            $input = 'EUC-KR';
     323        }
     324        if ($output === 'windows-949') {
     325            $output = 'EUC-KR';
     326        }
     327        if ($input === 'Windows-31J') {
     328            $input = 'SJIS';
     329        }
     330        if ($output === 'Windows-31J') {
     331            $output = 'SJIS';
     332        }
     333
     334        // Check that the encoding is supported
     335        if (!in_array($input, mb_list_encodings())) {
     336            return false;
     337        }
     338
     339        if (@mb_convert_encoding("\x80", 'UTF-16BE', $input) === "\x00\x80") {
     340            return false;
     341        }
     342
     343        // Let's do some conversion
     344        if ($return = @mb_convert_encoding($data, $output, $input)) {
     345            return $return;
     346        }
     347
     348        return false;
     349    }
     350
     351    protected static function change_encoding_iconv($data, $input, $output)
     352    {
     353        return @iconv($input, $output, $data);
     354    }
     355
     356    /**
     357     * @param string $data
     358     * @param string $input
     359     * @param string $output
     360     * @return string|false
     361     */
     362    protected static function change_encoding_uconverter($data, $input, $output)
     363    {
     364        return @\UConverter::transcode($data, $output, $input);
     365    }
     366
     367    /**
     368     * Normalize an encoding name
     369     *
     370     * This is automatically generated by create.php
     371     *
     372     * To generate it, run `php create.php` on the command line, and copy the
     373     * output to replace this function.
     374     *
     375     * @param string $charset Character set to standardise
     376     * @return string Standardised name
     377     */
     378    public static function encoding($charset)
     379    {
     380        // Normalization from UTS #22
     381        switch (strtolower(preg_replace('/(?:[^a-zA-Z0-9]+|([^0-9])0+)/', '\1', $charset))) {
     382            case 'adobestandardencoding':
     383            case 'csadobestandardencoding':
     384                return 'Adobe-Standard-Encoding';
     385
     386            case 'adobesymbolencoding':
     387            case 'cshppsmath':
     388                return 'Adobe-Symbol-Encoding';
     389
     390            case 'ami1251':
     391            case 'amiga1251':
     392                return 'Amiga-1251';
     393
     394            case 'ansix31101983':
     395            case 'csat5001983':
     396            case 'csiso99naplps':
     397            case 'isoir99':
     398            case 'naplps':
     399                return 'ANSI_X3.110-1983';
     400
     401            case 'arabic7':
     402            case 'asmo449':
     403            case 'csiso89asmo449':
     404            case 'iso9036':
     405            case 'isoir89':
     406                return 'ASMO_449';
     407
     408            case 'big5':
     409            case 'csbig5':
     410                return 'Big5';
     411
     412            case 'big5hkscs':
     413                return 'Big5-HKSCS';
     414
     415            case 'bocu1':
     416            case 'csbocu1':
     417                return 'BOCU-1';
     418
     419            case 'brf':
     420            case 'csbrf':
     421                return 'BRF';
     422
     423            case 'bs4730':
     424            case 'csiso4unitedkingdom':
     425            case 'gb':
     426            case 'iso646gb':
     427            case 'isoir4':
     428            case 'uk':
     429                return 'BS_4730';
     430
     431            case 'bsviewdata':
     432            case 'csiso47bsviewdata':
     433            case 'isoir47':
     434                return 'BS_viewdata';
     435
     436            case 'cesu8':
     437            case 'cscesu8':
     438                return 'CESU-8';
     439
     440            case 'ca':
     441            case 'csa71':
     442            case 'csaz243419851':
     443            case 'csiso121canadian1':
     444            case 'iso646ca':
     445            case 'isoir121':
     446                return 'CSA_Z243.4-1985-1';
     447
     448            case 'csa72':
     449            case 'csaz243419852':
     450            case 'csiso122canadian2':
     451            case 'iso646ca2':
     452            case 'isoir122':
     453                return 'CSA_Z243.4-1985-2';
     454
     455            case 'csaz24341985gr':
     456            case 'csiso123csaz24341985gr':
     457            case 'isoir123':
     458                return 'CSA_Z243.4-1985-gr';
     459
     460            case 'csiso139csn369103':
     461            case 'csn369103':
     462            case 'isoir139':
     463                return 'CSN_369103';
     464
     465            case 'csdecmcs':
     466            case 'dec':
     467            case 'decmcs':
     468                return 'DEC-MCS';
     469
     470            case 'csiso21german':
     471            case 'de':
     472            case 'din66003':
     473            case 'iso646de':
     474            case 'isoir21':
     475                return 'DIN_66003';
     476
     477            case 'csdkus':
     478            case 'dkus':
     479                return 'dk-us';
     480
     481            case 'csiso646danish':
     482            case 'dk':
     483            case 'ds2089':
     484            case 'iso646dk':
     485                return 'DS_2089';
     486
     487            case 'csibmebcdicatde':
     488            case 'ebcdicatde':
     489                return 'EBCDIC-AT-DE';
     490
     491            case 'csebcdicatdea':
     492            case 'ebcdicatdea':
     493                return 'EBCDIC-AT-DE-A';
     494
     495            case 'csebcdiccafr':
     496            case 'ebcdiccafr':
     497                return 'EBCDIC-CA-FR';
     498
     499            case 'csebcdicdkno':
     500            case 'ebcdicdkno':
     501                return 'EBCDIC-DK-NO';
     502
     503            case 'csebcdicdknoa':
     504            case 'ebcdicdknoa':
     505                return 'EBCDIC-DK-NO-A';
     506
     507            case 'csebcdices':
     508            case 'ebcdices':
     509                return 'EBCDIC-ES';
     510
     511            case 'csebcdicesa':
     512            case 'ebcdicesa':
     513                return 'EBCDIC-ES-A';
     514
     515            case 'csebcdicess':
     516            case 'ebcdicess':
     517                return 'EBCDIC-ES-S';
     518
     519            case 'csebcdicfise':
     520            case 'ebcdicfise':
     521                return 'EBCDIC-FI-SE';
     522
     523            case 'csebcdicfisea':
     524            case 'ebcdicfisea':
     525                return 'EBCDIC-FI-SE-A';
     526
     527            case 'csebcdicfr':
     528            case 'ebcdicfr':
     529                return 'EBCDIC-FR';
     530
     531            case 'csebcdicit':
     532            case 'ebcdicit':
     533                return 'EBCDIC-IT';
     534
     535            case 'csebcdicpt':
     536            case 'ebcdicpt':
     537                return 'EBCDIC-PT';
     538
     539            case 'csebcdicuk':
     540            case 'ebcdicuk':
     541                return 'EBCDIC-UK';
     542
     543            case 'csebcdicus':
     544            case 'ebcdicus':
     545                return 'EBCDIC-US';
     546
     547            case 'csiso111ecmacyrillic':
     548            case 'ecmacyrillic':
     549            case 'isoir111':
     550            case 'koi8e':
     551                return 'ECMA-cyrillic';
     552
     553            case 'csiso17spanish':
     554            case 'es':
     555            case 'iso646es':
     556            case 'isoir17':
     557                return 'ES';
     558
     559            case 'csiso85spanish2':
     560            case 'es2':
     561            case 'iso646es2':
     562            case 'isoir85':
     563                return 'ES2';
     564
     565            case 'cseucpkdfmtjapanese':
     566            case 'eucjp':
     567            case 'extendedunixcodepackedformatforjapanese':
     568                return 'EUC-JP';
     569
     570            case 'cseucfixwidjapanese':
     571            case 'extendedunixcodefixedwidthforjapanese':
     572                return 'Extended_UNIX_Code_Fixed_Width_for_Japanese';
     573
     574            case 'gb18030':
     575                return 'GB18030';
     576
     577            case 'chinese':
     578            case 'cp936':
     579            case 'csgb2312':
     580            case 'csiso58gb231280':
     581            case 'gb2312':
     582            case 'gb231280':
     583            case 'gbk':
     584            case 'isoir58':
     585            case 'ms936':
     586            case 'windows936':
     587                return 'GBK';
     588
     589            case 'cn':
     590            case 'csiso57gb1988':
     591            case 'gb198880':
     592            case 'iso646cn':
     593            case 'isoir57':
     594                return 'GB_1988-80';
     595
     596            case 'csiso153gost1976874':
     597            case 'gost1976874':
     598            case 'isoir153':
     599            case 'stsev35888':
     600                return 'GOST_19768-74';
     601
     602            case 'csiso150':
     603            case 'csiso150greekccitt':
     604            case 'greekccitt':
     605            case 'isoir150':
     606                return 'greek-ccitt';
     607
     608            case 'csiso88greek7':
     609            case 'greek7':
     610            case 'isoir88':
     611                return 'greek7';
     612
     613            case 'csiso18greek7old':
     614            case 'greek7old':
     615            case 'isoir18':
     616                return 'greek7-old';
     617
     618            case 'cshpdesktop':
     619            case 'hpdesktop':
     620                return 'HP-DeskTop';
     621
     622            case 'cshplegal':
     623            case 'hplegal':
     624                return 'HP-Legal';
     625
     626            case 'cshpmath8':
     627            case 'hpmath8':
     628                return 'HP-Math8';
     629
     630            case 'cshppifont':
     631            case 'hppifont':
     632                return 'HP-Pi-font';
     633
     634            case 'cshproman8':
     635            case 'hproman8':
     636            case 'r8':
     637            case 'roman8':
     638                return 'hp-roman8';
     639
     640            case 'hzgb2312':
     641                return 'HZ-GB-2312';
     642
     643            case 'csibmsymbols':
     644            case 'ibmsymbols':
     645                return 'IBM-Symbols';
     646
     647            case 'csibmthai':
     648            case 'ibmthai':
     649                return 'IBM-Thai';
     650
     651            case 'cp37':
     652            case 'csibm37':
     653            case 'ebcdiccpca':
     654            case 'ebcdiccpnl':
     655            case 'ebcdiccpus':
     656            case 'ebcdiccpwt':
     657            case 'ibm37':
     658                return 'IBM037';
     659
     660            case 'cp38':
     661            case 'csibm38':
     662            case 'ebcdicint':
     663            case 'ibm38':
     664                return 'IBM038';
     665
     666            case 'cp273':
     667            case 'csibm273':
     668            case 'ibm273':
     669                return 'IBM273';
     670
     671            case 'cp274':
     672            case 'csibm274':
     673            case 'ebcdicbe':
     674            case 'ibm274':
     675                return 'IBM274';
     676
     677            case 'cp275':
     678            case 'csibm275':
     679            case 'ebcdicbr':
     680            case 'ibm275':
     681                return 'IBM275';
     682
     683            case 'csibm277':
     684            case 'ebcdiccpdk':
     685            case 'ebcdiccpno':
     686            case 'ibm277':
     687                return 'IBM277';
     688
     689            case 'cp278':
     690            case 'csibm278':
     691            case 'ebcdiccpfi':
     692            case 'ebcdiccpse':
     693            case 'ibm278':
     694                return 'IBM278';
     695
     696            case 'cp280':
     697            case 'csibm280':
     698            case 'ebcdiccpit':
     699            case 'ibm280':
     700                return 'IBM280';
     701
     702            case 'cp281':
     703            case 'csibm281':
     704            case 'ebcdicjpe':
     705            case 'ibm281':
     706                return 'IBM281';
     707
     708            case 'cp284':
     709            case 'csibm284':
     710            case 'ebcdiccpes':
     711            case 'ibm284':
     712                return 'IBM284';
     713
     714            case 'cp285':
     715            case 'csibm285':
     716            case 'ebcdiccpgb':
     717            case 'ibm285':
     718                return 'IBM285';
     719
     720            case 'cp290':
     721            case 'csibm290':
     722            case 'ebcdicjpkana':
     723            case 'ibm290':
     724                return 'IBM290';
     725
     726            case 'cp297':
     727            case 'csibm297':
     728            case 'ebcdiccpfr':
     729            case 'ibm297':
     730                return 'IBM297';
     731
     732            case 'cp420':
     733            case 'csibm420':
     734            case 'ebcdiccpar1':
     735            case 'ibm420':
     736                return 'IBM420';
     737
     738            case 'cp423':
     739            case 'csibm423':
     740            case 'ebcdiccpgr':
     741            case 'ibm423':
     742                return 'IBM423';
     743
     744            case 'cp424':
     745            case 'csibm424':
     746            case 'ebcdiccphe':
     747            case 'ibm424':
     748                return 'IBM424';
     749
     750            case '437':
     751            case 'cp437':
     752            case 'cspc8codepage437':
     753            case 'ibm437':
     754                return 'IBM437';
     755
     756            case 'cp500':
     757            case 'csibm500':
     758            case 'ebcdiccpbe':
     759            case 'ebcdiccpch':
     760            case 'ibm500':
     761                return 'IBM500';
     762
     763            case 'cp775':
     764            case 'cspc775baltic':
     765            case 'ibm775':
     766                return 'IBM775';
     767
     768            case '850':
     769            case 'cp850':
     770            case 'cspc850multilingual':
     771            case 'ibm850':
     772                return 'IBM850';
     773
     774            case '851':
     775            case 'cp851':
     776            case 'csibm851':
     777            case 'ibm851':
     778                return 'IBM851';
     779
     780            case '852':
     781            case 'cp852':
     782            case 'cspcp852':
     783            case 'ibm852':
     784                return 'IBM852';
     785
     786            case '855':
     787            case 'cp855':
     788            case 'csibm855':
     789            case 'ibm855':
     790                return 'IBM855';
     791
     792            case '857':
     793            case 'cp857':
     794            case 'csibm857':
     795            case 'ibm857':
     796                return 'IBM857';
     797
     798            case 'ccsid858':
     799            case 'cp858':
     800            case 'ibm858':
     801            case 'pcmultilingual850euro':
     802                return 'IBM00858';
     803
     804            case '860':
     805            case 'cp860':
     806            case 'csibm860':
     807            case 'ibm860':
     808                return 'IBM860';
     809
     810            case '861':
     811            case 'cp861':
     812            case 'cpis':
     813            case 'csibm861':
     814            case 'ibm861':
     815                return 'IBM861';
     816
     817            case '862':
     818            case 'cp862':
     819            case 'cspc862latinhebrew':
     820            case 'ibm862':
     821                return 'IBM862';
     822
     823            case '863':
     824            case 'cp863':
     825            case 'csibm863':
     826            case 'ibm863':
     827                return 'IBM863';
     828
     829            case 'cp864':
     830            case 'csibm864':
     831            case 'ibm864':
     832                return 'IBM864';
     833
     834            case '865':
     835            case 'cp865':
     836            case 'csibm865':
     837            case 'ibm865':
     838                return 'IBM865';
     839
     840            case '866':
     841            case 'cp866':
     842            case 'csibm866':
     843            case 'ibm866':
     844                return 'IBM866';
     845
     846            case 'cp868':
     847            case 'cpar':
     848            case 'csibm868':
     849            case 'ibm868':
     850                return 'IBM868';
     851
     852            case '869':
     853            case 'cp869':
     854            case 'cpgr':
     855            case 'csibm869':
     856            case 'ibm869':
     857                return 'IBM869';
     858
     859            case 'cp870':
     860            case 'csibm870':
     861            case 'ebcdiccproece':
     862            case 'ebcdiccpyu':
     863            case 'ibm870':
     864                return 'IBM870';
     865
     866            case 'cp871':
     867            case 'csibm871':
     868            case 'ebcdiccpis':
     869            case 'ibm871':
     870                return 'IBM871';
     871
     872            case 'cp880':
     873            case 'csibm880':
     874            case 'ebcdiccyrillic':
     875            case 'ibm880':
     876                return 'IBM880';
     877
     878            case 'cp891':
     879            case 'csibm891':
     880            case 'ibm891':
     881                return 'IBM891';
     882
     883            case 'cp903':
     884            case 'csibm903':
     885            case 'ibm903':
     886                return 'IBM903';
     887
     888            case '904':
     889            case 'cp904':
     890            case 'csibbm904':
     891            case 'ibm904':
     892                return 'IBM904';
     893
     894            case 'cp905':
     895            case 'csibm905':
     896            case 'ebcdiccptr':
     897            case 'ibm905':
     898                return 'IBM905';
     899
     900            case 'cp918':
     901            case 'csibm918':
     902            case 'ebcdiccpar2':
     903            case 'ibm918':
     904                return 'IBM918';
     905
     906            case 'ccsid924':
     907            case 'cp924':
     908            case 'ebcdiclatin9euro':
     909            case 'ibm924':
     910                return 'IBM00924';
     911
     912            case 'cp1026':
     913            case 'csibm1026':
     914            case 'ibm1026':
     915                return 'IBM1026';
     916
     917            case 'ibm1047':
     918                return 'IBM1047';
     919
     920            case 'ccsid1140':
     921            case 'cp1140':
     922            case 'ebcdicus37euro':
     923            case 'ibm1140':
     924                return 'IBM01140';
     925
     926            case 'ccsid1141':
     927            case 'cp1141':
     928            case 'ebcdicde273euro':
     929            case 'ibm1141':
     930                return 'IBM01141';
     931
     932            case 'ccsid1142':
     933            case 'cp1142':
     934            case 'ebcdicdk277euro':
     935            case 'ebcdicno277euro':
     936            case 'ibm1142':
     937                return 'IBM01142';
     938
     939            case 'ccsid1143':
     940            case 'cp1143':
     941            case 'ebcdicfi278euro':
     942            case 'ebcdicse278euro':
     943            case 'ibm1143':
     944                return 'IBM01143';
     945
     946            case 'ccsid1144':
     947            case 'cp1144':
     948            case 'ebcdicit280euro':
     949            case 'ibm1144':
     950                return 'IBM01144';
     951
     952            case 'ccsid1145':
     953            case 'cp1145':
     954            case 'ebcdices284euro':
     955            case 'ibm1145':
     956                return 'IBM01145';
     957
     958            case 'ccsid1146':
     959            case 'cp1146':
     960            case 'ebcdicgb285euro':
     961            case 'ibm1146':
     962                return 'IBM01146';
     963
     964            case 'ccsid1147':
     965            case 'cp1147':
     966            case 'ebcdicfr297euro':
     967            case 'ibm1147':
     968                return 'IBM01147';
     969
     970            case 'ccsid1148':
     971            case 'cp1148':
     972            case 'ebcdicinternational500euro':
     973            case 'ibm1148':
     974                return 'IBM01148';
     975
     976            case 'ccsid1149':
     977            case 'cp1149':
     978            case 'ebcdicis871euro':
     979            case 'ibm1149':
     980                return 'IBM01149';
     981
     982            case 'csiso143iecp271':
     983            case 'iecp271':
     984            case 'isoir143':
     985                return 'IEC_P27-1';
     986
     987            case 'csiso49inis':
     988            case 'inis':
     989            case 'isoir49':
     990                return 'INIS';
     991
     992            case 'csiso50inis8':
     993            case 'inis8':
     994            case 'isoir50':
     995                return 'INIS-8';
     996
     997            case 'csiso51iniscyrillic':
     998            case 'iniscyrillic':
     999            case 'isoir51':
     1000                return 'INIS-cyrillic';
     1001
     1002            case 'csinvariant':
     1003            case 'invariant':
     1004                return 'INVARIANT';
     1005
     1006            case 'iso2022cn':
     1007                return 'ISO-2022-CN';
     1008
     1009            case 'iso2022cnext':
     1010                return 'ISO-2022-CN-EXT';
     1011
     1012            case 'csiso2022jp':
     1013            case 'iso2022jp':
     1014                return 'ISO-2022-JP';
     1015
     1016            case 'csiso2022jp2':
     1017            case 'iso2022jp2':
     1018                return 'ISO-2022-JP-2';
     1019
     1020            case 'csiso2022kr':
     1021            case 'iso2022kr':
     1022                return 'ISO-2022-KR';
     1023
     1024            case 'cswindows30latin1':
     1025            case 'iso88591windows30latin1':
     1026                return 'ISO-8859-1-Windows-3.0-Latin-1';
     1027
     1028            case 'cswindows31latin1':
     1029            case 'iso88591windows31latin1':
     1030                return 'ISO-8859-1-Windows-3.1-Latin-1';
     1031
     1032            case 'csisolatin2':
     1033            case 'iso88592':
     1034            case 'iso885921987':
     1035            case 'isoir101':
     1036            case 'l2':
     1037            case 'latin2':
     1038                return 'ISO-8859-2';
     1039
     1040            case 'cswindows31latin2':
     1041            case 'iso88592windowslatin2':
     1042                return 'ISO-8859-2-Windows-Latin-2';
     1043
     1044            case 'csisolatin3':
     1045            case 'iso88593':
     1046            case 'iso885931988':
     1047            case 'isoir109':
     1048            case 'l3':
     1049            case 'latin3':
     1050                return 'ISO-8859-3';
     1051
     1052            case 'csisolatin4':
     1053            case 'iso88594':
     1054            case 'iso885941988':
     1055            case 'isoir110':
     1056            case 'l4':
     1057            case 'latin4':
     1058                return 'ISO-8859-4';
     1059
     1060            case 'csisolatincyrillic':
     1061            case 'cyrillic':
     1062            case 'iso88595':
     1063            case 'iso885951988':
     1064            case 'isoir144':
     1065                return 'ISO-8859-5';
     1066
     1067            case 'arabic':
     1068            case 'asmo708':
     1069            case 'csisolatinarabic':
     1070            case 'ecma114':
     1071            case 'iso88596':
     1072            case 'iso885961987':
     1073            case 'isoir127':
     1074                return 'ISO-8859-6';
     1075
     1076            case 'csiso88596e':
     1077            case 'iso88596e':
     1078                return 'ISO-8859-6-E';
     1079
     1080            case 'csiso88596i':
     1081            case 'iso88596i':
     1082                return 'ISO-8859-6-I';
     1083
     1084            case 'csisolatingreek':
     1085            case 'ecma118':
     1086            case 'elot928':
     1087            case 'greek':
     1088            case 'greek8':
     1089            case 'iso88597':
     1090            case 'iso885971987':
     1091            case 'isoir126':
     1092                return 'ISO-8859-7';
     1093
     1094            case 'csisolatinhebrew':
     1095            case 'hebrew':
     1096            case 'iso88598':
     1097            case 'iso885981988':
     1098            case 'isoir138':
     1099                return 'ISO-8859-8';
     1100
     1101            case 'csiso88598e':
     1102            case 'iso88598e':
     1103                return 'ISO-8859-8-E';
     1104
     1105            case 'csiso88598i':
     1106            case 'iso88598i':
     1107                return 'ISO-8859-8-I';
     1108
     1109            case 'cswindows31latin5':
     1110            case 'iso88599windowslatin5':
     1111                return 'ISO-8859-9-Windows-Latin-5';
     1112
     1113            case 'csisolatin6':
     1114            case 'iso885910':
     1115            case 'iso8859101992':
     1116            case 'isoir157':
     1117            case 'l6':
     1118            case 'latin6':
     1119                return 'ISO-8859-10';
     1120
     1121            case 'iso885913':
     1122                return 'ISO-8859-13';
     1123
     1124            case 'iso885914':
     1125            case 'iso8859141998':
     1126            case 'isoceltic':
     1127            case 'isoir199':
     1128            case 'l8':
     1129            case 'latin8':
     1130                return 'ISO-8859-14';
     1131
     1132            case 'iso885915':
     1133            case 'latin9':
     1134                return 'ISO-8859-15';
     1135
     1136            case 'iso885916':
     1137            case 'iso8859162001':
     1138            case 'isoir226':
     1139            case 'l10':
     1140            case 'latin10':
     1141                return 'ISO-8859-16';
     1142
     1143            case 'iso10646j1':
     1144                return 'ISO-10646-J-1';
     1145
     1146            case 'csunicode':
     1147            case 'iso10646ucs2':
     1148                return 'ISO-10646-UCS-2';
     1149
     1150            case 'csucs4':
     1151            case 'iso10646ucs4':
     1152                return 'ISO-10646-UCS-4';
     1153
     1154            case 'csunicodeascii':
     1155            case 'iso10646ucsbasic':
     1156                return 'ISO-10646-UCS-Basic';
     1157
     1158            case 'csunicodelatin1':
     1159            case 'iso10646':
     1160            case 'iso10646unicodelatin1':
     1161                return 'ISO-10646-Unicode-Latin1';
     1162
     1163            case 'csiso10646utf1':
     1164            case 'iso10646utf1':
     1165                return 'ISO-10646-UTF-1';
     1166
     1167            case 'csiso115481':
     1168            case 'iso115481':
     1169            case 'isotr115481':
     1170                return 'ISO-11548-1';
     1171
     1172            case 'csiso90':
     1173            case 'isoir90':
     1174                return 'iso-ir-90';
     1175
     1176            case 'csunicodeibm1261':
     1177            case 'isounicodeibm1261':
     1178                return 'ISO-Unicode-IBM-1261';
     1179
     1180            case 'csunicodeibm1264':
     1181            case 'isounicodeibm1264':
     1182                return 'ISO-Unicode-IBM-1264';
     1183
     1184            case 'csunicodeibm1265':
     1185            case 'isounicodeibm1265':
     1186                return 'ISO-Unicode-IBM-1265';
     1187
     1188            case 'csunicodeibm1268':
     1189            case 'isounicodeibm1268':
     1190                return 'ISO-Unicode-IBM-1268';
     1191
     1192            case 'csunicodeibm1276':
     1193            case 'isounicodeibm1276':
     1194                return 'ISO-Unicode-IBM-1276';
     1195
     1196            case 'csiso646basic1983':
     1197            case 'iso646basic1983':
     1198            case 'ref':
     1199                return 'ISO_646.basic:1983';
     1200
     1201            case 'csiso2intlrefversion':
     1202            case 'irv':
     1203            case 'iso646irv1983':
     1204            case 'isoir2':
     1205                return 'ISO_646.irv:1983';
     1206
     1207            case 'csiso2033':
     1208            case 'e13b':
     1209            case 'iso20331983':
     1210            case 'isoir98':
     1211                return 'ISO_2033-1983';
     1212
     1213            case 'csiso5427cyrillic':
     1214            case 'iso5427':
     1215            case 'isoir37':
     1216                return 'ISO_5427';
     1217
     1218            case 'iso5427cyrillic1981':
     1219            case 'iso54271981':
     1220            case 'isoir54':
     1221                return 'ISO_5427:1981';
     1222
     1223            case 'csiso5428greek':
     1224            case 'iso54281980':
     1225            case 'isoir55':
     1226                return 'ISO_5428:1980';
     1227
     1228            case 'csiso6937add':
     1229            case 'iso6937225':
     1230            case 'isoir152':
     1231                return 'ISO_6937-2-25';
     1232
     1233            case 'csisotextcomm':
     1234            case 'iso69372add':
     1235            case 'isoir142':
     1236                return 'ISO_6937-2-add';
     1237
     1238            case 'csiso8859supp':
     1239            case 'iso8859supp':
     1240            case 'isoir154':
     1241            case 'latin125':
     1242                return 'ISO_8859-supp';
     1243
     1244            case 'csiso10367box':
     1245            case 'iso10367box':
     1246            case 'isoir155':
     1247                return 'ISO_10367-box';
     1248
     1249            case 'csiso15italian':
     1250            case 'iso646it':
     1251            case 'isoir15':
     1252            case 'it':
     1253                return 'IT';
     1254
     1255            case 'csiso13jisc6220jp':
     1256            case 'isoir13':
     1257            case 'jisc62201969':
     1258            case 'jisc62201969jp':
     1259            case 'katakana':
     1260            case 'x2017':
     1261                return 'JIS_C6220-1969-jp';
     1262
     1263            case 'csiso14jisc6220ro':
     1264            case 'iso646jp':
     1265            case 'isoir14':
     1266            case 'jisc62201969ro':
     1267            case 'jp':
     1268                return 'JIS_C6220-1969-ro';
     1269
     1270            case 'csiso42jisc62261978':
     1271            case 'isoir42':
     1272            case 'jisc62261978':
     1273                return 'JIS_C6226-1978';
     1274
     1275            case 'csiso87jisx208':
     1276            case 'isoir87':
     1277            case 'jisc62261983':
     1278            case 'jisx2081983':
     1279            case 'x208':
     1280                return 'JIS_C6226-1983';
     1281
     1282            case 'csiso91jisc62291984a':
     1283            case 'isoir91':
     1284            case 'jisc62291984a':
     1285            case 'jpocra':
     1286                return 'JIS_C6229-1984-a';
     1287
     1288            case 'csiso92jisc62991984b':
     1289            case 'iso646jpocrb':
     1290            case 'isoir92':
     1291            case 'jisc62291984b':
     1292            case 'jpocrb':
     1293                return 'JIS_C6229-1984-b';
     1294
     1295            case 'csiso93jis62291984badd':
     1296            case 'isoir93':
     1297            case 'jisc62291984badd':
     1298            case 'jpocrbadd':
     1299                return 'JIS_C6229-1984-b-add';
     1300
     1301            case 'csiso94jis62291984hand':
     1302            case 'isoir94':
     1303            case 'jisc62291984hand':
     1304            case 'jpocrhand':
     1305                return 'JIS_C6229-1984-hand';
     1306
     1307            case 'csiso95jis62291984handadd':
     1308            case 'isoir95':
     1309            case 'jisc62291984handadd':
     1310            case 'jpocrhandadd':
     1311                return 'JIS_C6229-1984-hand-add';
     1312
     1313            case 'csiso96jisc62291984kana':
     1314            case 'isoir96':
     1315            case 'jisc62291984kana':
     1316                return 'JIS_C6229-1984-kana';
     1317
     1318            case 'csjisencoding':
     1319            case 'jisencoding':
     1320                return 'JIS_Encoding';
     1321
     1322            case 'cshalfwidthkatakana':
     1323            case 'jisx201':
     1324            case 'x201':
     1325                return 'JIS_X0201';
     1326
     1327            case 'csiso159jisx2121990':
     1328            case 'isoir159':
     1329            case 'jisx2121990':
     1330            case 'x212':
     1331                return 'JIS_X0212-1990';
     1332
     1333            case 'csiso141jusib1002':
     1334            case 'iso646yu':
     1335            case 'isoir141':
     1336            case 'js':
     1337            case 'jusib1002':
     1338            case 'yu':
     1339                return 'JUS_I.B1.002';
     1340
     1341            case 'csiso147macedonian':
     1342            case 'isoir147':
     1343            case 'jusib1003mac':
     1344            case 'macedonian':
     1345                return 'JUS_I.B1.003-mac';
     1346
     1347            case 'csiso146serbian':
     1348            case 'isoir146':
     1349            case 'jusib1003serb':
     1350            case 'serbian':
     1351                return 'JUS_I.B1.003-serb';
     1352
     1353            case 'koi7switched':
     1354                return 'KOI7-switched';
     1355
     1356            case 'cskoi8r':
     1357            case 'koi8r':
     1358                return 'KOI8-R';
     1359
     1360            case 'koi8u':
     1361                return 'KOI8-U';
     1362
     1363            case 'csksc5636':
     1364            case 'iso646kr':
     1365            case 'ksc5636':
     1366                return 'KSC5636';
     1367
     1368            case 'cskz1048':
     1369            case 'kz1048':
     1370            case 'rk1048':
     1371            case 'strk10482002':
     1372                return 'KZ-1048';
     1373
     1374            case 'csiso19latingreek':
     1375            case 'isoir19':
     1376            case 'latingreek':
     1377                return 'latin-greek';
     1378
     1379            case 'csiso27latingreek1':
     1380            case 'isoir27':
     1381            case 'latingreek1':
     1382                return 'Latin-greek-1';
     1383
     1384            case 'csiso158lap':
     1385            case 'isoir158':
     1386            case 'lap':
     1387            case 'latinlap':
     1388                return 'latin-lap';
     1389
     1390            case 'csmacintosh':
     1391            case 'mac':
     1392            case 'macintosh':
     1393                return 'macintosh';
     1394
     1395            case 'csmicrosoftpublishing':
     1396            case 'microsoftpublishing':
     1397                return 'Microsoft-Publishing';
     1398
     1399            case 'csmnem':
     1400            case 'mnem':
     1401                return 'MNEM';
     1402
     1403            case 'csmnemonic':
     1404            case 'mnemonic':
     1405                return 'MNEMONIC';
     1406
     1407            case 'csiso86hungarian':
     1408            case 'hu':
     1409            case 'iso646hu':
     1410            case 'isoir86':
     1411            case 'msz77953':
     1412                return 'MSZ_7795.3';
     1413
     1414            case 'csnatsdano':
     1415            case 'isoir91':
     1416            case 'natsdano':
     1417                return 'NATS-DANO';
     1418
     1419            case 'csnatsdanoadd':
     1420            case 'isoir92':
     1421            case 'natsdanoadd':
     1422                return 'NATS-DANO-ADD';
     1423
     1424            case 'csnatssefi':
     1425            case 'isoir81':
     1426            case 'natssefi':
     1427                return 'NATS-SEFI';
     1428
     1429            case 'csnatssefiadd':
     1430            case 'isoir82':
     1431            case 'natssefiadd':
     1432                return 'NATS-SEFI-ADD';
     1433
     1434            case 'csiso151cuba':
     1435            case 'cuba':
     1436            case 'iso646cu':
     1437            case 'isoir151':
     1438            case 'ncnc1081':
     1439                return 'NC_NC00-10:81';
     1440
     1441            case 'csiso69french':
     1442            case 'fr':
     1443            case 'iso646fr':
     1444            case 'isoir69':
     1445            case 'nfz62010':
     1446                return 'NF_Z_62-010';
     1447
     1448            case 'csiso25french':
     1449            case 'iso646fr1':
     1450            case 'isoir25':
     1451            case 'nfz620101973':
     1452                return 'NF_Z_62-010_(1973)';
     1453
     1454            case 'csiso60danishnorwegian':
     1455            case 'csiso60norwegian1':
     1456            case 'iso646no':
     1457            case 'isoir60':
     1458            case 'no':
     1459            case 'ns45511':
     1460                return 'NS_4551-1';
     1461
     1462            case 'csiso61norwegian2':
     1463            case 'iso646no2':
     1464            case 'isoir61':
     1465            case 'no2':
     1466            case 'ns45512':
     1467                return 'NS_4551-2';
     1468
     1469            case 'osdebcdicdf3irv':
     1470                return 'OSD_EBCDIC_DF03_IRV';
     1471
     1472            case 'osdebcdicdf41':
     1473                return 'OSD_EBCDIC_DF04_1';
     1474
     1475            case 'osdebcdicdf415':
     1476                return 'OSD_EBCDIC_DF04_15';
     1477
     1478            case 'cspc8danishnorwegian':
     1479            case 'pc8danishnorwegian':
     1480                return 'PC8-Danish-Norwegian';
     1481
     1482            case 'cspc8turkish':
     1483            case 'pc8turkish':
     1484                return 'PC8-Turkish';
     1485
     1486            case 'csiso16portuguese':
     1487            case 'iso646pt':
     1488            case 'isoir16':
     1489            case 'pt':
     1490                return 'PT';
     1491
     1492            case 'csiso84portuguese2':
     1493            case 'iso646pt2':
     1494            case 'isoir84':
     1495            case 'pt2':
     1496                return 'PT2';
     1497
     1498            case 'cp154':
     1499            case 'csptcp154':
     1500            case 'cyrillicasian':
     1501            case 'pt154':
     1502            case 'ptcp154':
     1503                return 'PTCP154';
     1504
     1505            case 'scsu':
     1506                return 'SCSU';
     1507
     1508            case 'csiso10swedish':
     1509            case 'fi':
     1510            case 'iso646fi':
     1511            case 'iso646se':
     1512            case 'isoir10':
     1513            case 'se':
     1514            case 'sen850200b':
     1515                return 'SEN_850200_B';
     1516
     1517            case 'csiso11swedishfornames':
     1518            case 'iso646se2':
     1519            case 'isoir11':
     1520            case 'se2':
     1521            case 'sen850200c':
     1522                return 'SEN_850200_C';
     1523
     1524            case 'csiso102t617bit':
     1525            case 'isoir102':
     1526            case 't617bit':
     1527                return 'T.61-7bit';
     1528
     1529            case 'csiso103t618bit':
     1530            case 'isoir103':
     1531            case 't61':
     1532            case 't618bit':
     1533                return 'T.61-8bit';
     1534
     1535            case 'csiso128t101g2':
     1536            case 'isoir128':
     1537            case 't101g2':
     1538                return 'T.101-G2';
     1539
     1540            case 'cstscii':
     1541            case 'tscii':
     1542                return 'TSCII';
     1543
     1544            case 'csunicode11':
     1545            case 'unicode11':
     1546                return 'UNICODE-1-1';
     1547
     1548            case 'csunicode11utf7':
     1549            case 'unicode11utf7':
     1550                return 'UNICODE-1-1-UTF-7';
     1551
     1552            case 'csunknown8bit':
     1553            case 'unknown8bit':
     1554                return 'UNKNOWN-8BIT';
     1555
     1556            case 'ansix341968':
     1557            case 'ansix341986':
     1558            case 'ascii':
     1559            case 'cp367':
     1560            case 'csascii':
     1561            case 'ibm367':
     1562            case 'iso646irv1991':
     1563            case 'iso646us':
     1564            case 'isoir6':
     1565            case 'us':
     1566            case 'usascii':
     1567                return 'US-ASCII';
     1568
     1569            case 'csusdk':
     1570            case 'usdk':
     1571                return 'us-dk';
     1572
     1573            case 'utf7':
     1574                return 'UTF-7';
     1575
     1576            case 'utf8':
     1577                return 'UTF-8';
     1578
     1579            case 'utf16':
     1580                return 'UTF-16';
     1581
     1582            case 'utf16be':
     1583                return 'UTF-16BE';
     1584
     1585            case 'utf16le':
     1586                return 'UTF-16LE';
     1587
     1588            case 'utf32':
     1589                return 'UTF-32';
     1590
     1591            case 'utf32be':
     1592                return 'UTF-32BE';
     1593
     1594            case 'utf32le':
     1595                return 'UTF-32LE';
     1596
     1597            case 'csventurainternational':
     1598            case 'venturainternational':
     1599                return 'Ventura-International';
     1600
     1601            case 'csventuramath':
     1602            case 'venturamath':
     1603                return 'Ventura-Math';
     1604
     1605            case 'csventuraus':
     1606            case 'venturaus':
     1607                return 'Ventura-US';
     1608
     1609            case 'csiso70videotexsupp1':
     1610            case 'isoir70':
     1611            case 'videotexsuppl':
     1612                return 'videotex-suppl';
     1613
     1614            case 'csviqr':
     1615            case 'viqr':
     1616                return 'VIQR';
     1617
     1618            case 'csviscii':
     1619            case 'viscii':
     1620                return 'VISCII';
     1621
     1622            case 'csshiftjis':
     1623            case 'cswindows31j':
     1624            case 'mskanji':
     1625            case 'shiftjis':
     1626            case 'windows31j':
     1627                return 'Windows-31J';
     1628
     1629            case 'iso885911':
     1630            case 'tis620':
     1631                return 'windows-874';
     1632
     1633            case 'cseuckr':
     1634            case 'csksc56011987':
     1635            case 'euckr':
     1636            case 'isoir149':
     1637            case 'korean':
     1638            case 'ksc5601':
     1639            case 'ksc56011987':
     1640            case 'ksc56011989':
     1641            case 'windows949':
     1642                return 'windows-949';
     1643
     1644            case 'windows1250':
     1645                return 'windows-1250';
     1646
     1647            case 'windows1251':
     1648                return 'windows-1251';
     1649
     1650            case 'cp819':
     1651            case 'csisolatin1':
     1652            case 'ibm819':
     1653            case 'iso88591':
     1654            case 'iso885911987':
     1655            case 'isoir100':
     1656            case 'l1':
     1657            case 'latin1':
     1658            case 'windows1252':
     1659                return 'windows-1252';
     1660
     1661            case 'windows1253':
     1662                return 'windows-1253';
     1663
     1664            case 'csisolatin5':
     1665            case 'iso88599':
     1666            case 'iso885991989':
     1667            case 'isoir148':
     1668            case 'l5':
     1669            case 'latin5':
     1670            case 'windows1254':
     1671                return 'windows-1254';
     1672
     1673            case 'windows1255':
     1674                return 'windows-1255';
     1675
     1676            case 'windows1256':
     1677                return 'windows-1256';
     1678
     1679            case 'windows1257':
     1680                return 'windows-1257';
     1681
     1682            case 'windows1258':
     1683                return 'windows-1258';
     1684
     1685            default:
     1686                return $charset;
     1687        }
     1688    }
     1689
     1690    public static function get_curl_version()
     1691    {
     1692        if (is_array($curl = curl_version())) {
     1693            $curl = $curl['version'];
     1694        } elseif (substr($curl, 0, 5) === 'curl/') {
     1695            $curl = substr($curl, 5, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 5));
     1696        } elseif (substr($curl, 0, 8) === 'libcurl/') {
     1697            $curl = substr($curl, 8, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 8));
     1698        } else {
     1699            $curl = 0;
     1700        }
     1701        return $curl;
     1702    }
     1703
     1704    /**
     1705     * Strip HTML comments
     1706     *
     1707     * @param string $data Data to strip comments from
     1708     * @return string Comment stripped string
     1709     */
     1710    public static function strip_comments($data)
     1711    {
     1712        $output = '';
     1713        while (($start = strpos($data, '<!--')) !== false) {
     1714            $output .= substr($data, 0, $start);
     1715            if (($end = strpos($data, '-->', $start)) !== false) {
     1716                $data = substr_replace($data, '', 0, $end + 3);
     1717            } else {
     1718                $data = '';
     1719            }
     1720        }
     1721        return $output . $data;
     1722    }
     1723
     1724    public static function parse_date($dt)
     1725    {
     1726        $parser = \SimplePie\Parse\Date::get();
     1727        return $parser->parse($dt);
     1728    }
     1729
     1730    /**
     1731     * Decode HTML entities
     1732     *
     1733     * @deprecated since SimplePie 1.3, use DOMDocument instead
     1734     * @param string $data Input data
     1735     * @return string Output data
     1736     */
     1737    public static function entities_decode($data)
     1738    {
     1739        // trigger_error(sprintf('Using method "' . __METHOD__ . '" is deprecated since SimplePie 1.3, use "DOMDocument" instead.'), \E_USER_DEPRECATED);
     1740
     1741        $decoder = new \SimplePie_Decode_HTML_Entities($data);
     1742        return $decoder->parse();
     1743    }
     1744
     1745    /**
     1746     * Remove RFC822 comments
     1747     *
     1748     * @param string $data Data to strip comments from
     1749     * @return string Comment stripped string
     1750     */
     1751    public static function uncomment_rfc822($string)
     1752    {
     1753        $string = (string) $string;
     1754        $position = 0;
     1755        $length = strlen($string);
     1756        $depth = 0;
     1757
     1758        $output = '';
     1759
     1760        while ($position < $length && ($pos = strpos($string, '(', $position)) !== false) {
     1761            $output .= substr($string, $position, $pos - $position);
     1762            $position = $pos + 1;
     1763            if ($string[$pos - 1] !== '\\') {
     1764                $depth++;
     1765                while ($depth && $position < $length) {
     1766                    $position += strcspn($string, '()', $position);
     1767                    if ($string[$position - 1] === '\\') {
     1768                        $position++;
     1769                        continue;
     1770                    } elseif (isset($string[$position])) {
     1771                        switch ($string[$position]) {
     1772                            case '(':
     1773                                $depth++;
     1774                                break;
     1775
     1776                            case ')':
     1777                                $depth--;
     1778                                break;
     1779                        }
     1780                        $position++;
     1781                    } else {
     1782                        break;
     1783                    }
     1784                }
     1785            } else {
     1786                $output .= '(';
     1787            }
     1788        }
     1789        $output .= substr($string, $position);
     1790
     1791        return $output;
     1792    }
     1793
     1794    public static function parse_mime($mime)
     1795    {
     1796        if (($pos = strpos($mime, ';')) === false) {
     1797            return trim($mime);
     1798        }
     1799
     1800        return trim(substr($mime, 0, $pos));
     1801    }
     1802
     1803    public static function atom_03_construct_type($attribs)
     1804    {
     1805        if (isset($attribs['']['mode']) && strtolower(trim($attribs['']['mode'])) === 'base64') {
     1806            $mode = \SimplePie\SimplePie::CONSTRUCT_BASE64;
     1807        } else {
     1808            $mode = \SimplePie\SimplePie::CONSTRUCT_NONE;
     1809        }
     1810        if (isset($attribs['']['type'])) {
     1811            switch (strtolower(trim($attribs['']['type']))) {
     1812                case 'text':
     1813                case 'text/plain':
     1814                    return \SimplePie\SimplePie::CONSTRUCT_TEXT | $mode;
     1815
     1816                case 'html':
     1817                case 'text/html':
     1818                    return \SimplePie\SimplePie::CONSTRUCT_HTML | $mode;
     1819
     1820                case 'xhtml':
     1821                case 'application/xhtml+xml':
     1822                    return \SimplePie\SimplePie::CONSTRUCT_XHTML | $mode;
     1823
     1824                default:
     1825                    return \SimplePie\SimplePie::CONSTRUCT_NONE | $mode;
     1826            }
     1827        }
     1828
     1829        return \SimplePie\SimplePie::CONSTRUCT_TEXT | $mode;
     1830    }
     1831
     1832    public static function atom_10_construct_type($attribs)
     1833    {
     1834        if (isset($attribs['']['type'])) {
     1835            switch (strtolower(trim($attribs['']['type']))) {
     1836                case 'text':
     1837                    return \SimplePie\SimplePie::CONSTRUCT_TEXT;
     1838
     1839                case 'html':
     1840                    return \SimplePie\SimplePie::CONSTRUCT_HTML;
     1841
     1842                case 'xhtml':
     1843                    return \SimplePie\SimplePie::CONSTRUCT_XHTML;
     1844
     1845                default:
     1846                    return \SimplePie\SimplePie::CONSTRUCT_NONE;
     1847            }
     1848        }
     1849        return \SimplePie\SimplePie::CONSTRUCT_TEXT;
     1850    }
     1851
     1852    public static function atom_10_content_construct_type($attribs)
     1853    {
     1854        if (isset($attribs['']['type'])) {
     1855            $type = strtolower(trim($attribs['']['type']));
     1856            switch ($type) {
     1857                case 'text':
     1858                    return \SimplePie\SimplePie::CONSTRUCT_TEXT;
     1859
     1860                case 'html':
     1861                    return \SimplePie\SimplePie::CONSTRUCT_HTML;
     1862
     1863                case 'xhtml':
     1864                    return \SimplePie\SimplePie::CONSTRUCT_XHTML;
     1865            }
     1866            if (in_array(substr($type, -4), ['+xml', '/xml']) || substr($type, 0, 5) === 'text/') {
     1867                return \SimplePie\SimplePie::CONSTRUCT_NONE;
     1868            } else {
     1869                return \SimplePie\SimplePie::CONSTRUCT_BASE64;
     1870            }
     1871        }
     1872
     1873        return \SimplePie\SimplePie::CONSTRUCT_TEXT;
     1874    }
     1875
     1876    public static function is_isegment_nz_nc($string)
     1877    {
     1878        return (bool) preg_match('/^([A-Za-z0-9\-._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!$&\'()*+,;=@]|(%[0-9ABCDEF]{2}))+$/u', $string);
     1879    }
     1880
     1881    public static function space_separated_tokens($string)
     1882    {
     1883        $space_characters = "\x20\x09\x0A\x0B\x0C\x0D";
     1884        $string_length = strlen($string);
     1885
     1886        $position = strspn($string, $space_characters);
     1887        $tokens = [];
     1888
     1889        while ($position < $string_length) {
     1890            $len = strcspn($string, $space_characters, $position);
     1891            $tokens[] = substr($string, $position, $len);
     1892            $position += $len;
     1893            $position += strspn($string, $space_characters, $position);
     1894        }
     1895
     1896        return $tokens;
     1897    }
     1898
     1899    /**
     1900     * Converts a unicode codepoint to a UTF-8 character
     1901     *
     1902     * @static
     1903     * @param int $codepoint Unicode codepoint
     1904     * @return string UTF-8 character
     1905     */
     1906    public static function codepoint_to_utf8($codepoint)
     1907    {
     1908        $codepoint = (int) $codepoint;
     1909        if ($codepoint < 0) {
     1910            return false;
     1911        } elseif ($codepoint <= 0x7f) {
     1912            return chr($codepoint);
     1913        } elseif ($codepoint <= 0x7ff) {
     1914            return chr(0xc0 | ($codepoint >> 6)) . chr(0x80 | ($codepoint & 0x3f));
     1915        } elseif ($codepoint <= 0xffff) {
     1916            return chr(0xe0 | ($codepoint >> 12)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f));
     1917        } elseif ($codepoint <= 0x10ffff) {
     1918            return chr(0xf0 | ($codepoint >> 18)) . chr(0x80 | (($codepoint >> 12) & 0x3f)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f));
     1919        }
     1920
     1921        // U+FFFD REPLACEMENT CHARACTER
     1922        return "\xEF\xBF\xBD";
     1923    }
     1924
     1925    /**
     1926     * Similar to parse_str()
     1927     *
     1928     * Returns an associative array of name/value pairs, where the value is an
     1929     * array of values that have used the same name
     1930     *
     1931     * @static
     1932     * @param string $str The input string.
     1933     * @return array
     1934     */
     1935    public static function parse_str($str)
     1936    {
     1937        $return = [];
     1938        $str = explode('&', $str);
     1939
     1940        foreach ($str as $section) {
     1941            if (strpos($section, '=') !== false) {
     1942                [$name, $value] = explode('=', $section, 2);
     1943                $return[urldecode($name)][] = urldecode($value);
     1944            } else {
     1945                $return[urldecode($section)][] = null;
     1946            }
     1947        }
     1948
     1949        return $return;
     1950    }
     1951
     1952    /**
     1953     * Detect XML encoding, as per XML 1.0 Appendix F.1
     1954     *
     1955     * @todo Add support for EBCDIC
     1956     * @param string $data XML data
     1957     * @param \SimplePie\Registry $registry Class registry
     1958     * @return array Possible encodings
     1959     */
     1960    public static function xml_encoding($data, $registry)
     1961    {
     1962        // UTF-32 Big Endian BOM
     1963        if (substr($data, 0, 4) === "\x00\x00\xFE\xFF") {
     1964            $encoding[] = 'UTF-32BE';
     1965        }
     1966        // UTF-32 Little Endian BOM
     1967        elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00") {
     1968            $encoding[] = 'UTF-32LE';
     1969        }
     1970        // UTF-16 Big Endian BOM
     1971        elseif (substr($data, 0, 2) === "\xFE\xFF") {
     1972            $encoding[] = 'UTF-16BE';
     1973        }
     1974        // UTF-16 Little Endian BOM
     1975        elseif (substr($data, 0, 2) === "\xFF\xFE") {
     1976            $encoding[] = 'UTF-16LE';
     1977        }
     1978        // UTF-8 BOM
     1979        elseif (substr($data, 0, 3) === "\xEF\xBB\xBF") {
     1980            $encoding[] = 'UTF-8';
     1981        }
     1982        // UTF-32 Big Endian Without BOM
     1983        elseif (substr($data, 0, 20) === "\x00\x00\x00\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C") {
     1984            if ($pos = strpos($data, "\x00\x00\x00\x3F\x00\x00\x00\x3E")) {
     1985                $parser = $registry->create(Parser::class, [Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32BE', 'UTF-8')]);
     1986                if ($parser->parse()) {
     1987                    $encoding[] = $parser->encoding;
     1988                }
     1989            }
     1990            $encoding[] = 'UTF-32BE';
     1991        }
     1992        // UTF-32 Little Endian Without BOM
     1993        elseif (substr($data, 0, 20) === "\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C\x00\x00\x00") {
     1994            if ($pos = strpos($data, "\x3F\x00\x00\x00\x3E\x00\x00\x00")) {
     1995                $parser = $registry->create(Parser::class, [Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32LE', 'UTF-8')]);
     1996                if ($parser->parse()) {
     1997                    $encoding[] = $parser->encoding;
     1998                }
     1999            }
     2000            $encoding[] = 'UTF-32LE';
     2001        }
     2002        // UTF-16 Big Endian Without BOM
     2003        elseif (substr($data, 0, 10) === "\x00\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C") {
     2004            if ($pos = strpos($data, "\x00\x3F\x00\x3E")) {
     2005                $parser = $registry->create(Parser::class, [Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16BE', 'UTF-8')]);
     2006                if ($parser->parse()) {
     2007                    $encoding[] = $parser->encoding;
     2008                }
     2009            }
     2010            $encoding[] = 'UTF-16BE';
     2011        }
     2012        // UTF-16 Little Endian Without BOM
     2013        elseif (substr($data, 0, 10) === "\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C\x00") {
     2014            if ($pos = strpos($data, "\x3F\x00\x3E\x00")) {
     2015                $parser = $registry->create(Parser::class, [Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16LE', 'UTF-8')]);
     2016                if ($parser->parse()) {
     2017                    $encoding[] = $parser->encoding;
     2018                }
     2019            }
     2020            $encoding[] = 'UTF-16LE';
     2021        }
     2022        // US-ASCII (or superset)
     2023        elseif (substr($data, 0, 5) === "\x3C\x3F\x78\x6D\x6C") {
     2024            if ($pos = strpos($data, "\x3F\x3E")) {
     2025                $parser = $registry->create(Parser::class, [substr($data, 5, $pos - 5)]);
     2026                if ($parser->parse()) {
     2027                    $encoding[] = $parser->encoding;
     2028                }
     2029            }
     2030            $encoding[] = 'UTF-8';
     2031        }
     2032        // Fallback to UTF-8
     2033        else {
     2034            $encoding[] = 'UTF-8';
     2035        }
     2036        return $encoding;
     2037    }
     2038
     2039    public static function output_javascript()
     2040    {
     2041        if (function_exists('ob_gzhandler')) {
     2042            ob_start('ob_gzhandler');
     2043        }
     2044        header('Content-type: text/javascript; charset: UTF-8');
     2045        header('Cache-Control: must-revalidate');
     2046        header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 604800) . ' GMT'); // 7 days
     2047
     2048        $body = <<<END
    21532049function embed_quicktime(type, bgcolor, width, height, link, placeholder, loop) {
    21542050    if (placeholder != '') {
     
    21712067    document.writeln('<embed type="application/x-mplayer2" src="'+link+'" autosize="1" width="'+width+'" height="'+height+'" showcontrols="1" showstatusbar="0" showdisplay="0" autostart="0"></embed>');
    21722068}
    2173         <?php
    2174     }
    2175 
    2176     /**
    2177      * Get the SimplePie build timestamp
    2178      *
    2179      * Uses the git index if it exists, otherwise uses the modification time
    2180      * of the newest file.
    2181      */
    2182     public static function get_build()
    2183     {
    2184         $root = dirname(dirname(__FILE__));
    2185         if (file_exists($root . '/.git/index'))
    2186         {
    2187             return filemtime($root . '/.git/index');
    2188         }
    2189         elseif (file_exists($root . '/SimplePie'))
    2190         {
    2191             $time = 0;
    2192             foreach (glob($root . '/SimplePie/*.php') as $file)
    2193             {
    2194                 if (($mtime = filemtime($file)) > $time)
    2195                 {
    2196                     $time = $mtime;
    2197                 }
    2198             }
    2199             return $time;
    2200         }
    2201         elseif (file_exists(dirname(__FILE__) . '/Core.php'))
    2202         {
    2203             return filemtime(dirname(__FILE__) . '/Core.php');
    2204         }
    2205 
    2206         return filemtime(__FILE__);
    2207     }
    2208 
    2209     /**
    2210      * Format debugging information
    2211      */
    2212     public static function debug(&$sp)
    2213     {
    2214         $info = 'SimplePie ' . SIMPLEPIE_VERSION . ' Build ' . SIMPLEPIE_BUILD . "\n";
    2215         $info .= 'PHP ' . PHP_VERSION . "\n";
    2216         if ($sp->error() !== null)
    2217         {
    2218             $info .= 'Error occurred: ' . $sp->error() . "\n";
    2219         }
    2220         else
    2221         {
    2222             $info .= "No error found.\n";
    2223         }
    2224         $info .= "Extensions:\n";
    2225         $extensions = array('pcre', 'curl', 'zlib', 'mbstring', 'iconv', 'xmlreader', 'xml');
    2226         foreach ($extensions as $ext)
    2227         {
    2228             if (extension_loaded($ext))
    2229             {
    2230                 $info .= "    $ext loaded\n";
    2231                 switch ($ext)
    2232                 {
    2233                     case 'pcre':
    2234                         $info .= '      Version ' . PCRE_VERSION . "\n";
    2235                         break;
    2236                     case 'curl':
    2237                         $version = curl_version();
    2238                         $info .= '      Version ' . $version['version'] . "\n";
    2239                         break;
    2240                     case 'mbstring':
    2241                         $info .= '      Overloading: ' . mb_get_info('func_overload') . "\n";
    2242                         break;
    2243                     case 'iconv':
    2244                         $info .= '      Version ' . ICONV_VERSION . "\n";
    2245                         break;
    2246                     case 'xml':
    2247                         $info .= '      Version ' . LIBXML_DOTTED_VERSION . "\n";
    2248                         break;
    2249                 }
    2250             }
    2251             else
    2252             {
    2253                 $info .= "    $ext not loaded\n";
    2254             }
    2255         }
    2256         return $info;
    2257     }
    2258 
    2259     public static function silence_errors($num, $str)
    2260     {
    2261         // No-op
    2262     }
    2263 
    2264     /**
    2265      * Sanitize a URL by removing HTTP credentials.
    2266      * @param string $url the URL to sanitize.
    2267      * @return string the same URL without HTTP credentials.
    2268      */
    2269     public static function url_remove_credentials($url)
    2270     {
    2271         return preg_replace('#^(https?://)[^/:@]+:[^/:@]+@#i', '$1', $url);
    2272     }
     2069END;
     2070        echo $body;
     2071    }
     2072
     2073    /**
     2074     * Get the SimplePie build timestamp
     2075     *
     2076     * Uses the git index if it exists, otherwise uses the modification time
     2077     * of the newest file.
     2078     */
     2079    public static function get_build()
     2080    {
     2081        if (static::$SIMPLEPIE_BUILD !== null) {
     2082            return static::$SIMPLEPIE_BUILD;
     2083        }
     2084
     2085        $root = dirname(__FILE__, 2);
     2086        if (file_exists($root . '/.git/index')) {
     2087            static::$SIMPLEPIE_BUILD = filemtime($root . '/.git/index');
     2088
     2089            return static::$SIMPLEPIE_BUILD;
     2090        } elseif (file_exists($root . '/SimplePie')) {
     2091            $time = 0;
     2092            foreach (glob($root . '/SimplePie/*.php') as $file) {
     2093                if (($mtime = filemtime($file)) > $time) {
     2094                    $time = $mtime;
     2095                }
     2096            }
     2097            static::$SIMPLEPIE_BUILD = $time;
     2098
     2099            return static::$SIMPLEPIE_BUILD;
     2100        } elseif (file_exists(dirname(__FILE__) . '/Core.php')) {
     2101            static::$SIMPLEPIE_BUILD = filemtime(dirname(__FILE__) . '/Core.php');
     2102
     2103            return static::$SIMPLEPIE_BUILD;
     2104        }
     2105
     2106        static::$SIMPLEPIE_BUILD = filemtime(__FILE__);
     2107
     2108        return static::$SIMPLEPIE_BUILD;
     2109    }
     2110
     2111    /**
     2112     * Get the default user agent string
     2113     *
     2114     * @return string
     2115     */
     2116    public static function get_default_useragent()
     2117    {
     2118        return \SimplePie\SimplePie::NAME . '/' . \SimplePie\SimplePie::VERSION . ' (Feed Parser; ' . \SimplePie\SimplePie::URL . '; Allow like Gecko) Build/' . static::get_build();
     2119    }
     2120
     2121    /**
     2122     * Format debugging information
     2123     */
     2124    public static function debug(&$sp)
     2125    {
     2126        $info = 'SimplePie ' . \SimplePie\SimplePie::VERSION . ' Build ' . static::get_build() . "\n";
     2127        $info .= 'PHP ' . PHP_VERSION . "\n";
     2128        if ($sp->error() !== null) {
     2129            $info .= 'Error occurred: ' . $sp->error() . "\n";
     2130        } else {
     2131            $info .= "No error found.\n";
     2132        }
     2133        $info .= "Extensions:\n";
     2134        $extensions = ['pcre', 'curl', 'zlib', 'mbstring', 'iconv', 'xmlreader', 'xml'];
     2135        foreach ($extensions as $ext) {
     2136            if (extension_loaded($ext)) {
     2137                $info .= "    $ext loaded\n";
     2138                switch ($ext) {
     2139                    case 'pcre':
     2140                        $info .= '      Version ' . PCRE_VERSION . "\n";
     2141                        break;
     2142                    case 'curl':
     2143                        $version = curl_version();
     2144                        $info .= '      Version ' . $version['version'] . "\n";
     2145                        break;
     2146                    case 'mbstring':
     2147                        $info .= '      Overloading: ' . mb_get_info('func_overload') . "\n";
     2148                        break;
     2149                    case 'iconv':
     2150                        $info .= '      Version ' . ICONV_VERSION . "\n";
     2151                        break;
     2152                    case 'xml':
     2153                        $info .= '      Version ' . LIBXML_DOTTED_VERSION . "\n";
     2154                        break;
     2155                }
     2156            } else {
     2157                $info .= "    $ext not loaded\n";
     2158            }
     2159        }
     2160        return $info;
     2161    }
     2162
     2163    public static function silence_errors($num, $str)
     2164    {
     2165        // No-op
     2166    }
     2167
     2168    /**
     2169     * Sanitize a URL by removing HTTP credentials.
     2170     * @param string $url the URL to sanitize.
     2171     * @return string the same URL without HTTP credentials.
     2172     */
     2173    public static function url_remove_credentials($url)
     2174    {
     2175        return preg_replace('#^(https?://)[^/:@]+:[^/:@]+@#i', '$1', $url);
     2176    }
    22732177}
     2178
     2179class_alias('SimplePie\Misc', 'SimplePie_Misc', false);
  • trunk/src/wp-includes/SimplePie/src/Net/IPv6.php

    r47733 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie\Net;
    4446
    4547/**
     
    5658 * @author Sam Sneddon <geoffers@gmail.com>
    5759 */
    58 class SimplePie_Net_IPv6
     60class IPv6
    5961{
    60     /**
    61      * Uncompresses an IPv6 address
    62      *
    63      * RFC 4291 allows you to compress concecutive zero pieces in an address to
    64      * '::'. This method expects a valid IPv6 address and expands the '::' to
    65      * the required number of zero pieces.
    66      *
    67      * Example:  FF01::101   ->  FF01:0:0:0:0:0:0:101
    68      *           ::1         ->  0:0:0:0:0:0:0:1
    69      *
    70      * @author Alexander Merz <alexander.merz@web.de>
    71      * @author elfrink at introweb dot nl
    72      * @author Josh Peck <jmp at joshpeck dot org>
    73      * @copyright 2003-2005 The PHP Group
    74      * @license http://www.opensource.org/licenses/bsd-license.php
    75      * @param string $ip An IPv6 address
    76      * @return string The uncompressed IPv6 address
    77      */
    78     public static function uncompress($ip)
    79     {
    80         $c1 = -1;
    81         $c2 = -1;
    82         if (substr_count($ip, '::') === 1)
    83         {
    84             list($ip1, $ip2) = explode('::', $ip);
    85             if ($ip1 === '')
    86             {
    87                 $c1 = -1;
    88             }
    89             else
    90             {
    91                 $c1 = substr_count($ip1, ':');
    92             }
    93             if ($ip2 === '')
    94             {
    95                 $c2 = -1;
    96             }
    97             else
    98             {
    99                 $c2 = substr_count($ip2, ':');
    100             }
    101             if (strpos($ip2, '.') !== false)
    102             {
    103                 $c2++;
    104             }
    105             // ::
    106             if ($c1 === -1 && $c2 === -1)
    107             {
    108                 $ip = '0:0:0:0:0:0:0:0';
    109             }
    110             // ::xxx
    111             else if ($c1 === -1)
    112             {
    113                 $fill = str_repeat('0:', 7 - $c2);
    114                 $ip = str_replace('::', $fill, $ip);
    115             }
    116             // xxx::
    117             else if ($c2 === -1)
    118             {
    119                 $fill = str_repeat(':0', 7 - $c1);
    120                 $ip = str_replace('::', $fill, $ip);
    121             }
    122             // xxx::xxx
    123             else
    124             {
    125                 $fill = ':' . str_repeat('0:', 6 - $c2 - $c1);
    126                 $ip = str_replace('::', $fill, $ip);
    127             }
    128         }
    129         return $ip;
    130     }
    131 
    132     /**
    133      * Compresses an IPv6 address
    134      *
    135      * RFC 4291 allows you to compress concecutive zero pieces in an address to
    136      * '::'. This method expects a valid IPv6 address and compresses consecutive
    137      * zero pieces to '::'.
    138      *
    139      * Example:  FF01:0:0:0:0:0:0:101   ->  FF01::101
    140      *           0:0:0:0:0:0:0:1        ->  ::1
    141      *
    142      * @see uncompress()
    143      * @param string $ip An IPv6 address
    144      * @return string The compressed IPv6 address
    145      */
    146     public static function compress($ip)
    147     {
    148         // Prepare the IP to be compressed
    149         $ip = self::uncompress($ip);
    150         $ip_parts = self::split_v6_v4($ip);
    151 
    152         // Replace all leading zeros
    153         $ip_parts[0] = preg_replace('/(^|:)0+([0-9])/', '\1\2', $ip_parts[0]);
    154 
    155         // Find bunches of zeros
    156         if (preg_match_all('/(?:^|:)(?:0(?::|$))+/', $ip_parts[0], $matches, PREG_OFFSET_CAPTURE))
    157         {
    158             $max = 0;
    159             $pos = null;
    160             foreach ($matches[0] as $match)
    161             {
    162                 if (strlen($match[0]) > $max)
    163                 {
    164                     $max = strlen($match[0]);
    165                     $pos = $match[1];
    166                 }
    167             }
    168 
    169             $ip_parts[0] = substr_replace($ip_parts[0], '::', $pos, $max);
    170         }
    171 
    172         if ($ip_parts[1] !== '')
    173         {
    174             return implode(':', $ip_parts);
    175         }
    176 
    177         return $ip_parts[0];
    178     }
    179 
    180     /**
    181      * Splits an IPv6 address into the IPv6 and IPv4 representation parts
    182      *
    183      * RFC 4291 allows you to represent the last two parts of an IPv6 address
    184      * using the standard IPv4 representation
    185      *
    186      * Example:  0:0:0:0:0:0:13.1.68.3
    187      *           0:0:0:0:0:FFFF:129.144.52.38
    188      *
    189      * @param string $ip An IPv6 address
    190      * @return array [0] contains the IPv6 represented part, and [1] the IPv4 represented part
    191      */
    192     private static function split_v6_v4($ip)
    193     {
    194         if (strpos($ip, '.') !== false)
    195         {
    196             $pos = strrpos($ip, ':');
    197             $ipv6_part = substr($ip, 0, $pos);
    198             $ipv4_part = substr($ip, $pos + 1);
    199             return array($ipv6_part, $ipv4_part);
    200         }
    201 
    202         return array($ip, '');
    203     }
    204 
    205     /**
    206      * Checks an IPv6 address
    207      *
    208      * Checks if the given IP is a valid IPv6 address
    209      *
    210      * @param string $ip An IPv6 address
    211      * @return bool true if $ip is a valid IPv6 address
    212      */
    213     public static function check_ipv6($ip)
    214     {
    215         $ip = self::uncompress($ip);
    216         list($ipv6, $ipv4) = self::split_v6_v4($ip);
    217         $ipv6 = explode(':', $ipv6);
    218         $ipv4 = explode('.', $ipv4);
    219         if (count($ipv6) === 8 && count($ipv4) === 1 || count($ipv6) === 6 && count($ipv4) === 4)
    220         {
    221             foreach ($ipv6 as $ipv6_part)
    222             {
    223                 // The section can't be empty
    224                 if ($ipv6_part === '')
    225                     return false;
    226 
    227                 // Nor can it be over four characters
    228                 if (strlen($ipv6_part) > 4)
    229                     return false;
    230 
    231                 // Remove leading zeros (this is safe because of the above)
    232                 $ipv6_part = ltrim($ipv6_part, '0');
    233                 if ($ipv6_part === '')
    234                     $ipv6_part = '0';
    235 
    236                 // Check the value is valid
    237                 $value = hexdec($ipv6_part);
    238                 if (dechex($value) !== strtolower($ipv6_part) || $value < 0 || $value > 0xFFFF)
    239                     return false;
    240             }
    241             if (count($ipv4) === 4)
    242             {
    243                 foreach ($ipv4 as $ipv4_part)
    244                 {
    245                     $value = (int) $ipv4_part;
    246                     if ((string) $value !== $ipv4_part || $value < 0 || $value > 0xFF)
    247                         return false;
    248                 }
    249             }
    250             return true;
    251         }
    252 
    253         return false;
    254     }
    255 
    256     /**
    257      * Checks if the given IP is a valid IPv6 address
    258      *
    259      * @codeCoverageIgnore
    260      * @deprecated Use {@see SimplePie_Net_IPv6::check_ipv6()} instead
    261      * @see check_ipv6
    262      * @param string $ip An IPv6 address
    263      * @return bool true if $ip is a valid IPv6 address
    264      */
    265     public static function checkIPv6($ip)
    266     {
    267         return self::check_ipv6($ip);
    268     }
     62    /**
     63     * Uncompresses an IPv6 address
     64     *
     65     * RFC 4291 allows you to compress concecutive zero pieces in an address to
     66     * '::'. This method expects a valid IPv6 address and expands the '::' to
     67     * the required number of zero pieces.
     68     *
     69     * Example:  FF01::101   ->  FF01:0:0:0:0:0:0:101
     70     *           ::1         ->  0:0:0:0:0:0:0:1
     71     *
     72     * @author Alexander Merz <alexander.merz@web.de>
     73     * @author elfrink at introweb dot nl
     74     * @author Josh Peck <jmp at joshpeck dot org>
     75     * @copyright 2003-2005 The PHP Group
     76     * @license http://www.opensource.org/licenses/bsd-license.php
     77     * @param string $ip An IPv6 address
     78     * @return string The uncompressed IPv6 address
     79     */
     80    public static function uncompress($ip)
     81    {
     82        $c1 = -1;
     83        $c2 = -1;
     84        if (substr_count($ip, '::') === 1) {
     85            [$ip1, $ip2] = explode('::', $ip);
     86            if ($ip1 === '') {
     87                $c1 = -1;
     88            } else {
     89                $c1 = substr_count($ip1, ':');
     90            }
     91            if ($ip2 === '') {
     92                $c2 = -1;
     93            } else {
     94                $c2 = substr_count($ip2, ':');
     95            }
     96            if (strpos($ip2, '.') !== false) {
     97                $c2++;
     98            }
     99            // ::
     100            if ($c1 === -1 && $c2 === -1) {
     101                $ip = '0:0:0:0:0:0:0:0';
     102            }
     103            // ::xxx
     104            elseif ($c1 === -1) {
     105                $fill = str_repeat('0:', 7 - $c2);
     106                $ip = str_replace('::', $fill, $ip);
     107            }
     108            // xxx::
     109            elseif ($c2 === -1) {
     110                $fill = str_repeat(':0', 7 - $c1);
     111                $ip = str_replace('::', $fill, $ip);
     112            }
     113            // xxx::xxx
     114            else {
     115                $fill = ':' . str_repeat('0:', 6 - $c2 - $c1);
     116                $ip = str_replace('::', $fill, $ip);
     117            }
     118        }
     119        return $ip;
     120    }
     121
     122    /**
     123     * Compresses an IPv6 address
     124     *
     125     * RFC 4291 allows you to compress concecutive zero pieces in an address to
     126     * '::'. This method expects a valid IPv6 address and compresses consecutive
     127     * zero pieces to '::'.
     128     *
     129     * Example:  FF01:0:0:0:0:0:0:101   ->  FF01::101
     130     *           0:0:0:0:0:0:0:1        ->  ::1
     131     *
     132     * @see uncompress()
     133     * @param string $ip An IPv6 address
     134     * @return string The compressed IPv6 address
     135     */
     136    public static function compress($ip)
     137    {
     138        // Prepare the IP to be compressed
     139        $ip = self::uncompress($ip);
     140        $ip_parts = self::split_v6_v4($ip);
     141
     142        // Replace all leading zeros
     143        $ip_parts[0] = preg_replace('/(^|:)0+([0-9])/', '\1\2', $ip_parts[0]);
     144
     145        // Find bunches of zeros
     146        if (preg_match_all('/(?:^|:)(?:0(?::|$))+/', $ip_parts[0], $matches, PREG_OFFSET_CAPTURE)) {
     147            $max = 0;
     148            $pos = null;
     149            foreach ($matches[0] as $match) {
     150                if (strlen($match[0]) > $max) {
     151                    $max = strlen($match[0]);
     152                    $pos = $match[1];
     153                }
     154            }
     155
     156            $ip_parts[0] = substr_replace($ip_parts[0], '::', $pos, $max);
     157        }
     158
     159        if ($ip_parts[1] !== '') {
     160            return implode(':', $ip_parts);
     161        }
     162
     163        return $ip_parts[0];
     164    }
     165
     166    /**
     167     * Splits an IPv6 address into the IPv6 and IPv4 representation parts
     168     *
     169     * RFC 4291 allows you to represent the last two parts of an IPv6 address
     170     * using the standard IPv4 representation
     171     *
     172     * Example:  0:0:0:0:0:0:13.1.68.3
     173     *           0:0:0:0:0:FFFF:129.144.52.38
     174     *
     175     * @param string $ip An IPv6 address
     176     * @return array [0] contains the IPv6 represented part, and [1] the IPv4 represented part
     177     */
     178    private static function split_v6_v4($ip)
     179    {
     180        if (strpos($ip, '.') !== false) {
     181            $pos = strrpos($ip, ':');
     182            $ipv6_part = substr($ip, 0, $pos);
     183            $ipv4_part = substr($ip, $pos + 1);
     184            return [$ipv6_part, $ipv4_part];
     185        }
     186
     187        return [$ip, ''];
     188    }
     189
     190    /**
     191     * Checks an IPv6 address
     192     *
     193     * Checks if the given IP is a valid IPv6 address
     194     *
     195     * @param string $ip An IPv6 address
     196     * @return bool true if $ip is a valid IPv6 address
     197     */
     198    public static function check_ipv6($ip)
     199    {
     200        $ip = self::uncompress($ip);
     201        [$ipv6, $ipv4] = self::split_v6_v4($ip);
     202        $ipv6 = explode(':', $ipv6);
     203        $ipv4 = explode('.', $ipv4);
     204        if (count($ipv6) === 8 && count($ipv4) === 1 || count($ipv6) === 6 && count($ipv4) === 4) {
     205            foreach ($ipv6 as $ipv6_part) {
     206                // The section can't be empty
     207                if ($ipv6_part === '') {
     208                    return false;
     209                }
     210
     211                // Nor can it be over four characters
     212                if (strlen($ipv6_part) > 4) {
     213                    return false;
     214                }
     215
     216                // Remove leading zeros (this is safe because of the above)
     217                $ipv6_part = ltrim($ipv6_part, '0');
     218                if ($ipv6_part === '') {
     219                    $ipv6_part = '0';
     220                }
     221
     222                // Check the value is valid
     223                $value = hexdec($ipv6_part);
     224                if (dechex($value) !== strtolower($ipv6_part) || $value < 0 || $value > 0xFFFF) {
     225                    return false;
     226                }
     227            }
     228            if (count($ipv4) === 4) {
     229                foreach ($ipv4 as $ipv4_part) {
     230                    $value = (int) $ipv4_part;
     231                    if ((string) $value !== $ipv4_part || $value < 0 || $value > 0xFF) {
     232                        return false;
     233                    }
     234                }
     235            }
     236            return true;
     237        }
     238
     239        return false;
     240    }
     241
     242    /**
     243     * Checks if the given IP is a valid IPv6 address
     244     *
     245     * @codeCoverageIgnore
     246     * @deprecated Use {@see IPv6::check_ipv6()} instead
     247     * @see check_ipv6
     248     * @param string $ip An IPv6 address
     249     * @return bool true if $ip is a valid IPv6 address
     250     */
     251    public static function checkIPv6($ip)
     252    {
     253        return self::check_ipv6($ip);
     254    }
    269255}
     256
     257class_alias('SimplePie\Net\IPv6', 'SimplePie_Net_IPv6');
  • trunk/src/wp-includes/SimplePie/src/Parse/Date.php

    r47733 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie\Parse;
    4446
    4547/**
     
    4951 * @subpackage Parsing
    5052 */
    51 class SimplePie_Parse_Date
     53class Date
    5254{
    53     /**
    54      * Input data
    55      *
    56      * @access protected
    57      * @var string
    58      */
    59     var $date;
    60 
    61     /**
    62      * List of days, calendar day name => ordinal day number in the week
    63      *
    64      * @access protected
    65      * @var array
    66      */
    67     var $day = array(
    68         // English
    69         'mon' => 1,
    70         'monday' => 1,
    71         'tue' => 2,
    72         'tuesday' => 2,
    73         'wed' => 3,
    74         'wednesday' => 3,
    75         'thu' => 4,
    76         'thursday' => 4,
    77         'fri' => 5,
    78         'friday' => 5,
    79         'sat' => 6,
    80         'saturday' => 6,
    81         'sun' => 7,
    82         'sunday' => 7,
    83         // Dutch
    84         'maandag' => 1,
    85         'dinsdag' => 2,
    86         'woensdag' => 3,
    87         'donderdag' => 4,
    88         'vrijdag' => 5,
    89         'zaterdag' => 6,
    90         'zondag' => 7,
    91         // French
    92         'lundi' => 1,
    93         'mardi' => 2,
    94         'mercredi' => 3,
    95         'jeudi' => 4,
    96         'vendredi' => 5,
    97         'samedi' => 6,
    98         'dimanche' => 7,
    99         // German
    100         'montag' => 1,
    101         'mo' => 1,
    102         'dienstag' => 2,
    103         'di' => 2,
    104         'mittwoch' => 3,
    105         'mi' => 3,
    106         'donnerstag' => 4,
    107         'do' => 4,
    108         'freitag' => 5,
    109         'fr' => 5,
    110         'samstag' => 6,
    111         'sa' => 6,
    112         'sonnabend' => 6,
    113         // AFAIK no short form for sonnabend
    114         'so' => 7,
    115         'sonntag' => 7,
    116         // Italian
    117         'lunedì' => 1,
    118         'martedì' => 2,
    119         'mercoledì' => 3,
    120         'giovedì' => 4,
    121         'venerdì' => 5,
    122         'sabato' => 6,
    123         'domenica' => 7,
    124         // Spanish
    125         'lunes' => 1,
    126         'martes' => 2,
    127         'miércoles' => 3,
    128         'jueves' => 4,
    129         'viernes' => 5,
    130         'sábado' => 6,
    131         'domingo' => 7,
    132         // Finnish
    133         'maanantai' => 1,
    134         'tiistai' => 2,
    135         'keskiviikko' => 3,
    136         'torstai' => 4,
    137         'perjantai' => 5,
    138         'lauantai' => 6,
    139         'sunnuntai' => 7,
    140         // Hungarian
    141         'hétfő' => 1,
    142         'kedd' => 2,
    143         'szerda' => 3,
    144         'csütörtok' => 4,
    145         'péntek' => 5,
    146         'szombat' => 6,
    147         'vasárnap' => 7,
    148         // Greek
    149         'Δευ' => 1,
    150         'Τρι' => 2,
    151         'Τετ' => 3,
    152         'Πεμ' => 4,
    153         'Παρ' => 5,
    154         'Σαβ' => 6,
    155         'Κυρ' => 7,
    156         // Russian
    157         'Пн.' => 1,
    158         'Вт.' => 2,
    159         'Ср.' => 3,
    160         'Чт.' => 4,
    161         'Пт.' => 5,
    162         'Сб.' => 6,
    163         'Вс.' => 7,
    164     );
    165 
    166     /**
    167      * List of months, calendar month name => calendar month number
    168      *
    169      * @access protected
    170      * @var array
    171      */
    172     var $month = array(
    173         // English
    174         'jan' => 1,
    175         'january' => 1,
    176         'feb' => 2,
    177         'february' => 2,
    178         'mar' => 3,
    179         'march' => 3,
    180         'apr' => 4,
    181         'april' => 4,
    182         'may' => 5,
    183         // No long form of May
    184         'jun' => 6,
    185         'june' => 6,
    186         'jul' => 7,
    187         'july' => 7,
    188         'aug' => 8,
    189         'august' => 8,
    190         'sep' => 9,
    191         'september' => 9,
    192         'oct' => 10,
    193         'october' => 10,
    194         'nov' => 11,
    195         'november' => 11,
    196         'dec' => 12,
    197         'december' => 12,
    198         // Dutch
    199         'januari' => 1,
    200         'februari' => 2,
    201         'maart' => 3,
    202         'april' => 4,
    203         'mei' => 5,
    204         'juni' => 6,
    205         'juli' => 7,
    206         'augustus' => 8,
    207         'september' => 9,
    208         'oktober' => 10,
    209         'november' => 11,
    210         'december' => 12,
    211         // French
    212         'janvier' => 1,
    213         'février' => 2,
    214         'mars' => 3,
    215         'avril' => 4,
    216         'mai' => 5,
    217         'juin' => 6,
    218         'juillet' => 7,
    219         'août' => 8,
    220         'septembre' => 9,
    221         'octobre' => 10,
    222         'novembre' => 11,
    223         'décembre' => 12,
    224         // German
    225         'januar' => 1,
    226         'jan' => 1,
    227         'februar' => 2,
    228         'feb' => 2,
    229         'märz' => 3,
    230         'mär' => 3,
    231         'april' => 4,
    232         'apr' => 4,
    233         'mai' => 5, // no short form for may
    234         'juni' => 6,
    235         'jun' => 6,
    236         'juli' => 7,
    237         'jul' => 7,
    238         'august' => 8,
    239         'aug' => 8,
    240         'september' => 9,
    241         'sep' => 9,
    242         'oktober' => 10,
    243         'okt' => 10,
    244         'november' => 11,
    245         'nov' => 11,
    246         'dezember' => 12,
    247         'dez' => 12,
    248         // Italian
    249         'gennaio' => 1,
    250         'febbraio' => 2,
    251         'marzo' => 3,
    252         'aprile' => 4,
    253         'maggio' => 5,
    254         'giugno' => 6,
    255         'luglio' => 7,
    256         'agosto' => 8,
    257         'settembre' => 9,
    258         'ottobre' => 10,
    259         'novembre' => 11,
    260         'dicembre' => 12,
    261         // Spanish
    262         'enero' => 1,
    263         'febrero' => 2,
    264         'marzo' => 3,
    265         'abril' => 4,
    266         'mayo' => 5,
    267         'junio' => 6,
    268         'julio' => 7,
    269         'agosto' => 8,
    270         'septiembre' => 9,
    271         'setiembre' => 9,
    272         'octubre' => 10,
    273         'noviembre' => 11,
    274         'diciembre' => 12,
    275         // Finnish
    276         'tammikuu' => 1,
    277         'helmikuu' => 2,
    278         'maaliskuu' => 3,
    279         'huhtikuu' => 4,
    280         'toukokuu' => 5,
    281         'kesäkuu' => 6,
    282         'heinäkuu' => 7,
    283         'elokuu' => 8,
    284         'suuskuu' => 9,
    285         'lokakuu' => 10,
    286         'marras' => 11,
    287         'joulukuu' => 12,
    288         // Hungarian
    289         'január' => 1,
    290         'február' => 2,
    291         'március' => 3,
    292         'április' => 4,
    293         'május' => 5,
    294         'június' => 6,
    295         'július' => 7,
    296         'augusztus' => 8,
    297         'szeptember' => 9,
    298         'október' => 10,
    299         'november' => 11,
    300         'december' => 12,
    301         // Greek
    302         'Ιαν' => 1,
    303         'Φεβ' => 2,
    304         'Μάώ' => 3,
    305         'Μαώ' => 3,
    306         'Απρ' => 4,
    307         'Μάι' => 5,
    308         'Μαϊ' => 5,
    309         'Μαι' => 5,
    310         'Ιούν' => 6,
    311         'Ιον' => 6,
    312         'Ιούλ' => 7,
    313         'Ιολ' => 7,
    314         'Αύγ' => 8,
    315         'Αυγ' => 8,
    316         'Σεπ' => 9,
    317         'Οκτ' => 10,
    318         'Νοέ' => 11,
    319         'Δεκ' => 12,       
    320         // Russian
    321         'Янв' => 1,
    322         'января' => 1,
    323         'Фев' => 2,
    324         'февраля' => 2,
    325         'Мар' => 3,
    326         'марта' => 3,
    327         'Апр' => 4,
    328         'апреля' => 4,
    329         'Май' => 5,
    330         'мая' => 5,
    331         'Июн' => 6,
    332         'июня' => 6,
    333         'Июл' => 7,
    334         'июля' => 7,
    335         'Авг' => 8,
    336         'августа' => 8,
    337         'Сен' => 9,
    338         'сентября' => 9,
    339         'Окт' => 10,
    340         'октября' => 10,
    341         'Ноя' => 11,
    342         'ноября' => 11,
    343         'Дек' => 12,
    344         'декабря' => 12,
    345 
    346     );
    347 
    348     /**
    349      * List of timezones, abbreviation => offset from UTC
    350      *
    351      * @access protected
    352      * @var array
    353      */
    354     var $timezone = array(
    355         'ACDT' => 37800,
    356         'ACIT' => 28800,
    357         'ACST' => 34200,
    358         'ACT' => -18000,
    359         'ACWDT' => 35100,
    360         'ACWST' => 31500,
    361         'AEDT' => 39600,
    362         'AEST' => 36000,
    363         'AFT' => 16200,
    364         'AKDT' => -28800,
    365         'AKST' => -32400,
    366         'AMDT' => 18000,
    367         'AMT' => -14400,
    368         'ANAST' => 46800,
    369         'ANAT' => 43200,
    370         'ART' => -10800,
    371         'AZOST' => -3600,
    372         'AZST' => 18000,
    373         'AZT' => 14400,
    374         'BIOT' => 21600,
    375         'BIT' => -43200,
    376         'BOT' => -14400,
    377         'BRST' => -7200,
    378         'BRT' => -10800,
    379         'BST' => 3600,
    380         'BTT' => 21600,
    381         'CAST' => 18000,
    382         'CAT' => 7200,
    383         'CCT' => 23400,
    384         'CDT' => -18000,
    385         'CEDT' => 7200,
    386         'CEST' => 7200,
    387         'CET' => 3600,
    388         'CGST' => -7200,
    389         'CGT' => -10800,
    390         'CHADT' => 49500,
    391         'CHAST' => 45900,
    392         'CIST' => -28800,
    393         'CKT' => -36000,
    394         'CLDT' => -10800,
    395         'CLST' => -14400,
    396         'COT' => -18000,
    397         'CST' => -21600,
    398         'CVT' => -3600,
    399         'CXT' => 25200,
    400         'DAVT' => 25200,
    401         'DTAT' => 36000,
    402         'EADT' => -18000,
    403         'EAST' => -21600,
    404         'EAT' => 10800,
    405         'ECT' => -18000,
    406         'EDT' => -14400,
    407         'EEST' => 10800,
    408         'EET' => 7200,
    409         'EGT' => -3600,
    410         'EKST' => 21600,
    411         'EST' => -18000,
    412         'FJT' => 43200,
    413         'FKDT' => -10800,
    414         'FKST' => -14400,
    415         'FNT' => -7200,
    416         'GALT' => -21600,
    417         'GEDT' => 14400,
    418         'GEST' => 10800,
    419         'GFT' => -10800,
    420         'GILT' => 43200,
    421         'GIT' => -32400,
    422         'GST' => 14400,
    423         'GST' => -7200,
    424         'GYT' => -14400,
    425         'HAA' => -10800,
    426         'HAC' => -18000,
    427         'HADT' => -32400,
    428         'HAE' => -14400,
    429         'HAP' => -25200,
    430         'HAR' => -21600,
    431         'HAST' => -36000,
    432         'HAT' => -9000,
    433         'HAY' => -28800,
    434         'HKST' => 28800,
    435         'HMT' => 18000,
    436         'HNA' => -14400,
    437         'HNC' => -21600,
    438         'HNE' => -18000,
    439         'HNP' => -28800,
    440         'HNR' => -25200,
    441         'HNT' => -12600,
    442         'HNY' => -32400,
    443         'IRDT' => 16200,
    444         'IRKST' => 32400,
    445         'IRKT' => 28800,
    446         'IRST' => 12600,
    447         'JFDT' => -10800,
    448         'JFST' => -14400,
    449         'JST' => 32400,
    450         'KGST' => 21600,
    451         'KGT' => 18000,
    452         'KOST' => 39600,
    453         'KOVST' => 28800,
    454         'KOVT' => 25200,
    455         'KRAST' => 28800,
    456         'KRAT' => 25200,
    457         'KST' => 32400,
    458         'LHDT' => 39600,
    459         'LHST' => 37800,
    460         'LINT' => 50400,
    461         'LKT' => 21600,
    462         'MAGST' => 43200,
    463         'MAGT' => 39600,
    464         'MAWT' => 21600,
    465         'MDT' => -21600,
    466         'MESZ' => 7200,
    467         'MEZ' => 3600,
    468         'MHT' => 43200,
    469         'MIT' => -34200,
    470         'MNST' => 32400,
    471         'MSDT' => 14400,
    472         'MSST' => 10800,
    473         'MST' => -25200,
    474         'MUT' => 14400,
    475         'MVT' => 18000,
    476         'MYT' => 28800,
    477         'NCT' => 39600,
    478         'NDT' => -9000,
    479         'NFT' => 41400,
    480         'NMIT' => 36000,
    481         'NOVST' => 25200,
    482         'NOVT' => 21600,
    483         'NPT' => 20700,
    484         'NRT' => 43200,
    485         'NST' => -12600,
    486         'NUT' => -39600,
    487         'NZDT' => 46800,
    488         'NZST' => 43200,
    489         'OMSST' => 25200,
    490         'OMST' => 21600,
    491         'PDT' => -25200,
    492         'PET' => -18000,
    493         'PETST' => 46800,
    494         'PETT' => 43200,
    495         'PGT' => 36000,
    496         'PHOT' => 46800,
    497         'PHT' => 28800,
    498         'PKT' => 18000,
    499         'PMDT' => -7200,
    500         'PMST' => -10800,
    501         'PONT' => 39600,
    502         'PST' => -28800,
    503         'PWT' => 32400,
    504         'PYST' => -10800,
    505         'PYT' => -14400,
    506         'RET' => 14400,
    507         'ROTT' => -10800,
    508         'SAMST' => 18000,
    509         'SAMT' => 14400,
    510         'SAST' => 7200,
    511         'SBT' => 39600,
    512         'SCDT' => 46800,
    513         'SCST' => 43200,
    514         'SCT' => 14400,
    515         'SEST' => 3600,
    516         'SGT' => 28800,
    517         'SIT' => 28800,
    518         'SRT' => -10800,
    519         'SST' => -39600,
    520         'SYST' => 10800,
    521         'SYT' => 7200,
    522         'TFT' => 18000,
    523         'THAT' => -36000,
    524         'TJT' => 18000,
    525         'TKT' => -36000,
    526         'TMT' => 18000,
    527         'TOT' => 46800,
    528         'TPT' => 32400,
    529         'TRUT' => 36000,
    530         'TVT' => 43200,
    531         'TWT' => 28800,
    532         'UYST' => -7200,
    533         'UYT' => -10800,
    534         'UZT' => 18000,
    535         'VET' => -14400,
    536         'VLAST' => 39600,
    537         'VLAT' => 36000,
    538         'VOST' => 21600,
    539         'VUT' => 39600,
    540         'WAST' => 7200,
    541         'WAT' => 3600,
    542         'WDT' => 32400,
    543         'WEST' => 3600,
    544         'WFT' => 43200,
    545         'WIB' => 25200,
    546         'WIT' => 32400,
    547         'WITA' => 28800,
    548         'WKST' => 18000,
    549         'WST' => 28800,
    550         'YAKST' => 36000,
    551         'YAKT' => 32400,
    552         'YAPT' => 36000,
    553         'YEKST' => 21600,
    554         'YEKT' => 18000,
    555     );
    556 
    557     /**
    558      * Cached PCRE for SimplePie_Parse_Date::$day
    559      *
    560      * @access protected
    561      * @var string
    562      */
    563     var $day_pcre;
    564 
    565     /**
    566      * Cached PCRE for SimplePie_Parse_Date::$month
    567      *
    568      * @access protected
    569      * @var string
    570      */
    571     var $month_pcre;
    572 
    573     /**
    574      * Array of user-added callback methods
    575      *
    576      * @access private
    577      * @var array
    578      */
    579     var $built_in = array();
    580 
    581     /**
    582      * Array of user-added callback methods
    583      *
    584      * @access private
    585      * @var array
    586      */
    587     var $user = array();
    588 
    589     /**
    590      * Create new SimplePie_Parse_Date object, and set self::day_pcre,
    591      * self::month_pcre, and self::built_in
    592      *
    593      * @access private
    594      */
    595     public function __construct()
    596     {
    597         $this->day_pcre = '(' . implode('|', array_keys($this->day)) . ')';
    598         $this->month_pcre = '(' . implode('|', array_keys($this->month)) . ')';
    599 
    600         static $cache;
    601         if (!isset($cache[get_class($this)]))
    602         {
    603             $all_methods = get_class_methods($this);
    604 
    605             foreach ($all_methods as $method)
    606             {
    607                 if (strtolower(substr($method, 0, 5)) === 'date_')
    608                 {
    609                     $cache[get_class($this)][] = $method;
    610                 }
    611             }
    612         }
    613 
    614         foreach ($cache[get_class($this)] as $method)
    615         {
    616             $this->built_in[] = $method;
    617         }
    618     }
    619 
    620     /**
    621      * Get the object
    622      *
    623      * @access public
    624      */
    625     public static function get()
    626     {
    627         static $object;
    628         if (!$object)
    629         {
    630             $object = new SimplePie_Parse_Date;
    631         }
    632         return $object;
    633     }
    634 
    635     /**
    636      * Parse a date
    637      *
    638      * @final
    639      * @access public
    640      * @param string $date Date to parse
    641      * @return int Timestamp corresponding to date string, or false on failure
    642      */
    643     public function parse($date)
    644     {
    645         foreach ($this->user as $method)
    646         {
    647             if (($returned = call_user_func($method, $date)) !== false)
    648             {
    649                 return $returned;
    650             }
    651         }
    652 
    653         foreach ($this->built_in as $method)
    654         {
    655             if (($returned = call_user_func(array($this, $method), $date)) !== false)
    656             {
    657                 return $returned;
    658             }
    659         }
    660 
    661         return false;
    662     }
    663 
    664     /**
    665      * Add a callback method to parse a date
    666      *
    667      * @final
    668      * @access public
    669      * @param callback $callback
    670      */
    671     public function add_callback($callback)
    672     {
    673         if (is_callable($callback))
    674         {
    675             $this->user[] = $callback;
    676         }
    677         else
    678         {
    679             trigger_error('User-supplied function must be a valid callback', E_USER_WARNING);
    680         }
    681     }
    682 
    683     /**
    684      * Parse a superset of W3C-DTF (allows hyphens and colons to be omitted, as
    685      * well as allowing any of upper or lower case "T", horizontal tabs, or
    686      * spaces to be used as the time separator (including more than one))
    687      *
    688      * @access protected
    689      * @return int Timestamp
    690      */
    691     public function date_w3cdtf($date)
    692     {
    693         static $pcre;
    694         if (!$pcre)
    695         {
    696             $year = '([0-9]{4})';
    697             $month = $day = $hour = $minute = $second = '([0-9]{2})';
    698             $decimal = '([0-9]*)';
    699             $zone = '(?:(Z)|([+\-])([0-9]{1,2}):?([0-9]{1,2}))';
    700             $pcre = '/^' . $year . '(?:-?' . $month . '(?:-?' . $day . '(?:[Tt\x09\x20]+' . $hour . '(?::?' . $minute . '(?::?' . $second . '(?:.' . $decimal . ')?)?)?' . $zone . ')?)?)?$/';
    701         }
    702         if (preg_match($pcre, $date, $match))
    703         {
    704             /*
    705             Capturing subpatterns:
    706             1: Year
    707             2: Month
    708             3: Day
    709             4: Hour
    710             5: Minute
    711             6: Second
    712             7: Decimal fraction of a second
    713             8: Zulu
    714             9: Timezone ±
    715             10: Timezone hours
    716             11: Timezone minutes
    717             */
    718 
    719             // Fill in empty matches
    720             for ($i = count($match); $i <= 3; $i++)
    721             {
    722                 $match[$i] = '1';
    723             }
    724 
    725             for ($i = count($match); $i <= 7; $i++)
    726             {
    727                 $match[$i] = '0';
    728             }
    729 
    730             // Numeric timezone
    731             if (isset($match[9]) && $match[9] !== '')
    732             {
    733                 $timezone = $match[10] * 3600;
    734                 $timezone += $match[11] * 60;
    735                 if ($match[9] === '-')
    736                 {
    737                     $timezone = 0 - $timezone;
    738                 }
    739             }
    740             else
    741             {
    742                 $timezone = 0;
    743             }
    744 
    745             // Convert the number of seconds to an integer, taking decimals into account
    746             $second = round((int)$match[6] + (int)$match[7] / (10 ** strlen($match[7])));
    747 
    748             return gmmktime($match[4], $match[5], $second, $match[2], $match[3], $match[1]) - $timezone;
    749         }
    750 
    751         return false;
    752     }
    753 
    754     /**
    755      * Remove RFC822 comments
    756      *
    757      * @access protected
    758      * @param string $data Data to strip comments from
    759      * @return string Comment stripped string
    760      */
    761     public function remove_rfc2822_comments($string)
    762     {
    763         $string = (string) $string;
    764         $position = 0;
    765         $length = strlen($string);
    766         $depth = 0;
    767 
    768         $output = '';
    769 
    770         while ($position < $length && ($pos = strpos($string, '(', $position)) !== false)
    771         {
    772             $output .= substr($string, $position, $pos - $position);
    773             $position = $pos + 1;
    774             if ($pos === 0 || $string[$pos - 1] !== '\\')
    775             {
    776                 $depth++;
    777                 while ($depth && $position < $length)
    778                 {
    779                     $position += strcspn($string, '()', $position);
    780                     if ($string[$position - 1] === '\\')
    781                     {
    782                         $position++;
    783                         continue;
    784                     }
    785                     elseif (isset($string[$position]))
    786                     {
    787                         switch ($string[$position])
    788                         {
    789                             case '(':
    790                                 $depth++;
    791                                 break;
    792 
    793                             case ')':
    794                                 $depth--;
    795                                 break;
    796                         }
    797                         $position++;
    798                     }
    799                     else
    800                     {
    801                         break;
    802                     }
    803                 }
    804             }
    805             else
    806             {
    807                 $output .= '(';
    808             }
    809         }
    810         $output .= substr($string, $position);
    811 
    812         return $output;
    813     }
    814 
    815     /**
    816      * Parse RFC2822's date format
    817      *
    818      * @access protected
    819      * @return int Timestamp
    820      */
    821     public function date_rfc2822($date)
    822     {
    823         static $pcre;
    824         if (!$pcre)
    825         {
    826             $wsp = '[\x09\x20]';
    827             $fws = '(?:' . $wsp . '+|' . $wsp . '*(?:\x0D\x0A' . $wsp . '+)+)';
    828             $optional_fws = $fws . '?';
    829             $day_name = $this->day_pcre;
    830             $month = $this->month_pcre;
    831             $day = '([0-9]{1,2})';
    832             $hour = $minute = $second = '([0-9]{2})';
    833             $year = '([0-9]{2,4})';
    834             $num_zone = '([+\-])([0-9]{2})([0-9]{2})';
    835             $character_zone = '([A-Z]{1,5})';
    836             $zone = '(?:' . $num_zone . '|' . $character_zone . ')';
    837             $pcre = '/(?:' . $optional_fws . $day_name . $optional_fws . ',)?' . $optional_fws . $day . $fws . $month . $fws . $year . $fws . $hour . $optional_fws . ':' . $optional_fws . $minute . '(?:' . $optional_fws . ':' . $optional_fws . $second . ')?' . $fws . $zone . '/i';
    838         }
    839         if (preg_match($pcre, $this->remove_rfc2822_comments($date), $match))
    840         {
    841             /*
    842             Capturing subpatterns:
    843             1: Day name
    844             2: Day
    845             3: Month
    846             4: Year
    847             5: Hour
    848             6: Minute
    849             7: Second
    850             8: Timezone ±
    851             9: Timezone hours
    852             10: Timezone minutes
    853             11: Alphabetic timezone
    854             */
    855 
    856             // Find the month number
    857             $month = $this->month[strtolower($match[3])];
    858 
    859             // Numeric timezone
    860             if ($match[8] !== '')
    861             {
    862                 $timezone = $match[9] * 3600;
    863                 $timezone += $match[10] * 60;
    864                 if ($match[8] === '-')
    865                 {
    866                     $timezone = 0 - $timezone;
    867                 }
    868             }
    869             // Character timezone
    870             elseif (isset($this->timezone[strtoupper($match[11])]))
    871             {
    872                 $timezone = $this->timezone[strtoupper($match[11])];
    873             }
    874             // Assume everything else to be -0000
    875             else
    876             {
    877                 $timezone = 0;
    878             }
    879 
    880             // Deal with 2/3 digit years
    881             if ($match[4] < 50)
    882             {
    883                 $match[4] += 2000;
    884             }
    885             elseif ($match[4] < 1000)
    886             {
    887                 $match[4] += 1900;
    888             }
    889 
    890             // Second is optional, if it is empty set it to zero
    891             if ($match[7] !== '')
    892             {
    893                 $second = $match[7];
    894             }
    895             else
    896             {
    897                 $second = 0;
    898             }
    899 
    900             return gmmktime($match[5], $match[6], $second, $month, $match[2], $match[4]) - $timezone;
    901         }
    902 
    903         return false;
    904     }
    905 
    906     /**
    907      * Parse RFC850's date format
    908      *
    909      * @access protected
    910      * @return int Timestamp
    911      */
    912     public function date_rfc850($date)
    913     {
    914         static $pcre;
    915         if (!$pcre)
    916         {
    917             $space = '[\x09\x20]+';
    918             $day_name = $this->day_pcre;
    919             $month = $this->month_pcre;
    920             $day = '([0-9]{1,2})';
    921             $year = $hour = $minute = $second = '([0-9]{2})';
    922             $zone = '([A-Z]{1,5})';
    923             $pcre = '/^' . $day_name . ',' . $space . $day . '-' . $month . '-' . $year . $space . $hour . ':' . $minute . ':' . $second . $space . $zone . '$/i';
    924         }
    925         if (preg_match($pcre, $date, $match))
    926         {
    927             /*
    928             Capturing subpatterns:
    929             1: Day name
    930             2: Day
    931             3: Month
    932             4: Year
    933             5: Hour
    934             6: Minute
    935             7: Second
    936             8: Timezone
    937             */
    938 
    939             // Month
    940             $month = $this->month[strtolower($match[3])];
    941 
    942             // Character timezone
    943             if (isset($this->timezone[strtoupper($match[8])]))
    944             {
    945                 $timezone = $this->timezone[strtoupper($match[8])];
    946             }
    947             // Assume everything else to be -0000
    948             else
    949             {
    950                 $timezone = 0;
    951             }
    952 
    953             // Deal with 2 digit year
    954             if ($match[4] < 50)
    955             {
    956                 $match[4] += 2000;
    957             }
    958             else
    959             {
    960                 $match[4] += 1900;
    961             }
    962 
    963             return gmmktime($match[5], $match[6], $match[7], $month, $match[2], $match[4]) - $timezone;
    964         }
    965 
    966         return false;
    967     }
    968 
    969     /**
    970      * Parse C99's asctime()'s date format
    971      *
    972      * @access protected
    973      * @return int Timestamp
    974      */
    975     public function date_asctime($date)
    976     {
    977         static $pcre;
    978         if (!$pcre)
    979         {
    980             $space = '[\x09\x20]+';
    981             $wday_name = $this->day_pcre;
    982             $mon_name = $this->month_pcre;
    983             $day = '([0-9]{1,2})';
    984             $hour = $sec = $min = '([0-9]{2})';
    985             $year = '([0-9]{4})';
    986             $terminator = '\x0A?\x00?';
    987             $pcre = '/^' . $wday_name . $space . $mon_name . $space . $day . $space . $hour . ':' . $min . ':' . $sec . $space . $year . $terminator . '$/i';
    988         }
    989         if (preg_match($pcre, $date, $match))
    990         {
    991             /*
    992             Capturing subpatterns:
    993             1: Day name
    994             2: Month
    995             3: Day
    996             4: Hour
    997             5: Minute
    998             6: Second
    999             7: Year
    1000             */
    1001 
    1002             $month = $this->month[strtolower($match[2])];
    1003             return gmmktime($match[4], $match[5], $match[6], $month, $match[3], $match[7]);
    1004         }
    1005 
    1006         return false;
    1007     }
    1008 
    1009     /**
    1010      * Parse dates using strtotime()
    1011      *
    1012      * @access protected
    1013      * @return int Timestamp
    1014      */
    1015     public function date_strtotime($date)
    1016     {
    1017         $strtotime = strtotime($date);
    1018         if ($strtotime === -1 || $strtotime === false)
    1019         {
    1020             return false;
    1021         }
    1022 
    1023         return $strtotime;
    1024     }
     55    /**
     56     * Input data
     57     *
     58     * @access protected
     59     * @var string
     60     */
     61    public $date;
     62
     63    /**
     64     * List of days, calendar day name => ordinal day number in the week
     65     *
     66     * @access protected
     67     * @var array
     68     */
     69    public $day = [
     70        // English
     71        'mon' => 1,
     72        'monday' => 1,
     73        'tue' => 2,
     74        'tuesday' => 2,
     75        'wed' => 3,
     76        'wednesday' => 3,
     77        'thu' => 4,
     78        'thursday' => 4,
     79        'fri' => 5,
     80        'friday' => 5,
     81        'sat' => 6,
     82        'saturday' => 6,
     83        'sun' => 7,
     84        'sunday' => 7,
     85        // Dutch
     86        'maandag' => 1,
     87        'dinsdag' => 2,
     88        'woensdag' => 3,
     89        'donderdag' => 4,
     90        'vrijdag' => 5,
     91        'zaterdag' => 6,
     92        'zondag' => 7,
     93        // French
     94        'lundi' => 1,
     95        'mardi' => 2,
     96        'mercredi' => 3,
     97        'jeudi' => 4,
     98        'vendredi' => 5,
     99        'samedi' => 6,
     100        'dimanche' => 7,
     101        // German
     102        'montag' => 1,
     103        'mo' => 1,
     104        'dienstag' => 2,
     105        'di' => 2,
     106        'mittwoch' => 3,
     107        'mi' => 3,
     108        'donnerstag' => 4,
     109        'do' => 4,
     110        'freitag' => 5,
     111        'fr' => 5,
     112        'samstag' => 6,
     113        'sa' => 6,
     114        'sonnabend' => 6,
     115        // AFAIK no short form for sonnabend
     116        'so' => 7,
     117        'sonntag' => 7,
     118        // Italian
     119        'lunedì' => 1,
     120        'martedì' => 2,
     121        'mercoledì' => 3,
     122        'giovedì' => 4,
     123        'venerdì' => 5,
     124        'sabato' => 6,
     125        'domenica' => 7,
     126        // Spanish
     127        'lunes' => 1,
     128        'martes' => 2,
     129        'miércoles' => 3,
     130        'jueves' => 4,
     131        'viernes' => 5,
     132        'sábado' => 6,
     133        'domingo' => 7,
     134        // Finnish
     135        'maanantai' => 1,
     136        'tiistai' => 2,
     137        'keskiviikko' => 3,
     138        'torstai' => 4,
     139        'perjantai' => 5,
     140        'lauantai' => 6,
     141        'sunnuntai' => 7,
     142        // Hungarian
     143        'hétfő' => 1,
     144        'kedd' => 2,
     145        'szerda' => 3,
     146        'csütörtok' => 4,
     147        'péntek' => 5,
     148        'szombat' => 6,
     149        'vasárnap' => 7,
     150        // Greek
     151        'Δευ' => 1,
     152        'Τρι' => 2,
     153        'Τετ' => 3,
     154        'Πεμ' => 4,
     155        'Παρ' => 5,
     156        'Σαβ' => 6,
     157        'Κυρ' => 7,
     158        // Russian
     159        'Пн.' => 1,
     160        'Вт.' => 2,
     161        'Ср.' => 3,
     162        'Чт.' => 4,
     163        'Пт.' => 5,
     164        'Сб.' => 6,
     165        'Вс.' => 7,
     166    ];
     167
     168    /**
     169     * List of months, calendar month name => calendar month number
     170     *
     171     * @access protected
     172     * @var array
     173     */
     174    public $month = [
     175        // English
     176        'jan' => 1,
     177        'january' => 1,
     178        'feb' => 2,
     179        'february' => 2,
     180        'mar' => 3,
     181        'march' => 3,
     182        'apr' => 4,
     183        'april' => 4,
     184        'may' => 5,
     185        // No long form of May
     186        'jun' => 6,
     187        'june' => 6,
     188        'jul' => 7,
     189        'july' => 7,
     190        'aug' => 8,
     191        'august' => 8,
     192        'sep' => 9,
     193        'september' => 9,
     194        'oct' => 10,
     195        'october' => 10,
     196        'nov' => 11,
     197        'november' => 11,
     198        'dec' => 12,
     199        'december' => 12,
     200        // Dutch
     201        'januari' => 1,
     202        'februari' => 2,
     203        'maart' => 3,
     204        'april' => 4,
     205        'mei' => 5,
     206        'juni' => 6,
     207        'juli' => 7,
     208        'augustus' => 8,
     209        'september' => 9,
     210        'oktober' => 10,
     211        'november' => 11,
     212        'december' => 12,
     213        // French
     214        'janvier' => 1,
     215        'février' => 2,
     216        'mars' => 3,
     217        'avril' => 4,
     218        'mai' => 5,
     219        'juin' => 6,
     220        'juillet' => 7,
     221        'août' => 8,
     222        'septembre' => 9,
     223        'octobre' => 10,
     224        'novembre' => 11,
     225        'décembre' => 12,
     226        // German
     227        'januar' => 1,
     228        'jan' => 1,
     229        'februar' => 2,
     230        'feb' => 2,
     231        'märz' => 3,
     232        'mär' => 3,
     233        'april' => 4,
     234        'apr' => 4,
     235        'mai' => 5, // no short form for may
     236        'juni' => 6,
     237        'jun' => 6,
     238        'juli' => 7,
     239        'jul' => 7,
     240        'august' => 8,
     241        'aug' => 8,
     242        'september' => 9,
     243        'sep' => 9,
     244        'oktober' => 10,
     245        'okt' => 10,
     246        'november' => 11,
     247        'nov' => 11,
     248        'dezember' => 12,
     249        'dez' => 12,
     250        // Italian
     251        'gennaio' => 1,
     252        'febbraio' => 2,
     253        'marzo' => 3,
     254        'aprile' => 4,
     255        'maggio' => 5,
     256        'giugno' => 6,
     257        'luglio' => 7,
     258        'agosto' => 8,
     259        'settembre' => 9,
     260        'ottobre' => 10,
     261        'novembre' => 11,
     262        'dicembre' => 12,
     263        // Spanish
     264        'enero' => 1,
     265        'febrero' => 2,
     266        'marzo' => 3,
     267        'abril' => 4,
     268        'mayo' => 5,
     269        'junio' => 6,
     270        'julio' => 7,
     271        'agosto' => 8,
     272        'septiembre' => 9,
     273        'setiembre' => 9,
     274        'octubre' => 10,
     275        'noviembre' => 11,
     276        'diciembre' => 12,
     277        // Finnish
     278        'tammikuu' => 1,
     279        'helmikuu' => 2,
     280        'maaliskuu' => 3,
     281        'huhtikuu' => 4,
     282        'toukokuu' => 5,
     283        'kesäkuu' => 6,
     284        'heinäkuu' => 7,
     285        'elokuu' => 8,
     286        'suuskuu' => 9,
     287        'lokakuu' => 10,
     288        'marras' => 11,
     289        'joulukuu' => 12,
     290        // Hungarian
     291        'január' => 1,
     292        'február' => 2,
     293        'március' => 3,
     294        'április' => 4,
     295        'május' => 5,
     296        'június' => 6,
     297        'július' => 7,
     298        'augusztus' => 8,
     299        'szeptember' => 9,
     300        'október' => 10,
     301        'november' => 11,
     302        'december' => 12,
     303        // Greek
     304        'Ιαν' => 1,
     305        'Φεβ' => 2,
     306        'Μάώ' => 3,
     307        'Μαώ' => 3,
     308        'Απρ' => 4,
     309        'Μάι' => 5,
     310        'Μαϊ' => 5,
     311        'Μαι' => 5,
     312        'Ιούν' => 6,
     313        'Ιον' => 6,
     314        'Ιούλ' => 7,
     315        'Ιολ' => 7,
     316        'Αύγ' => 8,
     317        'Αυγ' => 8,
     318        'Σεπ' => 9,
     319        'Οκτ' => 10,
     320        'Νοέ' => 11,
     321        'Δεκ' => 12,
     322        // Russian
     323        'Янв' => 1,
     324        'января' => 1,
     325        'Фев' => 2,
     326        'февраля' => 2,
     327        'Мар' => 3,
     328        'марта' => 3,
     329        'Апр' => 4,
     330        'апреля' => 4,
     331        'Май' => 5,
     332        'мая' => 5,
     333        'Июн' => 6,
     334        'июня' => 6,
     335        'Июл' => 7,
     336        'июля' => 7,
     337        'Авг' => 8,
     338        'августа' => 8,
     339        'Сен' => 9,
     340        'сентября' => 9,
     341        'Окт' => 10,
     342        'октября' => 10,
     343        'Ноя' => 11,
     344        'ноября' => 11,
     345        'Дек' => 12,
     346        'декабря' => 12,
     347
     348    ];
     349
     350    /**
     351     * List of timezones, abbreviation => offset from UTC
     352     *
     353     * @access protected
     354     * @var array
     355     */
     356    public $timezone = [
     357        'ACDT' => 37800,
     358        'ACIT' => 28800,
     359        'ACST' => 34200,
     360        'ACT' => -18000,
     361        'ACWDT' => 35100,
     362        'ACWST' => 31500,
     363        'AEDT' => 39600,
     364        'AEST' => 36000,
     365        'AFT' => 16200,
     366        'AKDT' => -28800,
     367        'AKST' => -32400,
     368        'AMDT' => 18000,
     369        'AMT' => -14400,
     370        'ANAST' => 46800,
     371        'ANAT' => 43200,
     372        'ART' => -10800,
     373        'AZOST' => -3600,
     374        'AZST' => 18000,
     375        'AZT' => 14400,
     376        'BIOT' => 21600,
     377        'BIT' => -43200,
     378        'BOT' => -14400,
     379        'BRST' => -7200,
     380        'BRT' => -10800,
     381        'BST' => 3600,
     382        'BTT' => 21600,
     383        'CAST' => 18000,
     384        'CAT' => 7200,
     385        'CCT' => 23400,
     386        'CDT' => -18000,
     387        'CEDT' => 7200,
     388        'CEST' => 7200,
     389        'CET' => 3600,
     390        'CGST' => -7200,
     391        'CGT' => -10800,
     392        'CHADT' => 49500,
     393        'CHAST' => 45900,
     394        'CIST' => -28800,
     395        'CKT' => -36000,
     396        'CLDT' => -10800,
     397        'CLST' => -14400,
     398        'COT' => -18000,
     399        'CST' => -21600,
     400        'CVT' => -3600,
     401        'CXT' => 25200,
     402        'DAVT' => 25200,
     403        'DTAT' => 36000,
     404        'EADT' => -18000,
     405        'EAST' => -21600,
     406        'EAT' => 10800,
     407        'ECT' => -18000,
     408        'EDT' => -14400,
     409        'EEST' => 10800,
     410        'EET' => 7200,
     411        'EGT' => -3600,
     412        'EKST' => 21600,
     413        'EST' => -18000,
     414        'FJT' => 43200,
     415        'FKDT' => -10800,
     416        'FKST' => -14400,
     417        'FNT' => -7200,
     418        'GALT' => -21600,
     419        'GEDT' => 14400,
     420        'GEST' => 10800,
     421        'GFT' => -10800,
     422        'GILT' => 43200,
     423        'GIT' => -32400,
     424        'GST' => 14400,
     425        'GST' => -7200,
     426        'GYT' => -14400,
     427        'HAA' => -10800,
     428        'HAC' => -18000,
     429        'HADT' => -32400,
     430        'HAE' => -14400,
     431        'HAP' => -25200,
     432        'HAR' => -21600,
     433        'HAST' => -36000,
     434        'HAT' => -9000,
     435        'HAY' => -28800,
     436        'HKST' => 28800,
     437        'HMT' => 18000,
     438        'HNA' => -14400,
     439        'HNC' => -21600,
     440        'HNE' => -18000,
     441        'HNP' => -28800,
     442        'HNR' => -25200,
     443        'HNT' => -12600,
     444        'HNY' => -32400,
     445        'IRDT' => 16200,
     446        'IRKST' => 32400,
     447        'IRKT' => 28800,
     448        'IRST' => 12600,
     449        'JFDT' => -10800,
     450        'JFST' => -14400,
     451        'JST' => 32400,
     452        'KGST' => 21600,
     453        'KGT' => 18000,
     454        'KOST' => 39600,
     455        'KOVST' => 28800,
     456        'KOVT' => 25200,
     457        'KRAST' => 28800,
     458        'KRAT' => 25200,
     459        'KST' => 32400,
     460        'LHDT' => 39600,
     461        'LHST' => 37800,
     462        'LINT' => 50400,
     463        'LKT' => 21600,
     464        'MAGST' => 43200,
     465        'MAGT' => 39600,
     466        'MAWT' => 21600,
     467        'MDT' => -21600,
     468        'MESZ' => 7200,
     469        'MEZ' => 3600,
     470        'MHT' => 43200,
     471        'MIT' => -34200,
     472        'MNST' => 32400,
     473        'MSDT' => 14400,
     474        'MSST' => 10800,
     475        'MST' => -25200,
     476        'MUT' => 14400,
     477        'MVT' => 18000,
     478        'MYT' => 28800,
     479        'NCT' => 39600,
     480        'NDT' => -9000,
     481        'NFT' => 41400,
     482        'NMIT' => 36000,
     483        'NOVST' => 25200,
     484        'NOVT' => 21600,
     485        'NPT' => 20700,
     486        'NRT' => 43200,
     487        'NST' => -12600,
     488        'NUT' => -39600,
     489        'NZDT' => 46800,
     490        'NZST' => 43200,
     491        'OMSST' => 25200,
     492        'OMST' => 21600,
     493        'PDT' => -25200,
     494        'PET' => -18000,
     495        'PETST' => 46800,
     496        'PETT' => 43200,
     497        'PGT' => 36000,
     498        'PHOT' => 46800,
     499        'PHT' => 28800,
     500        'PKT' => 18000,
     501        'PMDT' => -7200,
     502        'PMST' => -10800,
     503        'PONT' => 39600,
     504        'PST' => -28800,
     505        'PWT' => 32400,
     506        'PYST' => -10800,
     507        'PYT' => -14400,
     508        'RET' => 14400,
     509        'ROTT' => -10800,
     510        'SAMST' => 18000,
     511        'SAMT' => 14400,
     512        'SAST' => 7200,
     513        'SBT' => 39600,
     514        'SCDT' => 46800,
     515        'SCST' => 43200,
     516        'SCT' => 14400,
     517        'SEST' => 3600,
     518        'SGT' => 28800,
     519        'SIT' => 28800,
     520        'SRT' => -10800,
     521        'SST' => -39600,
     522        'SYST' => 10800,
     523        'SYT' => 7200,
     524        'TFT' => 18000,
     525        'THAT' => -36000,
     526        'TJT' => 18000,
     527        'TKT' => -36000,
     528        'TMT' => 18000,
     529        'TOT' => 46800,
     530        'TPT' => 32400,
     531        'TRUT' => 36000,
     532        'TVT' => 43200,
     533        'TWT' => 28800,
     534        'UYST' => -7200,
     535        'UYT' => -10800,
     536        'UZT' => 18000,
     537        'VET' => -14400,
     538        'VLAST' => 39600,
     539        'VLAT' => 36000,
     540        'VOST' => 21600,
     541        'VUT' => 39600,
     542        'WAST' => 7200,
     543        'WAT' => 3600,
     544        'WDT' => 32400,
     545        'WEST' => 3600,
     546        'WFT' => 43200,
     547        'WIB' => 25200,
     548        'WIT' => 32400,
     549        'WITA' => 28800,
     550        'WKST' => 18000,
     551        'WST' => 28800,
     552        'YAKST' => 36000,
     553        'YAKT' => 32400,
     554        'YAPT' => 36000,
     555        'YEKST' => 21600,
     556        'YEKT' => 18000,
     557    ];
     558
     559    /**
     560     * Cached PCRE for Date::$day
     561     *
     562     * @access protected
     563     * @var string
     564     */
     565    public $day_pcre;
     566
     567    /**
     568     * Cached PCRE for Date::$month
     569     *
     570     * @access protected
     571     * @var string
     572     */
     573    public $month_pcre;
     574
     575    /**
     576     * Array of user-added callback methods
     577     *
     578     * @access private
     579     * @var array
     580     */
     581    public $built_in = [];
     582
     583    /**
     584     * Array of user-added callback methods
     585     *
     586     * @access private
     587     * @var array
     588     */
     589    public $user = [];
     590
     591    /**
     592     * Create new Date object, and set self::day_pcre,
     593     * self::month_pcre, and self::built_in
     594     *
     595     * @access private
     596     */
     597    public function __construct()
     598    {
     599        $this->day_pcre = '(' . implode('|', array_keys($this->day)) . ')';
     600        $this->month_pcre = '(' . implode('|', array_keys($this->month)) . ')';
     601
     602        static $cache;
     603        if (!isset($cache[get_class($this)])) {
     604            $all_methods = get_class_methods($this);
     605
     606            foreach ($all_methods as $method) {
     607                if (strtolower(substr($method, 0, 5)) === 'date_') {
     608                    $cache[get_class($this)][] = $method;
     609                }
     610            }
     611        }
     612
     613        foreach ($cache[get_class($this)] as $method) {
     614            $this->built_in[] = $method;
     615        }
     616    }
     617
     618    /**
     619     * Get the object
     620     *
     621     * @access public
     622     */
     623    public static function get()
     624    {
     625        static $object;
     626        if (!$object) {
     627            $object = new Date();
     628        }
     629        return $object;
     630    }
     631
     632    /**
     633     * Parse a date
     634     *
     635     * @final
     636     * @access public
     637     * @param string $date Date to parse
     638     * @return int Timestamp corresponding to date string, or false on failure
     639     */
     640    public function parse($date)
     641    {
     642        foreach ($this->user as $method) {
     643            if (($returned = call_user_func($method, $date)) !== false) {
     644                return $returned;
     645            }
     646        }
     647
     648        foreach ($this->built_in as $method) {
     649            if (($returned = call_user_func([$this, $method], $date)) !== false) {
     650                return $returned;
     651            }
     652        }
     653
     654        return false;
     655    }
     656
     657    /**
     658     * Add a callback method to parse a date
     659     *
     660     * @final
     661     * @access public
     662     * @param callable $callback
     663     */
     664    public function add_callback($callback)
     665    {
     666        if (is_callable($callback)) {
     667            $this->user[] = $callback;
     668        } else {
     669            trigger_error('User-supplied function must be a valid callback', E_USER_WARNING);
     670        }
     671    }
     672
     673    /**
     674     * Parse a superset of W3C-DTF (allows hyphens and colons to be omitted, as
     675     * well as allowing any of upper or lower case "T", horizontal tabs, or
     676     * spaces to be used as the time separator (including more than one))
     677     *
     678     * @access protected
     679     * @return int Timestamp
     680     */
     681    public function date_w3cdtf($date)
     682    {
     683        $pcre = <<<'PCRE'
     684            /
     685            ^
     686            (?P<year>[0-9]{4})
     687            (?:
     688                -?
     689                (?P<month>[0-9]{2})
     690                (?:
     691                    -?
     692                    (?P<day>[0-9]{2})
     693                    (?:
     694                        [Tt\x09\x20]+
     695                        (?P<hour>[0-9]{2})
     696                        (?:
     697                            :?
     698                            (?P<minute>[0-9]{2})
     699                            (?:
     700                                :?
     701                                (?P<second>[0-9]{2})
     702                                (?:
     703                                    .
     704                                    (?P<second_fraction>[0-9]*)
     705                                )?
     706                            )?
     707                        )?
     708                        (?:
     709                            (?P<zulu>Z)
     710                            |   (?P<tz_sign>[+\-])
     711                                (?P<tz_hour>[0-9]{1,2})
     712                                :?
     713                                (?P<tz_minute>[0-9]{1,2})
     714                        )
     715                    )?
     716                )?
     717            )?
     718            $
     719            /x
     720PCRE;
     721        if (preg_match($pcre, $date, $match)) {
     722            // Fill in empty matches and convert to proper types.
     723            $year = (int) $match['year'];
     724            $month = isset($match['month']) ? (int) $match['month'] : 1;
     725            $day = isset($match['day']) ? (int) $match['day'] : 1;
     726            $hour = isset($match['hour']) ? (int) $match['hour'] : 0;
     727            $minute = isset($match['minute']) ? (int) $match['minute'] : 0;
     728            $second = isset($match['second']) ? (int) $match['second'] : 0;
     729            $second_fraction = isset($match['second_fraction']) ? ((int) $match['second_fraction']) / (10 ** strlen($match['second_fraction'])) : 0;
     730            $tz_sign = ($match['tz_sign'] ?? '') === '-' ? -1 : 1;
     731            $tz_hour = isset($match['tz_hour']) ? (int) $match['tz_hour'] : 0;
     732            $tz_minute = isset($match['tz_minute']) ? (int) $match['tz_minute'] : 0;
     733
     734            // Numeric timezone
     735            $timezone = $tz_hour * 3600;
     736            $timezone += $tz_minute * 60;
     737            $timezone *= $tz_sign;
     738
     739            // Convert the number of seconds to an integer, taking decimals into account
     740            $second = (int) round($second + $second_fraction);
     741
     742            return gmmktime($hour, $minute, $second, $month, $day, $year) - $timezone;
     743        }
     744
     745        return false;
     746    }
     747
     748    /**
     749     * Remove RFC822 comments
     750     *
     751     * @access protected
     752     * @param string $data Data to strip comments from
     753     * @return string Comment stripped string
     754     */
     755    public function remove_rfc2822_comments($string)
     756    {
     757        $string = (string) $string;
     758        $position = 0;
     759        $length = strlen($string);
     760        $depth = 0;
     761
     762        $output = '';
     763
     764        while ($position < $length && ($pos = strpos($string, '(', $position)) !== false) {
     765            $output .= substr($string, $position, $pos - $position);
     766            $position = $pos + 1;
     767            if ($pos === 0 || $string[$pos - 1] !== '\\') {
     768                $depth++;
     769                while ($depth && $position < $length) {
     770                    $position += strcspn($string, '()', $position);
     771                    if ($string[$position - 1] === '\\') {
     772                        $position++;
     773                        continue;
     774                    } elseif (isset($string[$position])) {
     775                        switch ($string[$position]) {
     776                            case '(':
     777                                $depth++;
     778                                break;
     779
     780                            case ')':
     781                                $depth--;
     782                                break;
     783                        }
     784                        $position++;
     785                    } else {
     786                        break;
     787                    }
     788                }
     789            } else {
     790                $output .= '(';
     791            }
     792        }
     793        $output .= substr($string, $position);
     794
     795        return $output;
     796    }
     797
     798    /**
     799     * Parse RFC2822's date format
     800     *
     801     * @access protected
     802     * @return int Timestamp
     803     */
     804    public function date_rfc2822($date)
     805    {
     806        static $pcre;
     807        if (!$pcre) {
     808            $wsp = '[\x09\x20]';
     809            $fws = '(?:' . $wsp . '+|' . $wsp . '*(?:\x0D\x0A' . $wsp . '+)+)';
     810            $optional_fws = $fws . '?';
     811            $day_name = $this->day_pcre;
     812            $month = $this->month_pcre;
     813            $day = '([0-9]{1,2})';
     814            $hour = $minute = $second = '([0-9]{2})';
     815            $year = '([0-9]{2,4})';
     816            $num_zone = '([+\-])([0-9]{2})([0-9]{2})';
     817            $character_zone = '([A-Z]{1,5})';
     818            $zone = '(?:' . $num_zone . '|' . $character_zone . ')';
     819            $pcre = '/(?:' . $optional_fws . $day_name . $optional_fws . ',)?' . $optional_fws . $day . $fws . $month . $fws . $year . $fws . $hour . $optional_fws . ':' . $optional_fws . $minute . '(?:' . $optional_fws . ':' . $optional_fws . $second . ')?' . $fws . $zone . '/i';
     820        }
     821        if (preg_match($pcre, $this->remove_rfc2822_comments($date), $match)) {
     822            /*
     823            Capturing subpatterns:
     824            1: Day name
     825            2: Day
     826            3: Month
     827            4: Year
     828            5: Hour
     829            6: Minute
     830            7: Second
     831            8: Timezone ±
     832            9: Timezone hours
     833            10: Timezone minutes
     834            11: Alphabetic timezone
     835            */
     836
     837            // Find the month number
     838            $month = $this->month[strtolower($match[3])];
     839
     840            // Numeric timezone
     841            if ($match[8] !== '') {
     842                $timezone = $match[9] * 3600;
     843                $timezone += $match[10] * 60;
     844                if ($match[8] === '-') {
     845                    $timezone = 0 - $timezone;
     846                }
     847            }
     848            // Character timezone
     849            elseif (isset($this->timezone[strtoupper($match[11])])) {
     850                $timezone = $this->timezone[strtoupper($match[11])];
     851            }
     852            // Assume everything else to be -0000
     853            else {
     854                $timezone = 0;
     855            }
     856
     857            // Deal with 2/3 digit years
     858            if ($match[4] < 50) {
     859                $match[4] += 2000;
     860            } elseif ($match[4] < 1000) {
     861                $match[4] += 1900;
     862            }
     863
     864            // Second is optional, if it is empty set it to zero
     865            if ($match[7] !== '') {
     866                $second = $match[7];
     867            } else {
     868                $second = 0;
     869            }
     870
     871            return gmmktime(intval($match[5]), intval($match[6]), intval($second), intval($month), intval($match[2]), intval($match[4])) - $timezone;
     872        }
     873
     874        return false;
     875    }
     876
     877    /**
     878     * Parse RFC850's date format
     879     *
     880     * @access protected
     881     * @return int Timestamp
     882     */
     883    public function date_rfc850($date)
     884    {
     885        static $pcre;
     886        if (!$pcre) {
     887            $space = '[\x09\x20]+';
     888            $day_name = $this->day_pcre;
     889            $month = $this->month_pcre;
     890            $day = '([0-9]{1,2})';
     891            $year = $hour = $minute = $second = '([0-9]{2})';
     892            $zone = '([A-Z]{1,5})';
     893            $pcre = '/^' . $day_name . ',' . $space . $day . '-' . $month . '-' . $year . $space . $hour . ':' . $minute . ':' . $second . $space . $zone . '$/i';
     894        }
     895        if (preg_match($pcre, $date, $match)) {
     896            /*
     897            Capturing subpatterns:
     898            1: Day name
     899            2: Day
     900            3: Month
     901            4: Year
     902            5: Hour
     903            6: Minute
     904            7: Second
     905            8: Timezone
     906            */
     907
     908            // Month
     909            $month = $this->month[strtolower($match[3])];
     910
     911            // Character timezone
     912            if (isset($this->timezone[strtoupper($match[8])])) {
     913                $timezone = $this->timezone[strtoupper($match[8])];
     914            }
     915            // Assume everything else to be -0000
     916            else {
     917                $timezone = 0;
     918            }
     919
     920            // Deal with 2 digit year
     921            if ($match[4] < 50) {
     922                $match[4] += 2000;
     923            } else {
     924                $match[4] += 1900;
     925            }
     926
     927            return gmmktime($match[5], $match[6], $match[7], $month, $match[2], $match[4]) - $timezone;
     928        }
     929
     930        return false;
     931    }
     932
     933    /**
     934     * Parse C99's asctime()'s date format
     935     *
     936     * @access protected
     937     * @return int Timestamp
     938     */
     939    public function date_asctime($date)
     940    {
     941        static $pcre;
     942        if (!$pcre) {
     943            $space = '[\x09\x20]+';
     944            $wday_name = $this->day_pcre;
     945            $mon_name = $this->month_pcre;
     946            $day = '([0-9]{1,2})';
     947            $hour = $sec = $min = '([0-9]{2})';
     948            $year = '([0-9]{4})';
     949            $terminator = '\x0A?\x00?';
     950            $pcre = '/^' . $wday_name . $space . $mon_name . $space . $day . $space . $hour . ':' . $min . ':' . $sec . $space . $year . $terminator . '$/i';
     951        }
     952        if (preg_match($pcre, $date, $match)) {
     953            /*
     954            Capturing subpatterns:
     955            1: Day name
     956            2: Month
     957            3: Day
     958            4: Hour
     959            5: Minute
     960            6: Second
     961            7: Year
     962            */
     963
     964            $month = $this->month[strtolower($match[2])];
     965            return gmmktime((int) $match[4], (int) $match[5], (int) $match[6], $month, (int) $match[3], (int) $match[7]);
     966        }
     967
     968        return false;
     969    }
     970
     971    /**
     972     * Parse dates using strtotime()
     973     *
     974     * @access protected
     975     * @return int Timestamp
     976     */
     977    public function date_strtotime($date)
     978    {
     979        $strtotime = strtotime($date);
     980        if ($strtotime === -1 || $strtotime === false) {
     981            return false;
     982        }
     983
     984        return $strtotime;
     985    }
    1025986}
     987
     988class_alias('SimplePie\Parse\Date', 'SimplePie_Parse_Date');
  • trunk/src/wp-includes/SimplePie/src/Parser.php

    r59140 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie;
     46
     47use SimplePie\XML\Declaration\Parser as DeclarationParser;
     48
    4449/**
    4550 * Parses XML into something sane
    4651 *
    4752 *
    48  * This class can be overloaded with {@see SimplePie::set_parser_class()}
     53 * This class can be overloaded with {@see \SimplePie\SimplePie::set_parser_class()}
    4954 *
    5055 * @package SimplePie
    5156 * @subpackage Parsing
    5257 */
    53 class SimplePie_Parser
     58class Parser implements RegistryAware
    5459{
    55     var $error_code;
    56     var $error_string;
    57     var $current_line;
    58     var $current_column;
    59     var $current_byte;
    60     var $separator = ' ';
    61     var $namespace = array('');
    62     var $element = array('');
    63     var $xml_base = array('');
    64     var $xml_base_explicit = array(false);
    65     var $xml_lang = array('');
    66     var $data = array();
    67     var $datas = array(array());
    68     var $current_xhtml_construct = -1;
    69     var $encoding;
    70     protected $registry;
    71 
    72     public function set_registry(SimplePie_Registry $registry)
    73     {
    74         $this->registry = $registry;
    75     }
    76 
    77     public function parse(&$data, $encoding, $url = '')
    78     {
    79         if (class_exists('DOMXpath') && function_exists('Mf2\parse')) {
    80             $doc = new DOMDocument();
    81             @$doc->loadHTML($data);
    82             $xpath = new DOMXpath($doc);
    83             // Check for both h-feed and h-entry, as both a feed with no entries
    84             // and a list of entries without an h-feed wrapper are both valid.
    85             $query = '//*[contains(concat(" ", @class, " "), " h-feed ") or '.
    86                 'contains(concat(" ", @class, " "), " h-entry ")]';
    87             $result = $xpath->query($query);
    88             if ($result->length !== 0) {
    89                 return $this->parse_microformats($data, $url);
    90             }
    91         }
    92 
    93         // Use UTF-8 if we get passed US-ASCII, as every US-ASCII character is a UTF-8 character
    94         if (strtoupper($encoding) === 'US-ASCII')
    95         {
    96             $this->encoding = 'UTF-8';
    97         }
    98         else
    99         {
    100             $this->encoding = $encoding;
    101         }
    102 
    103         // Strip BOM:
    104         // UTF-32 Big Endian BOM
    105         if (substr($data, 0, 4) === "\x00\x00\xFE\xFF")
    106         {
    107             $data = substr($data, 4);
    108         }
    109         // UTF-32 Little Endian BOM
    110         elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00")
    111         {
    112             $data = substr($data, 4);
    113         }
    114         // UTF-16 Big Endian BOM
    115         elseif (substr($data, 0, 2) === "\xFE\xFF")
    116         {
    117             $data = substr($data, 2);
    118         }
    119         // UTF-16 Little Endian BOM
    120         elseif (substr($data, 0, 2) === "\xFF\xFE")
    121         {
    122             $data = substr($data, 2);
    123         }
    124         // UTF-8 BOM
    125         elseif (substr($data, 0, 3) === "\xEF\xBB\xBF")
    126         {
    127             $data = substr($data, 3);
    128         }
    129 
    130         if (substr($data, 0, 5) === '<?xml' && strspn(substr($data, 5, 1), "\x09\x0A\x0D\x20") && ($pos = strpos($data, '?>')) !== false)
    131         {
    132             $declaration = $this->registry->create('XML_Declaration_Parser', array(substr($data, 5, $pos - 5)));
    133             if ($declaration->parse())
    134             {
    135                 $data = substr($data, $pos + 2);
    136                 $data = '<?xml version="' . $declaration->version . '" encoding="' . $encoding . '" standalone="' . (($declaration->standalone) ? 'yes' : 'no') . '"?>' ."\n". $this->declare_html_entities() . $data;
    137             }
    138             else
    139             {
    140                 $this->error_string = 'SimplePie bug! Please report this!';
    141                 return false;
    142             }
    143         }
    144 
    145         $return = true;
    146 
    147         static $xml_is_sane = null;
    148         if ($xml_is_sane === null)
    149         {
    150             $parser_check = xml_parser_create();
    151             xml_parse_into_struct($parser_check, '<foo>&amp;</foo>', $values);
    152             xml_parser_free($parser_check);
    153             $xml_is_sane = isset($values[0]['value']);
    154         }
    155 
    156         // Create the parser
    157         if ($xml_is_sane)
    158         {
    159             $xml = xml_parser_create_ns($this->encoding, $this->separator);
    160             xml_parser_set_option($xml, XML_OPTION_SKIP_WHITE, 1);
    161             xml_parser_set_option($xml, XML_OPTION_CASE_FOLDING, 0);
    162             xml_set_object($xml, $this);
    163             xml_set_character_data_handler($xml, 'cdata');
    164             xml_set_element_handler($xml, 'tag_open', 'tag_close');
    165 
    166             // Parse!
    167             $wrapper = @is_writable(sys_get_temp_dir()) ? 'php://temp' : 'php://memory';
    168             if (($stream = fopen($wrapper, 'r+')) &&
    169                 fwrite($stream, $data) &&
    170                 rewind($stream))
    171             {
    172                 //Parse by chunks not to use too much memory
    173                 do
    174                 {
    175                     $stream_data = fread($stream, 1048576);
    176                     if (!xml_parse($xml, $stream_data === false ? '' : $stream_data, feof($stream)))
    177                     {
    178                         $this->error_code = xml_get_error_code($xml);
    179                         $this->error_string = xml_error_string($this->error_code);
    180                         $return = false;
    181                         break;
    182                     }
    183                 } while (!feof($stream));
    184                 fclose($stream);
    185             }
    186             else
    187             {
    188                 $return = false;
    189             }
    190 
    191             $this->current_line = xml_get_current_line_number($xml);
    192             $this->current_column = xml_get_current_column_number($xml);
    193             $this->current_byte = xml_get_current_byte_index($xml);
    194             xml_parser_free($xml);
    195             return $return;
    196         }
    197 
    198         libxml_clear_errors();
    199         $xml = new XMLReader();
    200         $xml->xml($data);
    201         while (@$xml->read())
    202         {
    203             switch ($xml->nodeType)
    204             {
    205 
    206                 case constant('XMLReader::END_ELEMENT'):
    207                     if ($xml->namespaceURI !== '')
    208                     {
    209                         $tagName = $xml->namespaceURI . $this->separator . $xml->localName;
    210                     }
    211                     else
    212                     {
    213                         $tagName = $xml->localName;
    214                     }
    215                     $this->tag_close(null, $tagName);
    216                     break;
    217                 case constant('XMLReader::ELEMENT'):
    218                     $empty = $xml->isEmptyElement;
    219                     if ($xml->namespaceURI !== '')
    220                     {
    221                         $tagName = $xml->namespaceURI . $this->separator . $xml->localName;
    222                     }
    223                     else
    224                     {
    225                         $tagName = $xml->localName;
    226                     }
    227                     $attributes = array();
    228                     while ($xml->moveToNextAttribute())
    229                     {
    230                         if ($xml->namespaceURI !== '')
    231                         {
    232                             $attrName = $xml->namespaceURI . $this->separator . $xml->localName;
    233                         }
    234                         else
    235                         {
    236                             $attrName = $xml->localName;
    237                         }
    238                         $attributes[$attrName] = $xml->value;
    239                     }
    240                     $this->tag_open(null, $tagName, $attributes);
    241                     if ($empty)
    242                     {
    243                         $this->tag_close(null, $tagName);
    244                     }
    245                     break;
    246                 case constant('XMLReader::TEXT'):
    247 
    248                 case constant('XMLReader::CDATA'):
    249                     $this->cdata(null, $xml->value);
    250                     break;
    251             }
    252         }
    253         if ($error = libxml_get_last_error())
    254         {
    255             $this->error_code = $error->code;
    256             $this->error_string = $error->message;
    257             $this->current_line = $error->line;
    258             $this->current_column = $error->column;
    259             return false;
    260         }
    261 
    262         return true;
    263     }
    264 
    265     public function get_error_code()
    266     {
    267         return $this->error_code;
    268     }
    269 
    270     public function get_error_string()
    271     {
    272         return $this->error_string;
    273     }
    274 
    275     public function get_current_line()
    276     {
    277         return $this->current_line;
    278     }
    279 
    280     public function get_current_column()
    281     {
    282         return $this->current_column;
    283     }
    284 
    285     public function get_current_byte()
    286     {
    287         return $this->current_byte;
    288     }
    289 
    290     public function get_data()
    291     {
    292         return $this->data;
    293     }
    294 
    295     public function tag_open($parser, $tag, $attributes)
    296     {
    297         list($this->namespace[], $this->element[]) = $this->split_ns($tag);
    298 
    299         $attribs = array();
    300         foreach ($attributes as $name => $value)
    301         {
    302             list($attrib_namespace, $attribute) = $this->split_ns($name);
    303             $attribs[$attrib_namespace][$attribute] = $value;
    304         }
    305 
    306         if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['base']))
    307         {
    308             $base = $this->registry->call('Misc', 'absolutize_url', array($attribs[SIMPLEPIE_NAMESPACE_XML]['base'], end($this->xml_base)));
    309             if ($base !== false)
    310             {
    311                 $this->xml_base[] = $base;
    312                 $this->xml_base_explicit[] = true;
    313             }
    314         }
    315         else
    316         {
    317             $this->xml_base[] = end($this->xml_base);
    318             $this->xml_base_explicit[] = end($this->xml_base_explicit);
    319         }
    320 
    321         if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['lang']))
    322         {
    323             $this->xml_lang[] = $attribs[SIMPLEPIE_NAMESPACE_XML]['lang'];
    324         }
    325         else
    326         {
    327             $this->xml_lang[] = end($this->xml_lang);
    328         }
    329 
    330         if ($this->current_xhtml_construct >= 0)
    331         {
    332             $this->current_xhtml_construct++;
    333             if (end($this->namespace) === SIMPLEPIE_NAMESPACE_XHTML)
    334             {
    335                 $this->data['data'] .= '<' . end($this->element);
    336                 if (isset($attribs['']))
    337                 {
    338                     foreach ($attribs[''] as $name => $value)
    339                     {
    340                         $this->data['data'] .= ' ' . $name . '="' . htmlspecialchars($value, ENT_COMPAT, $this->encoding) . '"';
    341                     }
    342                 }
    343                 $this->data['data'] .= '>';
    344             }
    345         }
    346         else
    347         {
    348             $this->datas[] =& $this->data;
    349             $this->data =& $this->data['child'][end($this->namespace)][end($this->element)][];
    350             $this->data = array('data' => '', 'attribs' => $attribs, 'xml_base' => end($this->xml_base), 'xml_base_explicit' => end($this->xml_base_explicit), 'xml_lang' => end($this->xml_lang));
    351             if ((end($this->namespace) === SIMPLEPIE_NAMESPACE_ATOM_03 && in_array(end($this->element), array('title', 'tagline', 'copyright', 'info', 'summary', 'content')) && isset($attribs['']['mode']) && $attribs['']['mode'] === 'xml')
    352             || (end($this->namespace) === SIMPLEPIE_NAMESPACE_ATOM_10 && in_array(end($this->element), array('rights', 'subtitle', 'summary', 'info', 'title', 'content')) && isset($attribs['']['type']) && $attribs['']['type'] === 'xhtml')
    353             || (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_20 && in_array(end($this->element), array('title')))
    354             || (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_090 && in_array(end($this->element), array('title')))
    355             || (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_10 && in_array(end($this->element), array('title'))))
    356             {
    357                 $this->current_xhtml_construct = 0;
    358             }
    359         }
    360     }
    361 
    362     public function cdata($parser, $cdata)
    363     {
    364         if ($this->current_xhtml_construct >= 0)
    365         {
    366             $this->data['data'] .= htmlspecialchars($cdata, ENT_QUOTES, $this->encoding);
    367         }
    368         else
    369         {
    370             $this->data['data'] .= $cdata;
    371         }
    372     }
    373 
    374     public function tag_close($parser, $tag)
    375     {
    376         if ($this->current_xhtml_construct >= 0)
    377         {
    378             $this->current_xhtml_construct--;
    379             if (end($this->namespace) === SIMPLEPIE_NAMESPACE_XHTML && !in_array(end($this->element), array('area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param')))
    380             {
    381                 $this->data['data'] .= '</' . end($this->element) . '>';
    382             }
    383         }
    384         if ($this->current_xhtml_construct === -1)
    385         {
    386             $this->data =& $this->datas[count($this->datas) - 1];
    387             array_pop($this->datas);
    388         }
    389 
    390         array_pop($this->element);
    391         array_pop($this->namespace);
    392         array_pop($this->xml_base);
    393         array_pop($this->xml_base_explicit);
    394         array_pop($this->xml_lang);
    395     }
    396 
    397     public function split_ns($string)
    398     {
    399         static $cache = array();
    400         if (!isset($cache[$string]))
    401         {
    402             if ($pos = strpos($string, $this->separator))
    403             {
    404                 static $separator_length;
    405                 if (!$separator_length)
    406                 {
    407                     $separator_length = strlen($this->separator);
    408                 }
    409                 $namespace = substr($string, 0, $pos);
    410                 $local_name = substr($string, $pos + $separator_length);
    411                 if (strtolower($namespace) === SIMPLEPIE_NAMESPACE_ITUNES)
    412                 {
    413                     $namespace = SIMPLEPIE_NAMESPACE_ITUNES;
    414                 }
    415 
    416                 // Normalize the Media RSS namespaces
    417                 if ($namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG ||
    418                     $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG2 ||
    419                     $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG3 ||
    420                     $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG4 ||
    421                     $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG5 )
    422                 {
    423                     $namespace = SIMPLEPIE_NAMESPACE_MEDIARSS;
    424                 }
    425                 $cache[$string] = array($namespace, $local_name);
    426             }
    427             else
    428             {
    429                 $cache[$string] = array('', $string);
    430             }
    431         }
    432         return $cache[$string];
    433     }
    434 
    435     private function parse_hcard($data, $category = false) {
    436         $name = '';
    437         $link = '';
    438         // Check if h-card is set and pass that information on in the link.
    439         if (isset($data['type']) && in_array('h-card', $data['type'])) {
    440             if (isset($data['properties']['name'][0])) {
    441                 $name = $data['properties']['name'][0];
    442             }
    443             if (isset($data['properties']['url'][0])) {
    444                 $link = $data['properties']['url'][0];
    445                 if ($name === '') {
    446                     $name = $link;
    447                 }
    448                 else {
    449                     // can't have commas in categories.
    450                     $name = str_replace(',', '', $name);
    451                 }
    452                 $person_tag = $category ? '<span class="person-tag"></span>' : '';
    453                 return '<a class="h-card" href="'.$link.'">'.$person_tag.$name.'</a>';
    454             }
    455         }
    456         return isset($data['value']) ? $data['value'] : '';
    457     }
    458 
    459     private function parse_microformats(&$data, $url) {
    460         $feed_title = '';
    461         $feed_author = NULL;
    462         $author_cache = array();
    463         $items = array();
    464         $entries = array();
    465         $mf = Mf2\parse($data, $url);
    466         // First look for an h-feed.
    467         $h_feed = array();
    468         foreach ($mf['items'] as $mf_item) {
    469             if (in_array('h-feed', $mf_item['type'])) {
    470                 $h_feed = $mf_item;
    471                 break;
    472             }
    473             // Also look for h-feed or h-entry in the children of each top level item.
    474             if (!isset($mf_item['children'][0]['type'])) continue;
    475             if (in_array('h-feed', $mf_item['children'][0]['type'])) {
    476                 $h_feed = $mf_item['children'][0];
    477                 // In this case the parent of the h-feed may be an h-card, so use it as
    478                 // the feed_author.
    479                 if (in_array('h-card', $mf_item['type'])) $feed_author = $mf_item;
    480                 break;
    481             }
    482             else if (in_array('h-entry', $mf_item['children'][0]['type'])) {
    483                 $entries = $mf_item['children'];
    484                 // In this case the parent of the h-entry list may be an h-card, so use
    485                 // it as the feed_author.
    486                 if (in_array('h-card', $mf_item['type'])) $feed_author = $mf_item;
    487                 break;
    488             }
    489         }
    490         if (isset($h_feed['children'])) {
    491             $entries = $h_feed['children'];
    492             // Also set the feed title and store author from the h-feed if available.
    493             if (isset($mf['items'][0]['properties']['name'][0])) {
    494                 $feed_title = $mf['items'][0]['properties']['name'][0];
    495             }
    496             if (isset($mf['items'][0]['properties']['author'][0])) {
    497                 $feed_author = $mf['items'][0]['properties']['author'][0];
    498             }
    499         }
    500         else if (count($entries) === 0) {
    501             $entries = $mf['items'];
    502         }
    503         for ($i = 0; $i < count($entries); $i++) {
    504             $entry = $entries[$i];
    505             if (in_array('h-entry', $entry['type'])) {
    506                 $item = array();
    507                 $title = '';
    508                 $description = '';
    509                 if (isset($entry['properties']['url'][0])) {
    510                     $link = $entry['properties']['url'][0];
    511                     if (isset($link['value'])) $link = $link['value'];
    512                     $item['link'] = array(array('data' => $link));
    513                 }
    514                 if (isset($entry['properties']['uid'][0])) {
    515                     $guid = $entry['properties']['uid'][0];
    516                     if (isset($guid['value'])) $guid = $guid['value'];
    517                     $item['guid'] = array(array('data' => $guid));
    518                 }
    519                 if (isset($entry['properties']['name'][0])) {
    520                     $title = $entry['properties']['name'][0];
    521                     if (isset($title['value'])) $title = $title['value'];
    522                     $item['title'] = array(array('data' => $title));
    523                 }
    524                 if (isset($entry['properties']['author'][0]) || isset($feed_author)) {
    525                     // author is a special case, it can be plain text or an h-card array.
    526                     // If it's plain text it can also be a url that should be followed to
    527                     // get the actual h-card.
    528                     $author = isset($entry['properties']['author'][0]) ?
    529                         $entry['properties']['author'][0] : $feed_author;
    530                     if (!is_string($author)) {
    531                         $author = $this->parse_hcard($author);
    532                     }
    533                     else if (strpos($author, 'http') === 0) {
    534                         if (isset($author_cache[$author])) {
    535                             $author = $author_cache[$author];
    536                         }
    537                         else {
    538                             $mf = Mf2\fetch($author);
    539                             foreach ($mf['items'] as $hcard) {
    540                                 // Only interested in an h-card by itself in this case.
    541                                 if (!in_array('h-card', $hcard['type'])) {
    542                                     continue;
    543                                 }
    544                                 // It must have a url property matching what we fetched.
    545                                 if (!isset($hcard['properties']['url']) ||
    546                                         !(in_array($author, $hcard['properties']['url']))) {
    547                                     continue;
    548                                 }
    549                                 // Save parse_hcard the trouble of finding the correct url.
    550                                 $hcard['properties']['url'][0] = $author;
    551                                 // Cache this h-card for the next h-entry to check.
    552                                 $author_cache[$author] = $this->parse_hcard($hcard);
    553                                 $author = $author_cache[$author];
    554                                 break;
    555                             }
    556                         }
    557                     }
    558                     $item['author'] = array(array('data' => $author));
    559                 }
    560                 if (isset($entry['properties']['photo'][0])) {
    561                     // If a photo is also in content, don't need to add it again here.
    562                     $content = '';
    563                     if (isset($entry['properties']['content'][0]['html'])) {
    564                         $content = $entry['properties']['content'][0]['html'];
    565                     }
    566                     $photo_list = array();
    567                     for ($j = 0; $j < count($entry['properties']['photo']); $j++) {
    568                         $photo = $entry['properties']['photo'][$j];
    569                         if (!empty($photo) && strpos($content, $photo) === false) {
    570                             $photo_list[] = $photo;
    571                         }
    572                     }
    573                     // When there's more than one photo show the first and use a lightbox.
    574                     // Need a permanent, unique name for the image set, but don't have
    575                     // anything unique except for the content itself, so use that.
    576                     $count = count($photo_list);
    577                     if ($count > 1) {
    578                         $image_set_id = preg_replace('/[[:^alnum:]]/', '', $photo_list[0]);
    579                         $description = '<p>';
    580                         for ($j = 0; $j < $count; $j++) {
    581                             $hidden = $j === 0 ? '' : 'class="hidden" ';
    582                             $description .= '<a href="'.$photo_list[$j].'" '.$hidden.
    583                                 'data-lightbox="image-set-'.$image_set_id.'">'.
    584                                 '<img src="'.$photo_list[$j].'"></a>';
    585                         }
    586                         $description .= '<br><b>'.$count.' photos</b></p>';
    587                     }
    588                     else if ($count == 1) {
    589                         $description = '<p><img src="'.$photo_list[0].'"></p>';
    590                     }
    591                 }
    592                 if (isset($entry['properties']['content'][0]['html'])) {
    593                     // e-content['value'] is the same as p-name when they are on the same
    594                     // element. Use this to replace title with a strip_tags version so
    595                     // that alt text from images is not included in the title.
    596                     if ($entry['properties']['content'][0]['value'] === $title) {
    597                         $title = strip_tags($entry['properties']['content'][0]['html']);
    598                         $item['title'] = array(array('data' => $title));
    599                     }
    600                     $description .= $entry['properties']['content'][0]['html'];
    601                     if (isset($entry['properties']['in-reply-to'][0])) {
    602                         $in_reply_to = '';
    603                         if (is_string($entry['properties']['in-reply-to'][0])) {
    604                             $in_reply_to = $entry['properties']['in-reply-to'][0];
    605                         }
    606                         else if (isset($entry['properties']['in-reply-to'][0]['value'])) {
    607                             $in_reply_to = $entry['properties']['in-reply-to'][0]['value'];
    608                         }
    609                         if ($in_reply_to !== '') {
    610                             $description .= '<p><span class="in-reply-to"></span> '.
    611                                 '<a href="'.$in_reply_to.'">'.$in_reply_to.'</a><p>';
    612                         }
    613                     }
    614                     $item['description'] = array(array('data' => $description));
    615                 }
    616                 if (isset($entry['properties']['category'])) {
    617                     $category_csv = '';
    618                     // Categories can also contain h-cards.
    619                     foreach ($entry['properties']['category'] as $category) {
    620                         if ($category_csv !== '') $category_csv .= ', ';
    621                         if (is_string($category)) {
    622                             // Can't have commas in categories.
    623                             $category_csv .= str_replace(',', '', $category);
    624                         }
    625                         else {
    626                             $category_csv .= $this->parse_hcard($category, true);
    627                         }
    628                     }
    629                     $item['category'] = array(array('data' => $category_csv));
    630                 }
    631                 if (isset($entry['properties']['published'][0])) {
    632                     $timestamp = strtotime($entry['properties']['published'][0]);
    633                     $pub_date = date('F j Y g:ia', $timestamp).' GMT';
    634                     $item['pubDate'] = array(array('data' => $pub_date));
    635                 }
    636                 // The title and description are set to the empty string to represent
    637                 // a deleted item (which also makes it an invalid rss item).
    638                 if (isset($entry['properties']['deleted'][0])) {
    639                     $item['title'] = array(array('data' => ''));
    640                     $item['description'] = array(array('data' => ''));
    641                 }
    642                 $items[] = array('child' => array('' => $item));
    643             }
    644         }
    645         // Mimic RSS data format when storing microformats.
    646         $link = array(array('data' => $url));
    647         $image = '';
    648         if (!is_string($feed_author) &&
    649                 isset($feed_author['properties']['photo'][0])) {
    650             $image = array(array('child' => array('' => array('url' =>
    651                 array(array('data' => $feed_author['properties']['photo'][0]))))));
    652         }
    653         // Use the name given for the h-feed, or get the title from the html.
    654         if ($feed_title !== '') {
    655             $feed_title = array(array('data' => htmlspecialchars($feed_title)));
    656         }
    657         else if ($position = strpos($data, '<title>')) {
    658             $start = $position < 200 ? 0 : $position - 200;
    659             $check = substr($data, $start, 400);
    660             $matches = array();
    661             if (preg_match('/<title>(.+)<\/title>/', $check, $matches)) {
    662                 $feed_title = array(array('data' => htmlspecialchars($matches[1])));
    663             }
    664         }
    665         $channel = array('channel' => array(array('child' => array('' =>
    666             array('link' => $link, 'image' => $image, 'title' => $feed_title,
    667                   'item' => $items)))));
    668         $rss = array(array('attribs' => array('' => array('version' => '2.0')),
    669                            'child' => array('' => $channel)));
    670         $this->data = array('child' => array('' => array('rss' => $rss)));
    671         return true;
    672     }
    673 
    674     private function declare_html_entities() {
    675         // This is required because the RSS specification says that entity-encoded
    676         // html is allowed, but the xml specification says they must be declared.
    677         return '<!DOCTYPE html [ <!ENTITY nbsp "&#x00A0;"> <!ENTITY iexcl "&#x00A1;"> <!ENTITY cent "&#x00A2;"> <!ENTITY pound "&#x00A3;"> <!ENTITY curren "&#x00A4;"> <!ENTITY yen "&#x00A5;"> <!ENTITY brvbar "&#x00A6;"> <!ENTITY sect "&#x00A7;"> <!ENTITY uml "&#x00A8;"> <!ENTITY copy "&#x00A9;"> <!ENTITY ordf "&#x00AA;"> <!ENTITY laquo "&#x00AB;"> <!ENTITY not "&#x00AC;"> <!ENTITY shy "&#x00AD;"> <!ENTITY reg "&#x00AE;"> <!ENTITY macr "&#x00AF;"> <!ENTITY deg "&#x00B0;"> <!ENTITY plusmn "&#x00B1;"> <!ENTITY sup2 "&#x00B2;"> <!ENTITY sup3 "&#x00B3;"> <!ENTITY acute "&#x00B4;"> <!ENTITY micro "&#x00B5;"> <!ENTITY para "&#x00B6;"> <!ENTITY middot "&#x00B7;"> <!ENTITY cedil "&#x00B8;"> <!ENTITY sup1 "&#x00B9;"> <!ENTITY ordm "&#x00BA;"> <!ENTITY raquo "&#x00BB;"> <!ENTITY frac14 "&#x00BC;"> <!ENTITY frac12 "&#x00BD;"> <!ENTITY frac34 "&#x00BE;"> <!ENTITY iquest "&#x00BF;"> <!ENTITY Agrave "&#x00C0;"> <!ENTITY Aacute "&#x00C1;"> <!ENTITY Acirc "&#x00C2;"> <!ENTITY Atilde "&#x00C3;"> <!ENTITY Auml "&#x00C4;"> <!ENTITY Aring "&#x00C5;"> <!ENTITY AElig "&#x00C6;"> <!ENTITY Ccedil "&#x00C7;"> <!ENTITY Egrave "&#x00C8;"> <!ENTITY Eacute "&#x00C9;"> <!ENTITY Ecirc "&#x00CA;"> <!ENTITY Euml "&#x00CB;"> <!ENTITY Igrave "&#x00CC;"> <!ENTITY Iacute "&#x00CD;"> <!ENTITY Icirc "&#x00CE;"> <!ENTITY Iuml "&#x00CF;"> <!ENTITY ETH "&#x00D0;"> <!ENTITY Ntilde "&#x00D1;"> <!ENTITY Ograve "&#x00D2;"> <!ENTITY Oacute "&#x00D3;"> <!ENTITY Ocirc "&#x00D4;"> <!ENTITY Otilde "&#x00D5;"> <!ENTITY Ouml "&#x00D6;"> <!ENTITY times "&#x00D7;"> <!ENTITY Oslash "&#x00D8;"> <!ENTITY Ugrave "&#x00D9;"> <!ENTITY Uacute "&#x00DA;"> <!ENTITY Ucirc "&#x00DB;"> <!ENTITY Uuml "&#x00DC;"> <!ENTITY Yacute "&#x00DD;"> <!ENTITY THORN "&#x00DE;"> <!ENTITY szlig "&#x00DF;"> <!ENTITY agrave "&#x00E0;"> <!ENTITY aacute "&#x00E1;"> <!ENTITY acirc "&#x00E2;"> <!ENTITY atilde "&#x00E3;"> <!ENTITY auml "&#x00E4;"> <!ENTITY aring "&#x00E5;"> <!ENTITY aelig "&#x00E6;"> <!ENTITY ccedil "&#x00E7;"> <!ENTITY egrave "&#x00E8;"> <!ENTITY eacute "&#x00E9;"> <!ENTITY ecirc "&#x00EA;"> <!ENTITY euml "&#x00EB;"> <!ENTITY igrave "&#x00EC;"> <!ENTITY iacute "&#x00ED;"> <!ENTITY icirc "&#x00EE;"> <!ENTITY iuml "&#x00EF;"> <!ENTITY eth "&#x00F0;"> <!ENTITY ntilde "&#x00F1;"> <!ENTITY ograve "&#x00F2;"> <!ENTITY oacute "&#x00F3;"> <!ENTITY ocirc "&#x00F4;"> <!ENTITY otilde "&#x00F5;"> <!ENTITY ouml "&#x00F6;"> <!ENTITY divide "&#x00F7;"> <!ENTITY oslash "&#x00F8;"> <!ENTITY ugrave "&#x00F9;"> <!ENTITY uacute "&#x00FA;"> <!ENTITY ucirc "&#x00FB;"> <!ENTITY uuml "&#x00FC;"> <!ENTITY yacute "&#x00FD;"> <!ENTITY thorn "&#x00FE;"> <!ENTITY yuml "&#x00FF;"> <!ENTITY OElig "&#x0152;"> <!ENTITY oelig "&#x0153;"> <!ENTITY Scaron "&#x0160;"> <!ENTITY scaron "&#x0161;"> <!ENTITY Yuml "&#x0178;"> <!ENTITY fnof "&#x0192;"> <!ENTITY circ "&#x02C6;"> <!ENTITY tilde "&#x02DC;"> <!ENTITY Alpha "&#x0391;"> <!ENTITY Beta "&#x0392;"> <!ENTITY Gamma "&#x0393;"> <!ENTITY Epsilon "&#x0395;"> <!ENTITY Zeta "&#x0396;"> <!ENTITY Eta "&#x0397;"> <!ENTITY Theta "&#x0398;"> <!ENTITY Iota "&#x0399;"> <!ENTITY Kappa "&#x039A;"> <!ENTITY Lambda "&#x039B;"> <!ENTITY Mu "&#x039C;"> <!ENTITY Nu "&#x039D;"> <!ENTITY Xi "&#x039E;"> <!ENTITY Omicron "&#x039F;"> <!ENTITY Pi "&#x03A0;"> <!ENTITY Rho "&#x03A1;"> <!ENTITY Sigma "&#x03A3;"> <!ENTITY Tau "&#x03A4;"> <!ENTITY Upsilon "&#x03A5;"> <!ENTITY Phi "&#x03A6;"> <!ENTITY Chi "&#x03A7;"> <!ENTITY Psi "&#x03A8;"> <!ENTITY Omega "&#x03A9;"> <!ENTITY alpha "&#x03B1;"> <!ENTITY beta "&#x03B2;"> <!ENTITY gamma "&#x03B3;"> <!ENTITY delta "&#x03B4;"> <!ENTITY epsilon "&#x03B5;"> <!ENTITY zeta "&#x03B6;"> <!ENTITY eta "&#x03B7;"> <!ENTITY theta "&#x03B8;"> <!ENTITY iota "&#x03B9;"> <!ENTITY kappa "&#x03BA;"> <!ENTITY lambda "&#x03BB;"> <!ENTITY mu "&#x03BC;"> <!ENTITY nu "&#x03BD;"> <!ENTITY xi "&#x03BE;"> <!ENTITY omicron "&#x03BF;"> <!ENTITY pi "&#x03C0;"> <!ENTITY rho "&#x03C1;"> <!ENTITY sigmaf "&#x03C2;"> <!ENTITY sigma "&#x03C3;"> <!ENTITY tau "&#x03C4;"> <!ENTITY upsilon "&#x03C5;"> <!ENTITY phi "&#x03C6;"> <!ENTITY chi "&#x03C7;"> <!ENTITY psi "&#x03C8;"> <!ENTITY omega "&#x03C9;"> <!ENTITY thetasym "&#x03D1;"> <!ENTITY upsih "&#x03D2;"> <!ENTITY piv "&#x03D6;"> <!ENTITY ensp "&#x2002;"> <!ENTITY emsp "&#x2003;"> <!ENTITY thinsp "&#x2009;"> <!ENTITY zwnj "&#x200C;"> <!ENTITY zwj "&#x200D;"> <!ENTITY lrm "&#x200E;"> <!ENTITY rlm "&#x200F;"> <!ENTITY ndash "&#x2013;"> <!ENTITY mdash "&#x2014;"> <!ENTITY lsquo "&#x2018;"> <!ENTITY rsquo "&#x2019;"> <!ENTITY sbquo "&#x201A;"> <!ENTITY ldquo "&#x201C;"> <!ENTITY rdquo "&#x201D;"> <!ENTITY bdquo "&#x201E;"> <!ENTITY dagger "&#x2020;"> <!ENTITY Dagger "&#x2021;"> <!ENTITY bull "&#x2022;"> <!ENTITY hellip "&#x2026;"> <!ENTITY permil "&#x2030;"> <!ENTITY prime "&#x2032;"> <!ENTITY Prime "&#x2033;"> <!ENTITY lsaquo "&#x2039;"> <!ENTITY rsaquo "&#x203A;"> <!ENTITY oline "&#x203E;"> <!ENTITY frasl "&#x2044;"> <!ENTITY euro "&#x20AC;"> <!ENTITY image "&#x2111;"> <!ENTITY weierp "&#x2118;"> <!ENTITY real "&#x211C;"> <!ENTITY trade "&#x2122;"> <!ENTITY alefsym "&#x2135;"> <!ENTITY larr "&#x2190;"> <!ENTITY uarr "&#x2191;"> <!ENTITY rarr "&#x2192;"> <!ENTITY darr "&#x2193;"> <!ENTITY harr "&#x2194;"> <!ENTITY crarr "&#x21B5;"> <!ENTITY lArr "&#x21D0;"> <!ENTITY uArr "&#x21D1;"> <!ENTITY rArr "&#x21D2;"> <!ENTITY dArr "&#x21D3;"> <!ENTITY hArr "&#x21D4;"> <!ENTITY forall "&#x2200;"> <!ENTITY part "&#x2202;"> <!ENTITY exist "&#x2203;"> <!ENTITY empty "&#x2205;"> <!ENTITY nabla "&#x2207;"> <!ENTITY isin "&#x2208;"> <!ENTITY notin "&#x2209;"> <!ENTITY ni "&#x220B;"> <!ENTITY prod "&#x220F;"> <!ENTITY sum "&#x2211;"> <!ENTITY minus "&#x2212;"> <!ENTITY lowast "&#x2217;"> <!ENTITY radic "&#x221A;"> <!ENTITY prop "&#x221D;"> <!ENTITY infin "&#x221E;"> <!ENTITY ang "&#x2220;"> <!ENTITY and "&#x2227;"> <!ENTITY or "&#x2228;"> <!ENTITY cap "&#x2229;"> <!ENTITY cup "&#x222A;"> <!ENTITY int "&#x222B;"> <!ENTITY there4 "&#x2234;"> <!ENTITY sim "&#x223C;"> <!ENTITY cong "&#x2245;"> <!ENTITY asymp "&#x2248;"> <!ENTITY ne "&#x2260;"> <!ENTITY equiv "&#x2261;"> <!ENTITY le "&#x2264;"> <!ENTITY ge "&#x2265;"> <!ENTITY sub "&#x2282;"> <!ENTITY sup "&#x2283;"> <!ENTITY nsub "&#x2284;"> <!ENTITY sube "&#x2286;"> <!ENTITY supe "&#x2287;"> <!ENTITY oplus "&#x2295;"> <!ENTITY otimes "&#x2297;"> <!ENTITY perp "&#x22A5;"> <!ENTITY sdot "&#x22C5;"> <!ENTITY lceil "&#x2308;"> <!ENTITY rceil "&#x2309;"> <!ENTITY lfloor "&#x230A;"> <!ENTITY rfloor "&#x230B;"> <!ENTITY lang "&#x2329;"> <!ENTITY rang "&#x232A;"> <!ENTITY loz "&#x25CA;"> <!ENTITY spades "&#x2660;"> <!ENTITY clubs "&#x2663;"> <!ENTITY hearts "&#x2665;"> <!ENTITY diams "&#x2666;"> ]>';
    678     }
     60    public $error_code;
     61    public $error_string;
     62    public $current_line;
     63    public $current_column;
     64    public $current_byte;
     65    public $separator = ' ';
     66    public $namespace = [''];
     67    public $element = [''];
     68    public $xml_base = [''];
     69    public $xml_base_explicit = [false];
     70    public $xml_lang = [''];
     71    public $data = [];
     72    public $datas = [[]];
     73    public $current_xhtml_construct = -1;
     74    public $encoding;
     75    protected $registry;
     76
     77    public function set_registry(\SimplePie\Registry $registry)/* : void */
     78    {
     79        $this->registry = $registry;
     80    }
     81
     82    public function parse(&$data, $encoding, $url = '')
     83    {
     84        if (class_exists('DOMXpath') && function_exists('Mf2\parse')) {
     85            $doc = new \DOMDocument();
     86            @$doc->loadHTML($data);
     87            $xpath = new \DOMXpath($doc);
     88            // Check for both h-feed and h-entry, as both a feed with no entries
     89            // and a list of entries without an h-feed wrapper are both valid.
     90            $query = '//*[contains(concat(" ", @class, " "), " h-feed ") or '.
     91                'contains(concat(" ", @class, " "), " h-entry ")]';
     92            $result = $xpath->query($query);
     93            if ($result->length !== 0) {
     94                return $this->parse_microformats($data, $url);
     95            }
     96        }
     97
     98        // Use UTF-8 if we get passed US-ASCII, as every US-ASCII character is a UTF-8 character
     99        if (strtoupper($encoding) === 'US-ASCII') {
     100            $this->encoding = 'UTF-8';
     101        } else {
     102            $this->encoding = $encoding;
     103        }
     104
     105        // Strip BOM:
     106        // UTF-32 Big Endian BOM
     107        if (substr($data, 0, 4) === "\x00\x00\xFE\xFF") {
     108            $data = substr($data, 4);
     109        }
     110        // UTF-32 Little Endian BOM
     111        elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00") {
     112            $data = substr($data, 4);
     113        }
     114        // UTF-16 Big Endian BOM
     115        elseif (substr($data, 0, 2) === "\xFE\xFF") {
     116            $data = substr($data, 2);
     117        }
     118        // UTF-16 Little Endian BOM
     119        elseif (substr($data, 0, 2) === "\xFF\xFE") {
     120            $data = substr($data, 2);
     121        }
     122        // UTF-8 BOM
     123        elseif (substr($data, 0, 3) === "\xEF\xBB\xBF") {
     124            $data = substr($data, 3);
     125        }
     126
     127        if (substr($data, 0, 5) === '<?xml' && strspn(substr($data, 5, 1), "\x09\x0A\x0D\x20") && ($pos = strpos($data, '?>')) !== false) {
     128            $declaration = $this->registry->create(DeclarationParser::class, [substr($data, 5, $pos - 5)]);
     129            if ($declaration->parse()) {
     130                $data = substr($data, $pos + 2);
     131                $data = '<?xml version="' . $declaration->version . '" encoding="' . $encoding . '" standalone="' . (($declaration->standalone) ? 'yes' : 'no') . '"?>' ."\n". $this->declare_html_entities() . $data;
     132            } else {
     133                $this->error_string = 'SimplePie bug! Please report this!';
     134                return false;
     135            }
     136        }
     137
     138        $return = true;
     139
     140        static $xml_is_sane = null;
     141        if ($xml_is_sane === null) {
     142            $parser_check = xml_parser_create();
     143            xml_parse_into_struct($parser_check, '<foo>&amp;</foo>', $values);
     144            xml_parser_free($parser_check);
     145            $xml_is_sane = isset($values[0]['value']);
     146        }
     147
     148        // Create the parser
     149        if ($xml_is_sane) {
     150            $xml = xml_parser_create_ns($this->encoding, $this->separator);
     151            xml_parser_set_option($xml, XML_OPTION_SKIP_WHITE, 1);
     152            xml_parser_set_option($xml, XML_OPTION_CASE_FOLDING, 0);
     153            xml_set_character_data_handler($xml, [$this, 'cdata']);
     154            xml_set_element_handler($xml, [$this, 'tag_open'], [$this, 'tag_close']);
     155
     156            // Parse!
     157            $wrapper = @is_writable(sys_get_temp_dir()) ? 'php://temp' : 'php://memory';
     158            if (($stream = fopen($wrapper, 'r+')) &&
     159                fwrite($stream, $data) &&
     160                rewind($stream)) {
     161                //Parse by chunks not to use too much memory
     162                do {
     163                    $stream_data = fread($stream, 1048576);
     164                    if (!xml_parse($xml, $stream_data === false ? '' : $stream_data, feof($stream))) {
     165                        $this->error_code = xml_get_error_code($xml);
     166                        $this->error_string = xml_error_string($this->error_code);
     167                        $return = false;
     168                        break;
     169                    }
     170                } while (!feof($stream));
     171                fclose($stream);
     172            } else {
     173                $return = false;
     174            }
     175
     176            $this->current_line = xml_get_current_line_number($xml);
     177            $this->current_column = xml_get_current_column_number($xml);
     178            $this->current_byte = xml_get_current_byte_index($xml);
     179            xml_parser_free($xml);
     180            return $return;
     181        }
     182
     183        libxml_clear_errors();
     184        $xml = new \XMLReader();
     185        $xml->xml($data);
     186        while (@$xml->read()) {
     187            switch ($xml->nodeType) {
     188                case constant('XMLReader::END_ELEMENT'):
     189                    if ($xml->namespaceURI !== '') {
     190                        $tagName = $xml->namespaceURI . $this->separator . $xml->localName;
     191                    } else {
     192                        $tagName = $xml->localName;
     193                    }
     194                    $this->tag_close(null, $tagName);
     195                    break;
     196                case constant('XMLReader::ELEMENT'):
     197                    $empty = $xml->isEmptyElement;
     198                    if ($xml->namespaceURI !== '') {
     199                        $tagName = $xml->namespaceURI . $this->separator . $xml->localName;
     200                    } else {
     201                        $tagName = $xml->localName;
     202                    }
     203                    $attributes = [];
     204                    while ($xml->moveToNextAttribute()) {
     205                        if ($xml->namespaceURI !== '') {
     206                            $attrName = $xml->namespaceURI . $this->separator . $xml->localName;
     207                        } else {
     208                            $attrName = $xml->localName;
     209                        }
     210                        $attributes[$attrName] = $xml->value;
     211                    }
     212                    $this->tag_open(null, $tagName, $attributes);
     213                    if ($empty) {
     214                        $this->tag_close(null, $tagName);
     215                    }
     216                    break;
     217                case constant('XMLReader::TEXT'):
     218
     219                case constant('XMLReader::CDATA'):
     220                    $this->cdata(null, $xml->value);
     221                    break;
     222            }
     223        }
     224        if ($error = libxml_get_last_error()) {
     225            $this->error_code = $error->code;
     226            $this->error_string = $error->message;
     227            $this->current_line = $error->line;
     228            $this->current_column = $error->column;
     229            return false;
     230        }
     231
     232        return true;
     233    }
     234
     235    public function get_error_code()
     236    {
     237        return $this->error_code;
     238    }
     239
     240    public function get_error_string()
     241    {
     242        return $this->error_string;
     243    }
     244
     245    public function get_current_line()
     246    {
     247        return $this->current_line;
     248    }
     249
     250    public function get_current_column()
     251    {
     252        return $this->current_column;
     253    }
     254
     255    public function get_current_byte()
     256    {
     257        return $this->current_byte;
     258    }
     259
     260    public function get_data()
     261    {
     262        return $this->data;
     263    }
     264
     265    public function tag_open($parser, $tag, $attributes)
     266    {
     267        [$this->namespace[], $this->element[]] = $this->split_ns($tag);
     268
     269        $attribs = [];
     270        foreach ($attributes as $name => $value) {
     271            [$attrib_namespace, $attribute] = $this->split_ns($name);
     272            $attribs[$attrib_namespace][$attribute] = $value;
     273        }
     274
     275        if (isset($attribs[\SimplePie\SimplePie::NAMESPACE_XML]['base'])) {
     276            $base = $this->registry->call(Misc::class, 'absolutize_url', [$attribs[\SimplePie\SimplePie::NAMESPACE_XML]['base'], end($this->xml_base)]);
     277            if ($base !== false) {
     278                $this->xml_base[] = $base;
     279                $this->xml_base_explicit[] = true;
     280            }
     281        } else {
     282            $this->xml_base[] = end($this->xml_base);
     283            $this->xml_base_explicit[] = end($this->xml_base_explicit);
     284        }
     285
     286        if (isset($attribs[\SimplePie\SimplePie::NAMESPACE_XML]['lang'])) {
     287            $this->xml_lang[] = $attribs[\SimplePie\SimplePie::NAMESPACE_XML]['lang'];
     288        } else {
     289            $this->xml_lang[] = end($this->xml_lang);
     290        }
     291
     292        if ($this->current_xhtml_construct >= 0) {
     293            $this->current_xhtml_construct++;
     294            if (end($this->namespace) === \SimplePie\SimplePie::NAMESPACE_XHTML) {
     295                $this->data['data'] .= '<' . end($this->element);
     296                if (isset($attribs[''])) {
     297                    foreach ($attribs[''] as $name => $value) {
     298                        $this->data['data'] .= ' ' . $name . '="' . htmlspecialchars($value, ENT_COMPAT, $this->encoding) . '"';
     299                    }
     300                }
     301                $this->data['data'] .= '>';
     302            }
     303        } else {
     304            $this->datas[] = &$this->data;
     305            $this->data = &$this->data['child'][end($this->namespace)][end($this->element)][];
     306            $this->data = ['data' => '', 'attribs' => $attribs, 'xml_base' => end($this->xml_base), 'xml_base_explicit' => end($this->xml_base_explicit), 'xml_lang' => end($this->xml_lang)];
     307            if ((end($this->namespace) === \SimplePie\SimplePie::NAMESPACE_ATOM_03 && in_array(end($this->element), ['title', 'tagline', 'copyright', 'info', 'summary', 'content']) && isset($attribs['']['mode']) && $attribs['']['mode'] === 'xml')
     308            || (end($this->namespace) === \SimplePie\SimplePie::NAMESPACE_ATOM_10 && in_array(end($this->element), ['rights', 'subtitle', 'summary', 'info', 'title', 'content']) && isset($attribs['']['type']) && $attribs['']['type'] === 'xhtml')
     309            || (end($this->namespace) === \SimplePie\SimplePie::NAMESPACE_RSS_20 && in_array(end($this->element), ['title']))
     310            || (end($this->namespace) === \SimplePie\SimplePie::NAMESPACE_RSS_090 && in_array(end($this->element), ['title']))
     311            || (end($this->namespace) === \SimplePie\SimplePie::NAMESPACE_RSS_10 && in_array(end($this->element), ['title']))) {
     312                $this->current_xhtml_construct = 0;
     313            }
     314        }
     315    }
     316
     317    public function cdata($parser, $cdata)
     318    {
     319        if ($this->current_xhtml_construct >= 0) {
     320            $this->data['data'] .= htmlspecialchars($cdata, ENT_QUOTES, $this->encoding);
     321        } else {
     322            $this->data['data'] .= $cdata;
     323        }
     324    }
     325
     326    public function tag_close($parser, $tag)
     327    {
     328        if ($this->current_xhtml_construct >= 0) {
     329            $this->current_xhtml_construct--;
     330            if (end($this->namespace) === \SimplePie\SimplePie::NAMESPACE_XHTML && !in_array(end($this->element), ['area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param'])) {
     331                $this->data['data'] .= '</' . end($this->element) . '>';
     332            }
     333        }
     334        if ($this->current_xhtml_construct === -1) {
     335            $this->data = &$this->datas[count($this->datas) - 1];
     336            array_pop($this->datas);
     337        }
     338
     339        array_pop($this->element);
     340        array_pop($this->namespace);
     341        array_pop($this->xml_base);
     342        array_pop($this->xml_base_explicit);
     343        array_pop($this->xml_lang);
     344    }
     345
     346    public function split_ns($string)
     347    {
     348        static $cache = [];
     349        if (!isset($cache[$string])) {
     350            if ($pos = strpos($string, $this->separator)) {
     351                static $separator_length;
     352                if (!$separator_length) {
     353                    $separator_length = strlen($this->separator);
     354                }
     355                $namespace = substr($string, 0, $pos);
     356                $local_name = substr($string, $pos + $separator_length);
     357                if (strtolower($namespace) === \SimplePie\SimplePie::NAMESPACE_ITUNES) {
     358                    $namespace = \SimplePie\SimplePie::NAMESPACE_ITUNES;
     359                }
     360
     361                // Normalize the Media RSS namespaces
     362                if ($namespace === \SimplePie\SimplePie::NAMESPACE_MEDIARSS_WRONG ||
     363                    $namespace === \SimplePie\SimplePie::NAMESPACE_MEDIARSS_WRONG2 ||
     364                    $namespace === \SimplePie\SimplePie::NAMESPACE_MEDIARSS_WRONG3 ||
     365                    $namespace === \SimplePie\SimplePie::NAMESPACE_MEDIARSS_WRONG4 ||
     366                    $namespace === \SimplePie\SimplePie::NAMESPACE_MEDIARSS_WRONG5) {
     367                    $namespace = \SimplePie\SimplePie::NAMESPACE_MEDIARSS;
     368                }
     369                $cache[$string] = [$namespace, $local_name];
     370            } else {
     371                $cache[$string] = ['', $string];
     372            }
     373        }
     374        return $cache[$string];
     375    }
     376
     377    private function parse_hcard($data, $category = false)
     378    {
     379        $name = '';
     380        $link = '';
     381        // Check if h-card is set and pass that information on in the link.
     382        if (isset($data['type']) && in_array('h-card', $data['type'])) {
     383            if (isset($data['properties']['name'][0])) {
     384                $name = $data['properties']['name'][0];
     385            }
     386            if (isset($data['properties']['url'][0])) {
     387                $link = $data['properties']['url'][0];
     388                if ($name === '') {
     389                    $name = $link;
     390                } else {
     391                    // can't have commas in categories.
     392                    $name = str_replace(',', '', $name);
     393                }
     394                $person_tag = $category ? '<span class="person-tag"></span>' : '';
     395                return '<a class="h-card" href="'.$link.'">'.$person_tag.$name.'</a>';
     396            }
     397        }
     398        return $data['value'] ?? '';
     399    }
     400
     401    private function parse_microformats(&$data, $url)
     402    {
     403        $feed_title = '';
     404        $feed_author = null;
     405        $author_cache = [];
     406        $items = [];
     407        $entries = [];
     408        $mf = \Mf2\parse($data, $url);
     409        // First look for an h-feed.
     410        $h_feed = [];
     411        foreach ($mf['items'] as $mf_item) {
     412            if (in_array('h-feed', $mf_item['type'])) {
     413                $h_feed = $mf_item;
     414                break;
     415            }
     416            // Also look for h-feed or h-entry in the children of each top level item.
     417            if (!isset($mf_item['children'][0]['type'])) {
     418                continue;
     419            }
     420            if (in_array('h-feed', $mf_item['children'][0]['type'])) {
     421                $h_feed = $mf_item['children'][0];
     422                // In this case the parent of the h-feed may be an h-card, so use it as
     423                // the feed_author.
     424                if (in_array('h-card', $mf_item['type'])) {
     425                    $feed_author = $mf_item;
     426                }
     427                break;
     428            } elseif (in_array('h-entry', $mf_item['children'][0]['type'])) {
     429                $entries = $mf_item['children'];
     430                // In this case the parent of the h-entry list may be an h-card, so use
     431                // it as the feed_author.
     432                if (in_array('h-card', $mf_item['type'])) {
     433                    $feed_author = $mf_item;
     434                }
     435                break;
     436            }
     437        }
     438        if (isset($h_feed['children'])) {
     439            $entries = $h_feed['children'];
     440            // Also set the feed title and store author from the h-feed if available.
     441            if (isset($mf['items'][0]['properties']['name'][0])) {
     442                $feed_title = $mf['items'][0]['properties']['name'][0];
     443            }
     444            if (isset($mf['items'][0]['properties']['author'][0])) {
     445                $feed_author = $mf['items'][0]['properties']['author'][0];
     446            }
     447        } elseif (count($entries) === 0) {
     448            $entries = $mf['items'];
     449        }
     450        for ($i = 0; $i < count($entries); $i++) {
     451            $entry = $entries[$i];
     452            if (in_array('h-entry', $entry['type'])) {
     453                $item = [];
     454                $title = '';
     455                $description = '';
     456                if (isset($entry['properties']['url'][0])) {
     457                    $link = $entry['properties']['url'][0];
     458                    if (isset($link['value'])) {
     459                        $link = $link['value'];
     460                    }
     461                    $item['link'] = [['data' => $link]];
     462                }
     463                if (isset($entry['properties']['uid'][0])) {
     464                    $guid = $entry['properties']['uid'][0];
     465                    if (isset($guid['value'])) {
     466                        $guid = $guid['value'];
     467                    }
     468                    $item['guid'] = [['data' => $guid]];
     469                }
     470                if (isset($entry['properties']['name'][0])) {
     471                    $title = $entry['properties']['name'][0];
     472                    if (isset($title['value'])) {
     473                        $title = $title['value'];
     474                    }
     475                    $item['title'] = [['data' => $title]];
     476                }
     477                if (isset($entry['properties']['author'][0]) || isset($feed_author)) {
     478                    // author is a special case, it can be plain text or an h-card array.
     479                    // If it's plain text it can also be a url that should be followed to
     480                    // get the actual h-card.
     481                    $author = $entry['properties']['author'][0] ?? $feed_author;
     482                    if (!is_string($author)) {
     483                        $author = $this->parse_hcard($author);
     484                    } elseif (strpos($author, 'http') === 0) {
     485                        if (isset($author_cache[$author])) {
     486                            $author = $author_cache[$author];
     487                        } else {
     488                            $mf = \Mf2\fetch($author);
     489                            foreach ($mf['items'] as $hcard) {
     490                                // Only interested in an h-card by itself in this case.
     491                                if (!in_array('h-card', $hcard['type'])) {
     492                                    continue;
     493                                }
     494                                // It must have a url property matching what we fetched.
     495                                if (!isset($hcard['properties']['url']) ||
     496                                        !(in_array($author, $hcard['properties']['url']))) {
     497                                    continue;
     498                                }
     499                                // Save parse_hcard the trouble of finding the correct url.
     500                                $hcard['properties']['url'][0] = $author;
     501                                // Cache this h-card for the next h-entry to check.
     502                                $author_cache[$author] = $this->parse_hcard($hcard);
     503                                $author = $author_cache[$author];
     504                                break;
     505                            }
     506                        }
     507                    }
     508                    $item['author'] = [['data' => $author]];
     509                }
     510                if (isset($entry['properties']['photo'][0])) {
     511                    // If a photo is also in content, don't need to add it again here.
     512                    $content = '';
     513                    if (isset($entry['properties']['content'][0]['html'])) {
     514                        $content = $entry['properties']['content'][0]['html'];
     515                    }
     516                    $photo_list = [];
     517                    for ($j = 0; $j < count($entry['properties']['photo']); $j++) {
     518                        $photo = $entry['properties']['photo'][$j];
     519                        if (!empty($photo) && strpos($content, $photo) === false) {
     520                            $photo_list[] = $photo;
     521                        }
     522                    }
     523                    // When there's more than one photo show the first and use a lightbox.
     524                    // Need a permanent, unique name for the image set, but don't have
     525                    // anything unique except for the content itself, so use that.
     526                    $count = count($photo_list);
     527                    if ($count > 1) {
     528                        $image_set_id = preg_replace('/[[:^alnum:]]/', '', $photo_list[0]);
     529                        $description = '<p>';
     530                        for ($j = 0; $j < $count; $j++) {
     531                            $hidden = $j === 0 ? '' : 'class="hidden" ';
     532                            $description .= '<a href="'.$photo_list[$j].'" '.$hidden.
     533                                'data-lightbox="image-set-'.$image_set_id.'">'.
     534                                '<img src="'.$photo_list[$j].'"></a>';
     535                        }
     536                        $description .= '<br><b>'.$count.' photos</b></p>';
     537                    } elseif ($count == 1) {
     538                        $description = '<p><img src="'.$photo_list[0].'"></p>';
     539                    }
     540                }
     541                if (isset($entry['properties']['content'][0]['html'])) {
     542                    // e-content['value'] is the same as p-name when they are on the same
     543                    // element. Use this to replace title with a strip_tags version so
     544                    // that alt text from images is not included in the title.
     545                    if ($entry['properties']['content'][0]['value'] === $title) {
     546                        $title = strip_tags($entry['properties']['content'][0]['html']);
     547                        $item['title'] = [['data' => $title]];
     548                    }
     549                    $description .= $entry['properties']['content'][0]['html'];
     550                    if (isset($entry['properties']['in-reply-to'][0])) {
     551                        $in_reply_to = '';
     552                        if (is_string($entry['properties']['in-reply-to'][0])) {
     553                            $in_reply_to = $entry['properties']['in-reply-to'][0];
     554                        } elseif (isset($entry['properties']['in-reply-to'][0]['value'])) {
     555                            $in_reply_to = $entry['properties']['in-reply-to'][0]['value'];
     556                        }
     557                        if ($in_reply_to !== '') {
     558                            $description .= '<p><span class="in-reply-to"></span> '.
     559                                '<a href="'.$in_reply_to.'">'.$in_reply_to.'</a><p>';
     560                        }
     561                    }
     562                    $item['description'] = [['data' => $description]];
     563                }
     564                if (isset($entry['properties']['category'])) {
     565                    $category_csv = '';
     566                    // Categories can also contain h-cards.
     567                    foreach ($entry['properties']['category'] as $category) {
     568                        if ($category_csv !== '') {
     569                            $category_csv .= ', ';
     570                        }
     571                        if (is_string($category)) {
     572                            // Can't have commas in categories.
     573                            $category_csv .= str_replace(',', '', $category);
     574                        } else {
     575                            $category_csv .= $this->parse_hcard($category, true);
     576                        }
     577                    }
     578                    $item['category'] = [['data' => $category_csv]];
     579                }
     580                if (isset($entry['properties']['published'][0])) {
     581                    $timestamp = strtotime($entry['properties']['published'][0]);
     582                    $pub_date = date('F j Y g:ia', $timestamp).' GMT';
     583                    $item['pubDate'] = [['data' => $pub_date]];
     584                }
     585                // The title and description are set to the empty string to represent
     586                // a deleted item (which also makes it an invalid rss item).
     587                if (isset($entry['properties']['deleted'][0])) {
     588                    $item['title'] = [['data' => '']];
     589                    $item['description'] = [['data' => '']];
     590                }
     591                $items[] = ['child' => ['' => $item]];
     592            }
     593        }
     594        // Mimic RSS data format when storing microformats.
     595        $link = [['data' => $url]];
     596        $image = '';
     597        if (!is_string($feed_author) &&
     598                isset($feed_author['properties']['photo'][0])) {
     599            $image = [['child' => ['' => ['url' =>
     600                [['data' => $feed_author['properties']['photo'][0]]]]]]];
     601        }
     602        // Use the name given for the h-feed, or get the title from the html.
     603        if ($feed_title !== '') {
     604            $feed_title = [['data' => htmlspecialchars($feed_title)]];
     605        } elseif ($position = strpos($data, '<title>')) {
     606            $start = $position < 200 ? 0 : $position - 200;
     607            $check = substr($data, $start, 400);
     608            $matches = [];
     609            if (preg_match('/<title>(.+)<\/title>/', $check, $matches)) {
     610                $feed_title = [['data' => htmlspecialchars($matches[1])]];
     611            }
     612        }
     613        $channel = ['channel' => [['child' => ['' =>
     614            ['link' => $link, 'image' => $image, 'title' => $feed_title,
     615                  'item' => $items]]]]];
     616        $rss = [['attribs' => ['' => ['version' => '2.0']],
     617                           'child' => ['' => $channel]]];
     618        $this->data = ['child' => ['' => ['rss' => $rss]]];
     619        return true;
     620    }
     621
     622    private function declare_html_entities()
     623    {
     624        // This is required because the RSS specification says that entity-encoded
     625        // html is allowed, but the xml specification says they must be declared.
     626        return '<!DOCTYPE html [ <!ENTITY nbsp "&#x00A0;"> <!ENTITY iexcl "&#x00A1;"> <!ENTITY cent "&#x00A2;"> <!ENTITY pound "&#x00A3;"> <!ENTITY curren "&#x00A4;"> <!ENTITY yen "&#x00A5;"> <!ENTITY brvbar "&#x00A6;"> <!ENTITY sect "&#x00A7;"> <!ENTITY uml "&#x00A8;"> <!ENTITY copy "&#x00A9;"> <!ENTITY ordf "&#x00AA;"> <!ENTITY laquo "&#x00AB;"> <!ENTITY not "&#x00AC;"> <!ENTITY shy "&#x00AD;"> <!ENTITY reg "&#x00AE;"> <!ENTITY macr "&#x00AF;"> <!ENTITY deg "&#x00B0;"> <!ENTITY plusmn "&#x00B1;"> <!ENTITY sup2 "&#x00B2;"> <!ENTITY sup3 "&#x00B3;"> <!ENTITY acute "&#x00B4;"> <!ENTITY micro "&#x00B5;"> <!ENTITY para "&#x00B6;"> <!ENTITY middot "&#x00B7;"> <!ENTITY cedil "&#x00B8;"> <!ENTITY sup1 "&#x00B9;"> <!ENTITY ordm "&#x00BA;"> <!ENTITY raquo "&#x00BB;"> <!ENTITY frac14 "&#x00BC;"> <!ENTITY frac12 "&#x00BD;"> <!ENTITY frac34 "&#x00BE;"> <!ENTITY iquest "&#x00BF;"> <!ENTITY Agrave "&#x00C0;"> <!ENTITY Aacute "&#x00C1;"> <!ENTITY Acirc "&#x00C2;"> <!ENTITY Atilde "&#x00C3;"> <!ENTITY Auml "&#x00C4;"> <!ENTITY Aring "&#x00C5;"> <!ENTITY AElig "&#x00C6;"> <!ENTITY Ccedil "&#x00C7;"> <!ENTITY Egrave "&#x00C8;"> <!ENTITY Eacute "&#x00C9;"> <!ENTITY Ecirc "&#x00CA;"> <!ENTITY Euml "&#x00CB;"> <!ENTITY Igrave "&#x00CC;"> <!ENTITY Iacute "&#x00CD;"> <!ENTITY Icirc "&#x00CE;"> <!ENTITY Iuml "&#x00CF;"> <!ENTITY ETH "&#x00D0;"> <!ENTITY Ntilde "&#x00D1;"> <!ENTITY Ograve "&#x00D2;"> <!ENTITY Oacute "&#x00D3;"> <!ENTITY Ocirc "&#x00D4;"> <!ENTITY Otilde "&#x00D5;"> <!ENTITY Ouml "&#x00D6;"> <!ENTITY times "&#x00D7;"> <!ENTITY Oslash "&#x00D8;"> <!ENTITY Ugrave "&#x00D9;"> <!ENTITY Uacute "&#x00DA;"> <!ENTITY Ucirc "&#x00DB;"> <!ENTITY Uuml "&#x00DC;"> <!ENTITY Yacute "&#x00DD;"> <!ENTITY THORN "&#x00DE;"> <!ENTITY szlig "&#x00DF;"> <!ENTITY agrave "&#x00E0;"> <!ENTITY aacute "&#x00E1;"> <!ENTITY acirc "&#x00E2;"> <!ENTITY atilde "&#x00E3;"> <!ENTITY auml "&#x00E4;"> <!ENTITY aring "&#x00E5;"> <!ENTITY aelig "&#x00E6;"> <!ENTITY ccedil "&#x00E7;"> <!ENTITY egrave "&#x00E8;"> <!ENTITY eacute "&#x00E9;"> <!ENTITY ecirc "&#x00EA;"> <!ENTITY euml "&#x00EB;"> <!ENTITY igrave "&#x00EC;"> <!ENTITY iacute "&#x00ED;"> <!ENTITY icirc "&#x00EE;"> <!ENTITY iuml "&#x00EF;"> <!ENTITY eth "&#x00F0;"> <!ENTITY ntilde "&#x00F1;"> <!ENTITY ograve "&#x00F2;"> <!ENTITY oacute "&#x00F3;"> <!ENTITY ocirc "&#x00F4;"> <!ENTITY otilde "&#x00F5;"> <!ENTITY ouml "&#x00F6;"> <!ENTITY divide "&#x00F7;"> <!ENTITY oslash "&#x00F8;"> <!ENTITY ugrave "&#x00F9;"> <!ENTITY uacute "&#x00FA;"> <!ENTITY ucirc "&#x00FB;"> <!ENTITY uuml "&#x00FC;"> <!ENTITY yacute "&#x00FD;"> <!ENTITY thorn "&#x00FE;"> <!ENTITY yuml "&#x00FF;"> <!ENTITY OElig "&#x0152;"> <!ENTITY oelig "&#x0153;"> <!ENTITY Scaron "&#x0160;"> <!ENTITY scaron "&#x0161;"> <!ENTITY Yuml "&#x0178;"> <!ENTITY fnof "&#x0192;"> <!ENTITY circ "&#x02C6;"> <!ENTITY tilde "&#x02DC;"> <!ENTITY Alpha "&#x0391;"> <!ENTITY Beta "&#x0392;"> <!ENTITY Gamma "&#x0393;"> <!ENTITY Epsilon "&#x0395;"> <!ENTITY Zeta "&#x0396;"> <!ENTITY Eta "&#x0397;"> <!ENTITY Theta "&#x0398;"> <!ENTITY Iota "&#x0399;"> <!ENTITY Kappa "&#x039A;"> <!ENTITY Lambda "&#x039B;"> <!ENTITY Mu "&#x039C;"> <!ENTITY Nu "&#x039D;"> <!ENTITY Xi "&#x039E;"> <!ENTITY Omicron "&#x039F;"> <!ENTITY Pi "&#x03A0;"> <!ENTITY Rho "&#x03A1;"> <!ENTITY Sigma "&#x03A3;"> <!ENTITY Tau "&#x03A4;"> <!ENTITY Upsilon "&#x03A5;"> <!ENTITY Phi "&#x03A6;"> <!ENTITY Chi "&#x03A7;"> <!ENTITY Psi "&#x03A8;"> <!ENTITY Omega "&#x03A9;"> <!ENTITY alpha "&#x03B1;"> <!ENTITY beta "&#x03B2;"> <!ENTITY gamma "&#x03B3;"> <!ENTITY delta "&#x03B4;"> <!ENTITY epsilon "&#x03B5;"> <!ENTITY zeta "&#x03B6;"> <!ENTITY eta "&#x03B7;"> <!ENTITY theta "&#x03B8;"> <!ENTITY iota "&#x03B9;"> <!ENTITY kappa "&#x03BA;"> <!ENTITY lambda "&#x03BB;"> <!ENTITY mu "&#x03BC;"> <!ENTITY nu "&#x03BD;"> <!ENTITY xi "&#x03BE;"> <!ENTITY omicron "&#x03BF;"> <!ENTITY pi "&#x03C0;"> <!ENTITY rho "&#x03C1;"> <!ENTITY sigmaf "&#x03C2;"> <!ENTITY sigma "&#x03C3;"> <!ENTITY tau "&#x03C4;"> <!ENTITY upsilon "&#x03C5;"> <!ENTITY phi "&#x03C6;"> <!ENTITY chi "&#x03C7;"> <!ENTITY psi "&#x03C8;"> <!ENTITY omega "&#x03C9;"> <!ENTITY thetasym "&#x03D1;"> <!ENTITY upsih "&#x03D2;"> <!ENTITY piv "&#x03D6;"> <!ENTITY ensp "&#x2002;"> <!ENTITY emsp "&#x2003;"> <!ENTITY thinsp "&#x2009;"> <!ENTITY zwnj "&#x200C;"> <!ENTITY zwj "&#x200D;"> <!ENTITY lrm "&#x200E;"> <!ENTITY rlm "&#x200F;"> <!ENTITY ndash "&#x2013;"> <!ENTITY mdash "&#x2014;"> <!ENTITY lsquo "&#x2018;"> <!ENTITY rsquo "&#x2019;"> <!ENTITY sbquo "&#x201A;"> <!ENTITY ldquo "&#x201C;"> <!ENTITY rdquo "&#x201D;"> <!ENTITY bdquo "&#x201E;"> <!ENTITY dagger "&#x2020;"> <!ENTITY Dagger "&#x2021;"> <!ENTITY bull "&#x2022;"> <!ENTITY hellip "&#x2026;"> <!ENTITY permil "&#x2030;"> <!ENTITY prime "&#x2032;"> <!ENTITY Prime "&#x2033;"> <!ENTITY lsaquo "&#x2039;"> <!ENTITY rsaquo "&#x203A;"> <!ENTITY oline "&#x203E;"> <!ENTITY frasl "&#x2044;"> <!ENTITY euro "&#x20AC;"> <!ENTITY image "&#x2111;"> <!ENTITY weierp "&#x2118;"> <!ENTITY real "&#x211C;"> <!ENTITY trade "&#x2122;"> <!ENTITY alefsym "&#x2135;"> <!ENTITY larr "&#x2190;"> <!ENTITY uarr "&#x2191;"> <!ENTITY rarr "&#x2192;"> <!ENTITY darr "&#x2193;"> <!ENTITY harr "&#x2194;"> <!ENTITY crarr "&#x21B5;"> <!ENTITY lArr "&#x21D0;"> <!ENTITY uArr "&#x21D1;"> <!ENTITY rArr "&#x21D2;"> <!ENTITY dArr "&#x21D3;"> <!ENTITY hArr "&#x21D4;"> <!ENTITY forall "&#x2200;"> <!ENTITY part "&#x2202;"> <!ENTITY exist "&#x2203;"> <!ENTITY empty "&#x2205;"> <!ENTITY nabla "&#x2207;"> <!ENTITY isin "&#x2208;"> <!ENTITY notin "&#x2209;"> <!ENTITY ni "&#x220B;"> <!ENTITY prod "&#x220F;"> <!ENTITY sum "&#x2211;"> <!ENTITY minus "&#x2212;"> <!ENTITY lowast "&#x2217;"> <!ENTITY radic "&#x221A;"> <!ENTITY prop "&#x221D;"> <!ENTITY infin "&#x221E;"> <!ENTITY ang "&#x2220;"> <!ENTITY and "&#x2227;"> <!ENTITY or "&#x2228;"> <!ENTITY cap "&#x2229;"> <!ENTITY cup "&#x222A;"> <!ENTITY int "&#x222B;"> <!ENTITY there4 "&#x2234;"> <!ENTITY sim "&#x223C;"> <!ENTITY cong "&#x2245;"> <!ENTITY asymp "&#x2248;"> <!ENTITY ne "&#x2260;"> <!ENTITY equiv "&#x2261;"> <!ENTITY le "&#x2264;"> <!ENTITY ge "&#x2265;"> <!ENTITY sub "&#x2282;"> <!ENTITY sup "&#x2283;"> <!ENTITY nsub "&#x2284;"> <!ENTITY sube "&#x2286;"> <!ENTITY supe "&#x2287;"> <!ENTITY oplus "&#x2295;"> <!ENTITY otimes "&#x2297;"> <!ENTITY perp "&#x22A5;"> <!ENTITY sdot "&#x22C5;"> <!ENTITY lceil "&#x2308;"> <!ENTITY rceil "&#x2309;"> <!ENTITY lfloor "&#x230A;"> <!ENTITY rfloor "&#x230B;"> <!ENTITY lang "&#x2329;"> <!ENTITY rang "&#x232A;"> <!ENTITY loz "&#x25CA;"> <!ENTITY spades "&#x2660;"> <!ENTITY clubs "&#x2663;"> <!ENTITY hearts "&#x2665;"> <!ENTITY diams "&#x2666;"> ]>';
     627    }
    679628}
     629
     630class_alias('SimplePie\Parser', 'SimplePie_Parser');
  • trunk/src/wp-includes/SimplePie/src/Rating.php

    r59140 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie;
     46
    4447/**
    4548 * Handles `<media:rating>` or `<itunes:explicit>` tags as defined in Media RSS and iTunes RSS respectively
    4649 *
    47  * Used by {@see SimplePie_Enclosure::get_rating()} and {@see SimplePie_Enclosure::get_ratings()}
     50 * Used by {@see \SimplePie\Enclosure::get_rating()} and {@see \SimplePie\Enclosure::get_ratings()}
    4851 *
    49  * This class can be overloaded with {@see SimplePie::set_rating_class()}
     52 * This class can be overloaded with {@see \SimplePie\SimplePie::set_rating_class()}
    5053 *
    5154 * @package SimplePie
    5255 * @subpackage API
    5356 */
    54 class SimplePie_Rating
     57class Rating
    5558{
    56     /**
    57     * Rating scheme
    58     *
    59     * @var string
    60     * @see get_scheme()
    61     */
    62     var $scheme;
     59    /**
     60    * Rating scheme
     61    *
     62    * @var string
     63    * @see get_scheme()
     64    */
     65    public $scheme;
    6366
    64     /**
    65     * Rating value
    66     *
    67     * @var string
    68     * @see get_value()
    69     */
    70     var $value;
     67    /**
     68    * Rating value
     69    *
     70    * @var string
     71    * @see get_value()
     72    */
     73    public $value;
    7174
    72     /**
    73     * Constructor, used to input the data
    74     *
    75     * For documentation on all the parameters, see the corresponding
    76     * properties and their accessors
    77     */
    78     public function __construct($scheme = null, $value = null)
    79     {
    80         $this->scheme = $scheme;
    81         $this->value = $value;
    82     }
     75    /**
     76    * Constructor, used to input the data
     77    *
     78    * For documentation on all the parameters, see the corresponding
     79    * properties and their accessors
     80    */
     81    public function __construct($scheme = null, $value = null)
     82    {
     83        $this->scheme = $scheme;
     84        $this->value = $value;
     85    }
    8386
    84     /**
    85     * String-ified version
    86     *
    87     * @return string
    88     */
    89     public function __toString()
    90     {
    91         // There is no $this->data here
    92         return md5(serialize($this));
    93     }
     87    /**
     88    * String-ified version
     89    *
     90    * @return string
     91    */
     92    public function __toString()
     93    {
     94        // There is no $this->data here
     95        return md5(serialize($this));
     96    }
    9497
    95     /**
    96      * Get the organizational scheme for the rating
    97      *
    98      * @return string|null
    99      */
    100     public function get_scheme()
    101     {
    102         if ($this->scheme !== null)
    103         {
    104             return $this->scheme;
    105         }
     98    /**
     99     * Get the organizational scheme for the rating
     100     *
     101     * @return string|null
     102     */
     103    public function get_scheme()
     104    {
     105        if ($this->scheme !== null) {
     106            return $this->scheme;
     107        }
    106108
    107         return null;
    108     }
     109        return null;
     110    }
    109111
    110     /**
    111      * Get the value of the rating
    112      *
    113      * @return string|null
    114      */
    115     public function get_value()
    116     {
    117         if ($this->value !== null)
    118         {
    119             return $this->value;
    120         }
     112    /**
     113     * Get the value of the rating
     114     *
     115     * @return string|null
     116     */
     117    public function get_value()
     118    {
     119        if ($this->value !== null) {
     120            return $this->value;
     121        }
    121122
    122         return null;
    123     }
     123        return null;
     124    }
    124125}
     126
     127class_alias('SimplePie\Rating', 'SimplePie_Rating');
  • trunk/src/wp-includes/SimplePie/src/Registry.php

    r59140 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie;
     46
     47use SimplePie\Content\Type\Sniffer;
     48use SimplePie\Parse\Date;
     49use SimplePie\XML\Declaration\Parser as DeclarationParser;
     50
    4451/**
    4552 * Handles creating objects and calling methods
    4653 *
    47  * Access this via {@see SimplePie::get_registry()}
     54 * Access this via {@see \SimplePie\SimplePie::get_registry()}
    4855 *
    4956 * @package SimplePie
    5057 */
    51 class SimplePie_Registry
     58class Registry
    5259{
    53     /**
    54      * Default class mapping
    55      *
    56      * Overriding classes *must* subclass these.
    57      *
    58      * @var array
    59      */
    60     protected $default = array(
    61         'Cache' => 'SimplePie_Cache',
    62         'Locator' => 'SimplePie_Locator',
    63         'Parser' => 'SimplePie_Parser',
    64         'File' => 'SimplePie_File',
    65         'Sanitize' => 'SimplePie_Sanitize',
    66         'Item' => 'SimplePie_Item',
    67         'Author' => 'SimplePie_Author',
    68         'Category' => 'SimplePie_Category',
    69         'Enclosure' => 'SimplePie_Enclosure',
    70         'Caption' => 'SimplePie_Caption',
    71         'Copyright' => 'SimplePie_Copyright',
    72         'Credit' => 'SimplePie_Credit',
    73         'Rating' => 'SimplePie_Rating',
    74         'Restriction' => 'SimplePie_Restriction',
    75         'Content_Type_Sniffer' => 'SimplePie_Content_Type_Sniffer',
    76         'Source' => 'SimplePie_Source',
    77         'Misc' => 'SimplePie_Misc',
    78         'XML_Declaration_Parser' => 'SimplePie_XML_Declaration_Parser',
    79         'Parse_Date' => 'SimplePie_Parse_Date',
    80     );
    81 
    82     /**
    83      * Class mapping
    84      *
    85      * @see register()
    86      * @var array
    87      */
    88     protected $classes = array();
    89 
    90     /**
    91      * Legacy classes
    92      *
    93      * @see register()
    94      * @var array
    95      */
    96     protected $legacy = array();
    97 
    98     /**
    99      * Constructor
    100      *
    101      * No-op
    102      */
    103     public function __construct() { }
    104 
    105     /**
    106      * Register a class
    107      *
    108      * @param string $type See {@see $default} for names
    109      * @param string $class Class name, must subclass the corresponding default
    110      * @param bool $legacy Whether to enable legacy support for this class
    111      * @return bool Successfulness
    112      */
    113     public function register($type, $class, $legacy = false)
    114     {
    115         if (!@is_subclass_of($class, $this->default[$type]))
    116         {
    117             return false;
    118         }
    119 
    120         $this->classes[$type] = $class;
    121 
    122         if ($legacy)
    123         {
    124             $this->legacy[] = $class;
    125         }
    126 
    127         return true;
    128     }
    129 
    130     /**
    131      * Get the class registered for a type
    132      *
    133      * Where possible, use {@see create()} or {@see call()} instead
    134      *
    135      * @param string $type
    136      * @return string|null
    137      */
    138     public function get_class($type)
    139     {
    140         if (!empty($this->classes[$type]))
    141         {
    142             return $this->classes[$type];
    143         }
    144         if (!empty($this->default[$type]))
    145         {
    146             return $this->default[$type];
    147         }
    148 
    149         return null;
    150     }
    151 
    152     /**
    153      * Create a new instance of a given type
    154      *
    155      * @param string $type
    156      * @param array $parameters Parameters to pass to the constructor
    157      * @return object Instance of class
    158      */
    159     public function &create($type, $parameters = array())
    160     {
    161         $class = $this->get_class($type);
    162 
    163         if (in_array($class, $this->legacy))
    164         {
    165             switch ($type)
    166             {
    167                 case 'locator':
    168                     // Legacy: file, timeout, useragent, file_class, max_checked_feeds, content_type_sniffer_class
    169                     // Specified: file, timeout, useragent, max_checked_feeds
    170                     $replacement = array($this->get_class('file'), $parameters[3], $this->get_class('content_type_sniffer'));
    171                     array_splice($parameters, 3, 1, $replacement);
    172                     break;
    173             }
    174         }
    175 
    176         if (!method_exists($class, '__construct'))
    177         {
    178             $instance = new $class;
    179         }
    180         else
    181         {
    182             $reflector = new ReflectionClass($class);
    183             $instance = $reflector->newInstanceArgs($parameters);
    184         }
    185 
    186         if (method_exists($instance, 'set_registry'))
    187         {
    188             $instance->set_registry($this);
    189         }
    190         return $instance;
    191     }
    192 
    193     /**
    194      * Call a static method for a type
    195      *
    196      * @param string $type
    197      * @param string $method
    198      * @param array $parameters
    199      * @return mixed
    200      */
    201     public function &call($type, $method, $parameters = array())
    202     {
    203         $class = $this->get_class($type);
    204 
    205         if (in_array($class, $this->legacy))
    206         {
    207             switch ($type)
    208             {
    209                 case 'Cache':
    210                     // For backwards compatibility with old non-static
    211                     // Cache::create() methods in PHP < 8.0.
    212                     // No longer supported as of PHP 8.0.
    213                     if ($method === 'get_handler')
    214                     {
    215                         $result = @call_user_func_array(array($class, 'create'), $parameters);
    216                         return $result;
    217                     }
    218                     break;
    219             }
    220         }
    221 
    222         $result = call_user_func_array(array($class, $method), $parameters);
    223         return $result;
    224     }
     60    /**
     61     * Default class mapping
     62     *
     63     * Overriding classes *must* subclass these.
     64     *
     65     * @var array<class-string, class-string>
     66     */
     67    protected $default = [
     68        Cache::class => Cache::class,
     69        Locator::class => Locator::class,
     70        Parser::class => Parser::class,
     71        File::class => File::class,
     72        Sanitize::class => Sanitize::class,
     73        Item::class => Item::class,
     74        Author::class => Author::class,
     75        Category::class => Category::class,
     76        Enclosure::class => Enclosure::class,
     77        Caption::class => Caption::class,
     78        Copyright::class => Copyright::class,
     79        Credit::class => Credit::class,
     80        Rating::class => Rating::class,
     81        Restriction::class => Restriction::class,
     82        Sniffer::class => Sniffer::class,
     83        Source::class => Source::class,
     84        Misc::class => Misc::class,
     85        DeclarationParser::class => DeclarationParser::class,
     86        Date::class => Date::class,
     87    ];
     88
     89    /**
     90     * Class mapping
     91     *
     92     * @see register()
     93     * @var array
     94     */
     95    protected $classes = [];
     96
     97    /**
     98     * Legacy classes
     99     *
     100     * @see register()
     101     * @var array<class-string>
     102     */
     103    protected $legacy = [];
     104
     105    /**
     106     * Legacy types
     107     *
     108     * @see register()
     109     * @var array<string, class-string>
     110     */
     111    private $legacyTypes = [
     112        'Cache' => Cache::class,
     113        'Locator' => Locator::class,
     114        'Parser' => Parser::class,
     115        'File' => File::class,
     116        'Sanitize' => Sanitize::class,
     117        'Item' => Item::class,
     118        'Author' => Author::class,
     119        'Category' => Category::class,
     120        'Enclosure' => Enclosure::class,
     121        'Caption' => Caption::class,
     122        'Copyright' => Copyright::class,
     123        'Credit' => Credit::class,
     124        'Rating' => Rating::class,
     125        'Restriction' => Restriction::class,
     126        'Content_Type_Sniffer' => Sniffer::class,
     127        'Source' => Source::class,
     128        'Misc' => Misc::class,
     129        'XML_Declaration_Parser' => DeclarationParser::class,
     130        'Parse_Date' => Date::class,
     131    ];
     132
     133    /**
     134     * Constructor
     135     *
     136     * No-op
     137     */
     138    public function __construct()
     139    {
     140    }
     141
     142    /**
     143     * Register a class
     144     *
     145     * @param string $type See {@see $default} for names
     146     * @param class-string $class Class name, must subclass the corresponding default
     147     * @param bool $legacy Whether to enable legacy support for this class
     148     * @return bool Successfulness
     149     */
     150    public function register($type, $class, $legacy = false)
     151    {
     152        if (array_key_exists($type, $this->legacyTypes)) {
     153            // trigger_error(sprintf('"%s"(): Using argument #1 ($type) with value "%s" is deprecated since SimplePie 1.8.0, use class-string "%s" instead.', __METHOD__, $type, $this->legacyTypes[$type]), \E_USER_DEPRECATED);
     154
     155            $type = $this->legacyTypes[$type];
     156        }
     157
     158        if (!array_key_exists($type, $this->default)) {
     159            return false;
     160        }
     161
     162        if (!class_exists($class)) {
     163            return false;
     164        }
     165
     166        /** @var string */
     167        $base_class = $this->default[$type];
     168
     169        if (!is_subclass_of($class, $base_class)) {
     170            return false;
     171        }
     172
     173        $this->classes[$type] = $class;
     174
     175        if ($legacy) {
     176            $this->legacy[] = $class;
     177        }
     178
     179        return true;
     180    }
     181
     182    /**
     183     * Get the class registered for a type
     184     *
     185     * Where possible, use {@see create()} or {@see call()} instead
     186     *
     187     * @template T
     188     * @param class-string<T> $type
     189     * @return class-string<T>|null
     190     */
     191    public function get_class($type)
     192    {
     193        if (array_key_exists($type, $this->legacyTypes)) {
     194            // trigger_error(sprintf('"%s"(): Using argument #1 ($type) with value "%s" is deprecated since SimplePie 1.8.0, use class-string "%s" instead.', __METHOD__, $type, $this->legacyTypes[$type]), \E_USER_DEPRECATED);
     195
     196            $type = $this->legacyTypes[$type];
     197        }
     198
     199        if (!array_key_exists($type, $this->default)) {
     200            return null;
     201        }
     202
     203        $class = $this->default[$type];
     204
     205        if (array_key_exists($type, $this->classes)) {
     206            $class = $this->classes[$type];
     207        }
     208
     209        return $class;
     210    }
     211
     212    /**
     213     * Create a new instance of a given type
     214     *
     215     * @template T class-string $type
     216     * @param class-string<T> $type
     217     * @param array $parameters Parameters to pass to the constructor
     218     * @return T Instance of class
     219     */
     220    public function &create($type, $parameters = [])
     221    {
     222        $class = $this->get_class($type);
     223
     224        if (!method_exists($class, '__construct')) {
     225            $instance = new $class();
     226        } else {
     227            $reflector = new \ReflectionClass($class);
     228            $instance = $reflector->newInstanceArgs($parameters);
     229        }
     230
     231        if ($instance instanceof RegistryAware) {
     232            $instance->set_registry($this);
     233        } elseif (method_exists($instance, 'set_registry')) {
     234            trigger_error(sprintf('Using the method "set_registry()" without implementing "%s" is deprecated since SimplePie 1.8.0, implement "%s" in "%s".', RegistryAware::class, RegistryAware::class, $class), \E_USER_DEPRECATED);
     235            $instance->set_registry($this);
     236        }
     237        return $instance;
     238    }
     239
     240    /**
     241     * Call a static method for a type
     242     *
     243     * @param class-string $type
     244     * @param string $method
     245     * @param array $parameters
     246     * @return mixed
     247     */
     248    public function &call($type, $method, $parameters = [])
     249    {
     250        $class = $this->get_class($type);
     251
     252        if (in_array($class, $this->legacy)) {
     253            switch ($type) {
     254                case Cache::class:
     255                    // For backwards compatibility with old non-static
     256                    // Cache::create() methods in PHP < 8.0.
     257                    // No longer supported as of PHP 8.0.
     258                    if ($method === 'get_handler') {
     259                        $result = @call_user_func_array([$class, 'create'], $parameters);
     260                        return $result;
     261                    }
     262                    break;
     263            }
     264        }
     265
     266        $result = call_user_func_array([$class, $method], $parameters);
     267        return $result;
     268    }
    225269}
     270
     271class_alias('SimplePie\Registry', 'SimplePie_Registry');
  • trunk/src/wp-includes/SimplePie/src/Restriction.php

    r59140 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie;
     46
    4447/**
    4548 * Handles `<media:restriction>` as defined in Media RSS
    4649 *
    47  * Used by {@see SimplePie_Enclosure::get_restriction()} and {@see SimplePie_Enclosure::get_restrictions()}
     50 * Used by {@see \SimplePie\Enclosure::get_restriction()} and {@see \SimplePie\Enclosure::get_restrictions()}
    4851 *
    49  * This class can be overloaded with {@see SimplePie::set_restriction_class()}
     52 * This class can be overloaded with {@see \SimplePie\SimplePie::set_restriction_class()}
    5053 *
    5154 * @package SimplePie
    5255 * @subpackage API
    5356 */
    54 class SimplePie_Restriction
     57class Restriction
    5558{
    56     /**
    57     * Relationship ('allow'/'deny')
    58     *
    59     * @var string
    60     * @see get_relationship()
    61     */
    62     var $relationship;
     59    /**
     60    * Relationship ('allow'/'deny')
     61    *
     62    * @var string
     63    * @see get_relationship()
     64    */
     65    public $relationship;
    6366
    64     /**
    65     * Type of restriction
    66     *
    67     * @var string
    68     * @see get_type()
    69     */
    70     var $type;
     67    /**
     68    * Type of restriction
     69    *
     70    * @var string
     71    * @see get_type()
     72    */
     73    public $type;
    7174
    72     /**
    73     * Restricted values
    74     *
    75     * @var string
    76     * @see get_value()
    77     */
    78     var $value;
     75    /**
     76    * Restricted values
     77    *
     78    * @var string
     79    * @see get_value()
     80    */
     81    public $value;
    7982
    80     /**
    81     * Constructor, used to input the data
    82     *
    83     * For documentation on all the parameters, see the corresponding
    84     * properties and their accessors
    85     */
    86     public function __construct($relationship = null, $type = null, $value = null)
    87     {
    88         $this->relationship = $relationship;
    89         $this->type = $type;
    90         $this->value = $value;
    91     }
     83    /**
     84    * Constructor, used to input the data
     85    *
     86    * For documentation on all the parameters, see the corresponding
     87    * properties and their accessors
     88    */
     89    public function __construct($relationship = null, $type = null, $value = null)
     90    {
     91        $this->relationship = $relationship;
     92        $this->type = $type;
     93        $this->value = $value;
     94    }
    9295
    93     /**
    94     * String-ified version
    95     *
    96     * @return string
    97     */
    98     public function __toString()
    99     {
    100         // There is no $this->data here
    101         return md5(serialize($this));
    102     }
     96    /**
     97    * String-ified version
     98    *
     99    * @return string
     100    */
     101    public function __toString()
     102    {
     103        // There is no $this->data here
     104        return md5(serialize($this));
     105    }
    103106
    104     /**
    105      * Get the relationship
    106      *
    107      * @return string|null Either 'allow' or 'deny'
    108      */
    109     public function get_relationship()
    110     {
    111         if ($this->relationship !== null)
    112         {
    113             return $this->relationship;
    114         }
     107    /**
     108     * Get the relationship
     109     *
     110     * @return string|null Either 'allow' or 'deny'
     111     */
     112    public function get_relationship()
     113    {
     114        if ($this->relationship !== null) {
     115            return $this->relationship;
     116        }
    115117
    116         return null;
    117     }
     118        return null;
     119    }
    118120
    119     /**
    120      * Get the type
    121      *
    122      * @return string|null
    123      */
    124     public function get_type()
    125     {
    126         if ($this->type !== null)
    127         {
    128             return $this->type;
    129         }
     121    /**
     122     * Get the type
     123     *
     124     * @return string|null
     125     */
     126    public function get_type()
     127    {
     128        if ($this->type !== null) {
     129            return $this->type;
     130        }
    130131
    131         return null;
    132     }
     132        return null;
     133    }
    133134
    134     /**
    135      * Get the list of restricted things
    136      *
    137      * @return string|null
    138      */
    139     public function get_value()
    140     {
    141         if ($this->value !== null)
    142         {
    143             return $this->value;
    144         }
     135    /**
     136     * Get the list of restricted things
     137     *
     138     * @return string|null
     139     */
     140    public function get_value()
     141    {
     142        if ($this->value !== null) {
     143            return $this->value;
     144        }
    145145
    146         return null;
    147     }
     146        return null;
     147    }
    148148}
     149
     150class_alias('SimplePie\Restriction', 'SimplePie_Restriction');
  • trunk/src/wp-includes/SimplePie/src/Sanitize.php

    r59140 r59141  
    11<?php
     2
    23/**
    34 * SimplePie
     
    67 * Takes the hard work out of managing a complete RSS/Atom solution.
    78 *
    8  * Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
     9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
    910 * All rights reserved.
    1011 *
     
    4243 */
    4344
     45namespace SimplePie;
     46
     47use InvalidArgumentException;
     48use SimplePie\Cache\Base;
     49use SimplePie\Cache\BaseDataCache;
     50use SimplePie\Cache\CallableNameFilter;
     51use SimplePie\Cache\DataCache;
     52use SimplePie\Cache\NameFilter;
     53
    4454/**
    4555 * Used for data cleanup and post-processing
    4656 *
    4757 *
    48  * This class can be overloaded with {@see SimplePie::set_sanitize_class()}
     58 * This class can be overloaded with {@see \SimplePie\SimplePie::set_sanitize_class()}
    4959 *
    5060 * @package SimplePie
    5161 * @todo Move to using an actual HTML parser (this will allow tags to be properly stripped, and to switch between HTML and XHTML), this will also make it easier to shorten a string while preserving HTML tags
    5262 */
    53 class SimplePie_Sanitize
     63class Sanitize implements RegistryAware
    5464{
    55     // Private vars
    56     var $base;
    57 
    58     // Options
    59     var $remove_div = true;
    60     var $image_handler = '';
    61     var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style');
    62     var $encode_instead_of_strip = false;
    63     var $strip_attributes = array('bgsound', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc');
    64     var $add_attributes = array('audio' => array('preload' => 'none'), 'iframe' => array('sandbox' => 'allow-scripts allow-same-origin'), 'video' => array('preload' => 'none'));
    65     var $strip_comments = false;
    66     var $output_encoding = 'UTF-8';
    67     var $enable_cache = true;
    68     var $cache_location = './cache';
    69     var $cache_name_function = 'md5';
    70     var $timeout = 10;
    71     var $useragent = '';
    72     var $force_fsockopen = false;
    73     var $replace_url_attributes = null;
    74     var $registry;
    75 
    76     /**
    77      * List of domains for which to force HTTPS.
    78      * @see SimplePie_Sanitize::set_https_domains()
    79      * Array is a tree split at DNS levels. Example:
    80      * array('biz' => true, 'com' => array('example' => true), 'net' => array('example' => array('www' => true)))
    81      */
    82     var $https_domains = array();
    83 
    84     public function __construct()
    85     {
    86         // Set defaults
    87         $this->set_url_replacements(null);
    88     }
    89 
    90     public function remove_div($enable = true)
    91     {
    92         $this->remove_div = (bool) $enable;
    93     }
    94 
    95     public function set_image_handler($page = false)
    96     {
    97         if ($page)
    98         {
    99             $this->image_handler = (string) $page;
    100         }
    101         else
    102         {
    103             $this->image_handler = false;
    104         }
    105     }
    106 
    107     public function set_registry(SimplePie_Registry $registry)
    108     {
    109         $this->registry = $registry;
    110     }
    111 
    112     public function pass_cache_data($enable_cache = true, $cache_location = './cache', $cache_name_function = 'md5', $cache_class = 'SimplePie_Cache')
    113     {
    114         if (isset($enable_cache))
    115         {
    116             $this->enable_cache = (bool) $enable_cache;
    117         }
    118 
    119         if ($cache_location)
    120         {
    121             $this->cache_location = (string) $cache_location;
    122         }
    123 
    124         if ($cache_name_function)
    125         {
    126             $this->cache_name_function = (string) $cache_name_function;
    127         }
    128     }
    129 
    130     public function pass_file_data($file_class = 'SimplePie_File', $timeout = 10, $useragent = '', $force_fsockopen = false)
    131     {
    132         if ($timeout)
    133         {
    134             $this->timeout = (string) $timeout;
    135         }
    136 
    137         if ($useragent)
    138         {
    139             $this->useragent = (string) $useragent;
    140         }
    141 
    142         if ($force_fsockopen)
    143         {
    144             $this->force_fsockopen = (string) $force_fsockopen;
    145         }
    146     }
    147 
    148     public function strip_htmltags($tags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'))
    149     {
    150         if ($tags)
    151         {
    152             if (is_array($tags))
    153             {
    154                 $this->strip_htmltags = $tags;
    155             }
    156             else
    157             {
    158                 $this->strip_htmltags = explode(',', $tags);
    159             }
    160         }
    161         else
    162         {
    163             $this->strip_htmltags = false;
    164         }
    165     }
    166 
    167     public function encode_instead_of_strip($encode = false)
    168     {
    169         $this->encode_instead_of_strip = (bool) $encode;
    170     }
    171 
    172     public function strip_attributes($attribs = array('bgsound', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'))
    173     {
    174         if ($attribs)
    175         {
    176             if (is_array($attribs))
    177             {
    178                 $this->strip_attributes = $attribs;
    179             }
    180             else
    181             {
    182                 $this->strip_attributes = explode(',', $attribs);
    183             }
    184         }
    185         else
    186         {
    187             $this->strip_attributes = false;
    188         }
    189     }
    190 
    191     public function add_attributes($attribs = array('audio' => array('preload' => 'none'), 'iframe' => array('sandbox' => 'allow-scripts allow-same-origin'), 'video' => array('preload' => 'none')))
    192     {
    193         if ($attribs)
    194         {
    195             if (is_array($attribs))
    196             {
    197                 $this->add_attributes = $attribs;
    198             }
    199             else
    200             {
    201                 $this->add_attributes = explode(',', $attribs);
    202             }
    203         }
    204         else
    205         {
    206             $this->add_attributes = false;
    207         }
    208     }
    209 
    210     public function strip_comments($strip = false)
    211     {
    212         $this->strip_comments = (bool) $strip;
    213     }
    214 
    215     public function set_output_encoding($encoding = 'UTF-8')
    216     {
    217         $this->output_encoding = (string) $encoding;
    218     }
    219 
    220     /**
    221      * Set element/attribute key/value pairs of HTML attributes
    222      * containing URLs that need to be resolved relative to the feed
    223      *
    224      * Defaults to |a|@href, |area|@href, |blockquote|@cite, |del|@cite,
    225      * |form|@action, |img|@longdesc, |img|@src, |input|@src, |ins|@cite,
    226      * |q|@cite
    227      *
    228      * @since 1.0
    229      * @param array|null $element_attribute Element/attribute key/value pairs, null for default
    230      */
    231     public function set_url_replacements($element_attribute = null)
    232     {
    233         if ($element_attribute === null)
    234         {
    235             $element_attribute = array(
    236                 'a' => 'href',
    237                 'area' => 'href',
    238                 'blockquote' => 'cite',
    239                 'del' => 'cite',
    240                 'form' => 'action',
    241                 'img' => array(
    242                     'longdesc',
    243                     'src'
    244                 ),
    245                 'input' => 'src',
    246                 'ins' => 'cite',
    247                 'q' => 'cite'
    248             );
    249         }
    250         $this->replace_url_attributes = (array) $element_attribute;
    251     }
    252 
    253     /**
    254      * Set the list of domains for which to force HTTPS.
    255      * @see SimplePie_Misc::https_url()
    256      * Example array('biz', 'example.com', 'example.org', 'www.example.net');
    257      */
    258     public function set_https_domains($domains)
    259     {
    260         $this->https_domains = array();
    261         foreach ($domains as $domain)
    262         {
    263             $domain = trim($domain, ". \t\n\r\0\x0B");
    264             $segments = array_reverse(explode('.', $domain));
    265             $node =& $this->https_domains;
    266             foreach ($segments as $segment)
    267             {//Build a tree
    268                 if ($node === true)
    269                 {
    270                     break;
    271                 }
    272                 if (!isset($node[$segment]))
    273                 {
    274                     $node[$segment] = array();
    275                 }
    276                 $node =& $node[$segment];
    277             }
    278             $node = true;
    279         }
    280     }
    281 
    282     /**
    283      * Check if the domain is in the list of forced HTTPS.
    284      */
    285     protected function is_https_domain($domain)
    286     {
    287         $domain = trim($domain, '. ');
    288         $segments = array_reverse(explode('.', $domain));
    289         $node =& $this->https_domains;
    290         foreach ($segments as $segment)
    291         {//Explore the tree
    292             if (isset($node[$segment]))
    293             {
    294                 $node =& $node[$segment];
    295             }
    296             else
    297             {
    298                 break;
    299             }
    300         }
    301         return $node === true;
    302     }
    303 
    304     /**
    305      * Force HTTPS for selected Web sites.
    306      */
    307     public function https_url($url)
    308     {
    309         return (strtolower(substr($url, 0, 7)) === 'http://') &&
    310             $this->is_https_domain(parse_url($url, PHP_URL_HOST)) ?
    311             substr_replace($url, 's', 4, 0) :   //Add the 's' to HTTPS
    312             $url;
    313     }
    314 
    315     public function sanitize($data, $type, $base = '')
    316     {
    317         $data = trim($data);
    318         if ($data !== '' || $type & SIMPLEPIE_CONSTRUCT_IRI)
    319         {
    320             if ($type & SIMPLEPIE_CONSTRUCT_MAYBE_HTML)
    321             {
    322                 if (preg_match('/(&(#(x[0-9a-fA-F]+|[0-9]+)|[a-zA-Z0-9]+)|<\/[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>)/', $data))
    323                 {
    324                     $type |= SIMPLEPIE_CONSTRUCT_HTML;
    325                 }
    326                 else
    327                 {
    328                     $type |= SIMPLEPIE_CONSTRUCT_TEXT;
    329                 }
    330             }
    331 
    332             if ($type & SIMPLEPIE_CONSTRUCT_BASE64)
    333             {
    334                 $data = base64_decode($data);
    335             }
    336 
    337             if ($type & (SIMPLEPIE_CONSTRUCT_HTML | SIMPLEPIE_CONSTRUCT_XHTML))
    338             {
    339 
    340                 if (!class_exists('DOMDocument'))
    341                 {
    342                     throw new SimplePie_Exception('DOMDocument not found, unable to use sanitizer');
    343                 }
    344                 $document = new DOMDocument();
    345                 $document->encoding = 'UTF-8';
    346 
    347                 $data = $this->preprocess($data, $type);
    348 
    349                 set_error_handler(array('SimplePie_Misc', 'silence_errors'));
    350                 $document->loadHTML($data);
    351                 restore_error_handler();
    352 
    353                 $xpath = new DOMXPath($document);
    354 
    355                 // Strip comments
    356                 if ($this->strip_comments)
    357                 {
    358                     $comments = $xpath->query('//comment()');
    359 
    360                     foreach ($comments as $comment)
    361                     {
    362                         $comment->parentNode->removeChild($comment);
    363                     }
    364                 }
    365 
    366                 // Strip out HTML tags and attributes that might cause various security problems.
    367                 // Based on recommendations by Mark Pilgrim at:
    368                 // http://diveintomark.org/archives/2003/06/12/how_to_consume_rss_safely
    369                 if ($this->strip_htmltags)
    370                 {
    371                     foreach ($this->strip_htmltags as $tag)
    372                     {
    373                         $this->strip_tag($tag, $document, $xpath, $type);
    374                     }
    375                 }
    376 
    377                 if ($this->strip_attributes)
    378                 {
    379                     foreach ($this->strip_attributes as $attrib)
    380                     {
    381                         $this->strip_attr($attrib, $xpath);
    382                     }
    383                 }
    384 
    385                 if ($this->add_attributes)
    386                 {
    387                     foreach ($this->add_attributes as $tag => $valuePairs)
    388                     {
    389                         $this->add_attr($tag, $valuePairs, $document);
    390                     }
    391                 }
    392 
    393                 // Replace relative URLs
    394                 $this->base = $base;
    395                 foreach ($this->replace_url_attributes as $element => $attributes)
    396                 {
    397                     $this->replace_urls($document, $element, $attributes);
    398                 }
    399 
    400                 // If image handling (caching, etc.) is enabled, cache and rewrite all the image tags.
    401                 if (isset($this->image_handler) && ((string) $this->image_handler) !== '' && $this->enable_cache)
    402                 {
    403                     $images = $document->getElementsByTagName('img');
    404                     foreach ($images as $img)
    405                     {
    406                         if ($img->hasAttribute('src'))
    407                         {
    408                             $image_url = call_user_func($this->cache_name_function, $img->getAttribute('src'));
    409                             $cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, $image_url, 'spi'));
    410 
    411                             if ($cache->load())
    412                             {
    413                                 $img->setAttribute('src', $this->image_handler . $image_url);
    414                             }
    415                             else
    416                             {
    417                                 $file = $this->registry->create('File', array($img->getAttribute('src'), $this->timeout, 5, array('X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']), $this->useragent, $this->force_fsockopen));
    418                                 $headers = $file->headers;
    419 
    420                                 if ($file->success && ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300)))
    421                                 {
    422                                     if ($cache->save(array('headers' => $file->headers, 'body' => $file->body)))
    423                                     {
    424                                         $img->setAttribute('src', $this->image_handler . $image_url);
    425                                     }
    426                                     else
    427                                     {
    428                                         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);
    429                                     }
    430                                 }
    431                             }
    432                         }
    433                     }
    434                 }
    435 
    436                 // Get content node
    437                 $div = $document->getElementsByTagName('body')->item(0)->firstChild;
    438                 // Finally, convert to a HTML string
    439                 $data = trim($document->saveHTML($div));
    440 
    441                 if ($this->remove_div)
    442                 {
    443                     $data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '', $data);
    444                     $data = preg_replace('/<\/div>$/', '', $data);
    445                 }
    446                 else
    447                 {
    448                     $data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '<div>', $data);
    449                 }
    450             }
    451 
    452             if ($type & SIMPLEPIE_CONSTRUCT_IRI)
    453             {
    454                 $absolute = $this->registry->call('Misc', 'absolutize_url', array($data, $base));
    455                 if ($absolute !== false)
    456                 {
    457                     $data = $absolute;
    458                 }
    459             }
    460 
    461             if ($type & (SIMPLEPIE_CONSTRUCT_TEXT | SIMPLEPIE_CONSTRUCT_IRI))
    462             {
    463                 $data = htmlspecialchars($data, ENT_COMPAT, 'UTF-8');
    464             }
    465 
    466             if ($this->output_encoding !== 'UTF-8')
    467             {
    468                 $data = $this->registry->call('Misc', 'change_encoding', array($data, 'UTF-8', $this->output_encoding));
    469             }
    470         }
    471         return $data;
    472     }
    473 
    474     protected function preprocess($html, $type)
    475     {
    476         $ret = '';
    477         $html = preg_replace('%</?(?:html|body)[^>]*?'.'>%is', '', $html);
    478         if ($type & ~SIMPLEPIE_CONSTRUCT_XHTML)
    479         {
    480             // Atom XHTML constructs are wrapped with a div by default
    481             // Note: No protection if $html contains a stray </div>!
    482             $html = '<div>' . $html . '</div>';
    483             $ret .= '<!DOCTYPE html>';
    484             $content_type = 'text/html';
    485         }
    486         else
    487         {
    488             $ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
    489             $content_type = 'application/xhtml+xml';
    490         }
    491 
    492         $ret .= '<html><head>';
    493         $ret .= '<meta http-equiv="Content-Type" content="' . $content_type . '; charset=utf-8" />';
    494         $ret .= '</head><body>' . $html . '</body></html>';
    495         return $ret;
    496     }
    497 
    498     public function replace_urls($document, $tag, $attributes)
    499     {
    500         if (!is_array($attributes))
    501         {
    502             $attributes = array($attributes);
    503         }
    504 
    505         if (!is_array($this->strip_htmltags) || !in_array($tag, $this->strip_htmltags))
    506         {
    507             $elements = $document->getElementsByTagName($tag);
    508             foreach ($elements as $element)
    509             {
    510                 foreach ($attributes as $attribute)
    511                 {
    512                     if ($element->hasAttribute($attribute))
    513                     {
    514                         $value = $this->registry->call('Misc', 'absolutize_url', array($element->getAttribute($attribute), $this->base));
    515                         if ($value !== false)
    516                         {
    517                             $value = $this->https_url($value);
    518                             $element->setAttribute($attribute, $value);
    519                         }
    520                     }
    521                 }
    522             }
    523         }
    524     }
    525 
    526     public function do_strip_htmltags($match)
    527     {
    528         if ($this->encode_instead_of_strip)
    529         {
    530             if (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style')))
    531             {
    532                 $match[1] = htmlspecialchars($match[1], ENT_COMPAT, 'UTF-8');
    533                 $match[2] = htmlspecialchars($match[2], ENT_COMPAT, 'UTF-8');
    534                 return "&lt;$match[1]$match[2]&gt;$match[3]&lt;/$match[1]&gt;";
    535             }
    536             else
    537             {
    538                 return htmlspecialchars($match[0], ENT_COMPAT, 'UTF-8');
    539             }
    540         }
    541         elseif (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style')))
    542         {
    543             return $match[4];
    544         }
    545         else
    546         {
    547             return '';
    548         }
    549     }
    550 
    551     protected function strip_tag($tag, $document, $xpath, $type)
    552     {
    553         $elements = $xpath->query('body//' . $tag);
    554         if ($this->encode_instead_of_strip)
    555         {
    556             foreach ($elements as $element)
    557             {
    558                 $fragment = $document->createDocumentFragment();
    559 
    560                 // For elements which aren't script or style, include the tag itself
    561                 if (!in_array($tag, array('script', 'style')))
    562                 {
    563                     $text = '<' . $tag;
    564                     if ($element->hasAttributes())
    565                     {
    566                         $attrs = array();
    567                         foreach ($element->attributes as $name => $attr)
    568                         {
    569                             $value = $attr->value;
    570 
    571                             // In XHTML, empty values should never exist, so we repeat the value
    572                             if (empty($value) && ($type & SIMPLEPIE_CONSTRUCT_XHTML))
    573                             {
    574                                 $value = $name;
    575                             }
    576                             // For HTML, empty is fine
    577                             elseif (empty($value) && ($type & SIMPLEPIE_CONSTRUCT_HTML))
    578                             {
    579                                 $attrs[] = $name;
    580                                 continue;
    581                             }
    582 
    583                             // Standard attribute text
    584                             $attrs[] = $name . '="' . $attr->value . '"';
    585                         }
    586                         $text .= ' ' . implode(' ', $attrs);
    587                     }
    588                     $text .= '>';
    589                     $fragment->appendChild(new DOMText($text));
    590                 }
    591 
    592                 $number = $element->childNodes->length;
    593                 for ($i = $number; $i > 0; $i--)
    594                 {
    595                     $child = $element->childNodes->item(0);
    596                     $fragment->appendChild($child);
    597                 }
    598 
    599                 if (!in_array($tag, array('script', 'style')))
    600                 {
    601                     $fragment->appendChild(new DOMText('</' . $tag . '>'));
    602                 }
    603 
    604                 $element->parentNode->replaceChild($fragment, $element);
    605             }
    606 
    607             return;
    608         }
    609         elseif (in_array($tag, array('script', 'style')))
    610         {
    611             foreach ($elements as $element)
    612             {
    613                 $element->parentNode->removeChild($element);
    614             }
    615 
    616             return;
    617         }
    618         else
    619         {
    620             foreach ($elements as $element)
    621             {
    622                 $fragment = $document->createDocumentFragment();
    623                 $number = $element->childNodes->length;
    624                 for ($i = $number; $i > 0; $i--)
    625                 {
    626                     $child = $element->childNodes->item(0);
    627                     $fragment->appendChild($child);
    628                 }
    629 
    630                 $element->parentNode->replaceChild($fragment, $element);
    631             }
    632         }
    633     }
    634 
    635     protected function strip_attr($attrib, $xpath)
    636     {
    637         $elements = $xpath->query('//*[@' . $attrib . ']');
    638 
    639         foreach ($elements as $element)
    640         {
    641             $element->removeAttribute($attrib);
    642         }
    643     }
    644 
    645     protected function add_attr($tag, $valuePairs, $document)
    646     {
    647         $elements = $document->getElementsByTagName($tag);
    648         foreach ($elements as $element)
    649         {
    650             foreach ($valuePairs as $attrib => $value)
    651             {
    652                 $element->setAttribute($attrib, $value);
    653             }
    654         }
    655     }
     65    // Private vars
     66    public $base;
     67
     68    // Options
     69    public $remove_div = true;
     70    public $image_handler = '';
     71    public $strip_htmltags = ['base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'];
     72    public $encode_instead_of_strip = false;
     73    public $strip_attributes = ['bgsound', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'];
     74    public $rename_attributes = [];
     75    public $add_attributes = ['audio' => ['preload' => 'none'], 'iframe' => ['sandbox' => 'allow-scripts allow-same-origin'], 'video' => ['preload' => 'none']];
     76    public $strip_comments = false;
     77    public $output_encoding = 'UTF-8';
     78    public $enable_cache = true;
     79    public $cache_location = './cache';
     80    public $cache_name_function = 'md5';
     81
     82    /**
     83     * @var NameFilter
     84     */
     85    private $cache_namefilter;
     86    public $timeout = 10;
     87    public $useragent = '';
     88    public $force_fsockopen = false;
     89    public $replace_url_attributes = null;
     90    public $registry;
     91
     92    /**
     93     * @var DataCache|null
     94     */
     95    private $cache = null;
     96
     97    /**
     98     * @var int Cache duration (in seconds)
     99     */
     100    private $cache_duration = 3600;
     101
     102    /**
     103     * List of domains for which to force HTTPS.
     104     * @see \SimplePie\Sanitize::set_https_domains()
     105     * Array is a tree split at DNS levels. Example:
     106     * array('biz' => true, 'com' => array('example' => true), 'net' => array('example' => array('www' => true)))
     107     */
     108    public $https_domains = [];
     109
     110    public function __construct()
     111    {
     112        // Set defaults
     113        $this->set_url_replacements(null);
     114    }
     115
     116    public function remove_div($enable = true)
     117    {
     118        $this->remove_div = (bool) $enable;
     119    }
     120
     121    public function set_image_handler($page = false)
     122    {
     123        if ($page) {
     124            $this->image_handler = (string) $page;
     125        } else {
     126            $this->image_handler = false;
     127        }
     128    }
     129
     130    public function set_registry(\SimplePie\Registry $registry)/* : void */
     131    {
     132        $this->registry = $registry;
     133    }
     134
     135    public function pass_cache_data($enable_cache = true, $cache_location = './cache', $cache_name_function = 'md5', $cache_class = 'SimplePie\Cache', ?DataCache $cache = null)
     136    {
     137        if (isset($enable_cache)) {
     138            $this->enable_cache = (bool) $enable_cache;
     139        }
     140
     141        if ($cache_location) {
     142            $this->cache_location = (string) $cache_location;
     143        }
     144
     145        if (!is_string($cache_name_function) && !is_object($cache_name_function) && !$cache_name_function instanceof NameFilter) {
     146            throw new InvalidArgumentException(sprintf(
     147                '%s(): Argument #3 ($cache_name_function) must be of type %s',
     148                __METHOD__,
     149                NameFilter::class
     150            ), 1);
     151        }
     152
     153        // BC: $cache_name_function could be a callable as string
     154        if (is_string($cache_name_function)) {
     155            // trigger_error(sprintf('Providing $cache_name_function as string in "%s()" is deprecated since SimplePie 1.8.0, provide as "%s" instead.', __METHOD__, NameFilter::class), \E_USER_DEPRECATED);
     156            $this->cache_name_function = (string) $cache_name_function;
     157
     158            $cache_name_function = new CallableNameFilter($cache_name_function);
     159        }
     160
     161        $this->cache_namefilter = $cache_name_function;
     162
     163        if ($cache !== null) {
     164            $this->cache = $cache;
     165        }
     166    }
     167
     168    public function pass_file_data($file_class = 'SimplePie\File', $timeout = 10, $useragent = '', $force_fsockopen = false)
     169    {
     170        if ($timeout) {
     171            $this->timeout = (string) $timeout;
     172        }
     173
     174        if ($useragent) {
     175            $this->useragent = (string) $useragent;
     176        }
     177
     178        if ($force_fsockopen) {
     179            $this->force_fsockopen = (string) $force_fsockopen;
     180        }
     181    }
     182
     183    public function strip_htmltags($tags = ['base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'])
     184    {
     185        if ($tags) {
     186            if (is_array($tags)) {
     187                $this->strip_htmltags = $tags;
     188            } else {
     189                $this->strip_htmltags = explode(',', $tags);
     190            }
     191        } else {
     192            $this->strip_htmltags = false;
     193        }
     194    }
     195
     196    public function encode_instead_of_strip($encode = false)
     197    {
     198        $this->encode_instead_of_strip = (bool) $encode;
     199    }
     200
     201    public function rename_attributes($attribs = [])
     202    {
     203        if ($attribs) {
     204            if (is_array($attribs)) {
     205                $this->rename_attributes = $attribs;
     206            } else {
     207                $this->rename_attributes = explode(',', $attribs);
     208            }
     209        } else {
     210            $this->rename_attributes = false;
     211        }
     212    }
     213
     214    public function strip_attributes($attribs = ['bgsound', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'])
     215    {
     216        if ($attribs) {
     217            if (is_array($attribs)) {
     218                $this->strip_attributes = $attribs;
     219            } else {
     220                $this->strip_attributes = explode(',', $attribs);
     221            }
     222        } else {
     223            $this->strip_attributes = false;
     224        }
     225    }
     226
     227    public function add_attributes($attribs = ['audio' => ['preload' => 'none'], 'iframe' => ['sandbox' => 'allow-scripts allow-same-origin'], 'video' => ['preload' => 'none']])
     228    {
     229        if ($attribs) {
     230            if (is_array($attribs)) {
     231                $this->add_attributes = $attribs;
     232            } else {
     233                $this->add_attributes = explode(',', $attribs);
     234            }
     235        } else {
     236            $this->add_attributes = false;
     237        }
     238    }
     239
     240    public function strip_comments($strip = false)
     241    {
     242        $this->strip_comments = (bool) $strip;
     243    }
     244
     245    public function set_output_encoding($encoding = 'UTF-8')
     246    {
     247        $this->output_encoding = (string) $encoding;
     248    }
     249
     250    /**
     251     * Set element/attribute key/value pairs of HTML attributes
     252     * containing URLs that need to be resolved relative to the feed
     253     *
     254     * Defaults to |a|@href, |area|@href, |audio|@src, |blockquote|@cite,
     255     * |del|@cite, |form|@action, |img|@longdesc, |img|@src, |input|@src,
     256     * |ins|@cite, |q|@cite, |source|@src, |video|@src
     257     *
     258     * @since 1.0
     259     * @param array|null $element_attribute Element/attribute key/value pairs, null for default
     260     */
     261    public function set_url_replacements($element_attribute = null)
     262    {
     263        if ($element_attribute === null) {
     264            $element_attribute = [
     265                'a' => 'href',
     266                'area' => 'href',
     267                'audio' => 'src',
     268                'blockquote' => 'cite',
     269                'del' => 'cite',
     270                'form' => 'action',
     271                'img' => [
     272                    'longdesc',
     273                    'src'
     274                ],
     275                'input' => 'src',
     276                'ins' => 'cite',
     277                'q' => 'cite',
     278                'source' => 'src',
     279                'video' => [
     280                    'poster',
     281                    'src'
     282                ]
     283            ];
     284        }
     285        $this->replace_url_attributes = (array) $element_attribute;
     286    }
     287
     288    /**
     289     * Set the list of domains for which to force HTTPS.
     290     * @see \SimplePie\Misc::https_url()
     291     * Example array('biz', 'example.com', 'example.org', 'www.example.net');
     292     */
     293    public function set_https_domains($domains)
     294    {
     295        $this->https_domains = [];
     296        foreach ($domains as $domain) {
     297            $domain = trim($domain, ". \t\n\r\0\x0B");
     298            $segments = array_reverse(explode('.', $domain));
     299            $node = &$this->https_domains;
     300            foreach ($segments as $segment) {//Build a tree
     301                if ($node === true) {
     302                    break;
     303                }
     304                if (!isset($node[$segment])) {
     305                    $node[$segment] = [];
     306                }
     307                $node = &$node[$segment];
     308            }
     309            $node = true;
     310        }
     311    }
     312
     313    /**
     314     * Check if the domain is in the list of forced HTTPS.
     315     */
     316    protected function is_https_domain($domain)
     317    {
     318        $domain = trim($domain, '. ');
     319        $segments = array_reverse(explode('.', $domain));
     320        $node = &$this->https_domains;
     321        foreach ($segments as $segment) {//Explore the tree
     322            if (isset($node[$segment])) {
     323                $node = &$node[$segment];
     324            } else {
     325                break;
     326            }
     327        }
     328        return $node === true;
     329    }
     330
     331    /**
     332     * Force HTTPS for selected Web sites.
     333     */
     334    public function https_url($url)
     335    {
     336        return (strtolower(substr($url, 0, 7)) === 'http://') &&
     337            $this->is_https_domain(parse_url($url, PHP_URL_HOST)) ?
     338            substr_replace($url, 's', 4, 0) : //Add the 's' to HTTPS
     339            $url;
     340    }
     341
     342    public function sanitize($data, $type, $base = '')
     343    {
     344        $data = trim($data);
     345        if ($data !== '' || $type & \SimplePie\SimplePie::CONSTRUCT_IRI) {
     346            if ($type & \SimplePie\SimplePie::CONSTRUCT_MAYBE_HTML) {
     347                if (preg_match('/(&(#(x[0-9a-fA-F]+|[0-9]+)|[a-zA-Z0-9]+)|<\/[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*' . \SimplePie\SimplePie::PCRE_HTML_ATTRIBUTE . '>)/', $data)) {
     348                    $type |= \SimplePie\SimplePie::CONSTRUCT_HTML;
     349                } else {
     350                    $type |= \SimplePie\SimplePie::CONSTRUCT_TEXT;
     351                }
     352            }
     353
     354            if ($type & \SimplePie\SimplePie::CONSTRUCT_BASE64) {
     355                $data = base64_decode($data);
     356            }
     357
     358            if ($type & (\SimplePie\SimplePie::CONSTRUCT_HTML | \SimplePie\SimplePie::CONSTRUCT_XHTML)) {
     359                if (!class_exists('DOMDocument')) {
     360                    throw new \SimplePie\Exception('DOMDocument not found, unable to use sanitizer');
     361                }
     362                $document = new \DOMDocument();
     363                $document->encoding = 'UTF-8';
     364
     365                $data = $this->preprocess($data, $type);
     366
     367                set_error_handler(['SimplePie\Misc', 'silence_errors']);
     368                $document->loadHTML($data);
     369                restore_error_handler();
     370
     371                $xpath = new \DOMXPath($document);
     372
     373                // Strip comments
     374                if ($this->strip_comments) {
     375                    $comments = $xpath->query('//comment()');
     376
     377                    foreach ($comments as $comment) {
     378                        $comment->parentNode->removeChild($comment);
     379                    }
     380                }
     381
     382                // Strip out HTML tags and attributes that might cause various security problems.
     383                // Based on recommendations by Mark Pilgrim at:
     384                // http://diveintomark.org/archives/2003/06/12/how_to_consume_rss_safely
     385                if ($this->strip_htmltags) {
     386                    foreach ($this->strip_htmltags as $tag) {
     387                        $this->strip_tag($tag, $document, $xpath, $type);
     388                    }
     389                }
     390
     391                if ($this->rename_attributes) {
     392                    foreach ($this->rename_attributes as $attrib) {
     393                        $this->rename_attr($attrib, $xpath);
     394                    }
     395                }
     396
     397                if ($this->strip_attributes) {
     398                    foreach ($this->strip_attributes as $attrib) {
     399                        $this->strip_attr($attrib, $xpath);
     400                    }
     401                }
     402
     403                if ($this->add_attributes) {
     404                    foreach ($this->add_attributes as $tag => $valuePairs) {
     405                        $this->add_attr($tag, $valuePairs, $document);
     406                    }
     407                }
     408
     409                // Replace relative URLs
     410                $this->base = $base;
     411                foreach ($this->replace_url_attributes as $element => $attributes) {
     412                    $this->replace_urls($document, $element, $attributes);
     413                }
     414
     415                // If image handling (caching, etc.) is enabled, cache and rewrite all the image tags.
     416                if (isset($this->image_handler) && ((string) $this->image_handler) !== '' && $this->enable_cache) {
     417                    $images = $document->getElementsByTagName('img');
     418
     419                    foreach ($images as $img) {
     420                        if ($img->hasAttribute('src')) {
     421                            $image_url = $this->cache_namefilter->filter($img->getAttribute('src'));
     422                            $cache = $this->get_cache($image_url);
     423
     424                            if ($cache->get_data($image_url, false)) {
     425                                $img->setAttribute('src', $this->image_handler . $image_url);
     426                            } else {
     427                                $file = $this->registry->create(File::class, [$img->getAttribute('src'), $this->timeout, 5, ['X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']], $this->useragent, $this->force_fsockopen]);
     428                                $headers = $file->headers;
     429
     430                                if ($file->success && ($file->method & \SimplePie\SimplePie::FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300))) {
     431                                    if ($cache->set_data($image_url, ['headers' => $file->headers, 'body' => $file->body], $this->cache_duration)) {
     432                                        $img->setAttribute('src', $this->image_handler . $image_url);
     433                                    } else {
     434                                        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);
     435                                    }
     436                                }
     437                            }
     438                        }
     439                    }
     440                }
     441
     442                // Get content node
     443                $div = $document->getElementsByTagName('body')->item(0)->firstChild;
     444                // Finally, convert to a HTML string
     445                $data = trim($document->saveHTML($div));
     446
     447                if ($this->remove_div) {
     448                    $data = preg_replace('/^<div' . \SimplePie\SimplePie::PCRE_XML_ATTRIBUTE . '>/', '', $data);
     449                    $data = preg_replace('/<\/div>$/', '', $data);
     450                } else {
     451                    $data = preg_replace('/^<div' . \SimplePie\SimplePie::PCRE_XML_ATTRIBUTE . '>/', '<div>', $data);
     452                }
     453
     454                $data = str_replace('</source>', '', $data);
     455            }
     456
     457            if ($type & \SimplePie\SimplePie::CONSTRUCT_IRI) {
     458                $absolute = $this->registry->call(Misc::class, 'absolutize_url', [$data, $base]);
     459                if ($absolute !== false) {
     460                    $data = $absolute;
     461                }
     462            }
     463
     464            if ($type & (\SimplePie\SimplePie::CONSTRUCT_TEXT | \SimplePie\SimplePie::CONSTRUCT_IRI)) {
     465                $data = htmlspecialchars($data, ENT_COMPAT, 'UTF-8');
     466            }
     467
     468            if ($this->output_encoding !== 'UTF-8') {
     469                $data = $this->registry->call(Misc::class, 'change_encoding', [$data, 'UTF-8', $this->output_encoding]);
     470            }
     471        }
     472        return $data;
     473    }
     474
     475    protected function preprocess($html, $type)
     476    {
     477        $ret = '';
     478        $html = preg_replace('%</?(?:html|body)[^>]*?'.'>%is', '', $html);
     479        if ($type & ~\SimplePie\SimplePie::CONSTRUCT_XHTML) {
     480            // Atom XHTML constructs are wrapped with a div by default
     481            // Note: No protection if $html contains a stray </div>!
     482            $html = '<div>' . $html . '</div>';
     483            $ret .= '<!DOCTYPE html>';
     484            $content_type = 'text/html';
     485        } else {
     486            $ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
     487            $content_type = 'application/xhtml+xml';
     488        }
     489
     490        $ret .= '<html><head>';
     491        $ret .= '<meta http-equiv="Content-Type" content="' . $content_type . '; charset=utf-8" />';
     492        $ret .= '</head><body>' . $html . '</body></html>';
     493        return $ret;
     494    }
     495
     496    public function replace_urls($document, $tag, $attributes)
     497    {
     498        if (!is_array($attributes)) {
     499            $attributes = [$attributes];
     500        }
     501
     502        if (!is_array($this->strip_htmltags) || !in_array($tag, $this->strip_htmltags)) {
     503            $elements = $document->getElementsByTagName($tag);
     504            foreach ($elements as $element) {
     505                foreach ($attributes as $attribute) {
     506                    if ($element->hasAttribute($attribute)) {
     507                        $value = $this->registry->call(Misc::class, 'absolutize_url', [$element->getAttribute($attribute), $this->base]);
     508                        if ($value !== false) {
     509                            $value = $this->https_url($value);
     510                            $element->setAttribute($attribute, $value);
     511                        }
     512                    }
     513                }
     514            }
     515        }
     516    }
     517
     518    public function do_strip_htmltags($match)
     519    {
     520        if ($this->encode_instead_of_strip) {
     521            if (isset($match[4]) && !in_array(strtolower($match[1]), ['script', 'style'])) {
     522                $match[1] = htmlspecialchars($match[1], ENT_COMPAT, 'UTF-8');
     523                $match[2] = htmlspecialchars($match[2], ENT_COMPAT, 'UTF-8');
     524                return "&lt;$match[1]$match[2]&gt;$match[3]&lt;/$match[1]&gt;";
     525            } else {
     526                return htmlspecialchars($match[0], ENT_COMPAT, 'UTF-8');
     527            }
     528        } elseif (isset($match[4]) && !in_array(strtolower($match[1]), ['script', 'style'])) {
     529            return $match[4];
     530        } else {
     531            return '';
     532        }
     533    }
     534
     535    protected function strip_tag($tag, $document, $xpath, $type)
     536    {
     537        $elements = $xpath->query('body//' . $tag);
     538        if ($this->encode_instead_of_strip) {
     539            foreach ($elements as $element) {
     540                $fragment = $document->createDocumentFragment();
     541
     542                // For elements which aren't script or style, include the tag itself
     543                if (!in_array($tag, ['script', 'style'])) {
     544                    $text = '<' . $tag;
     545                    if ($element->hasAttributes()) {
     546                        $attrs = [];
     547                        foreach ($element->attributes as $name => $attr) {
     548                            $value = $attr->value;
     549
     550                            // In XHTML, empty values should never exist, so we repeat the value
     551                            if (empty($value) && ($type & \SimplePie\SimplePie::CONSTRUCT_XHTML)) {
     552                                $value = $name;
     553                            }
     554                            // For HTML, empty is fine
     555                            elseif (empty($value) && ($type & \SimplePie\SimplePie::CONSTRUCT_HTML)) {
     556                                $attrs[] = $name;
     557                                continue;
     558                            }
     559
     560                            // Standard attribute text
     561                            $attrs[] = $name . '="' . $attr->value . '"';
     562                        }
     563                        $text .= ' ' . implode(' ', $attrs);
     564                    }
     565                    $text .= '>';
     566                    $fragment->appendChild(new \DOMText($text));
     567                }
     568
     569                $number = $element->childNodes->length;
     570                for ($i = $number; $i > 0; $i--) {
     571                    $child = $element->childNodes->item(0);
     572                    $fragment->appendChild($child);
     573                }
     574
     575                if (!in_array($tag, ['script', 'style'])) {
     576                    $fragment->appendChild(new \DOMText('</' . $tag . '>'));
     577                }
     578
     579                $element->parentNode->replaceChild($fragment, $element);
     580            }
     581
     582            return;
     583        } elseif (in_array($tag, ['script', 'style'])) {
     584            foreach ($elements as $element) {
     585                $element->parentNode->removeChild($element);
     586            }
     587
     588            return;
     589        } else {
     590            foreach ($elements as $element) {
     591                $fragment = $document->createDocumentFragment();
     592                $number = $element->childNodes->length;
     593                for ($i = $number; $i > 0; $i--) {
     594                    $child = $element->childNodes->item(0);
     595                    $fragment->appendChild($child);
     596                }
     597
     598                $element->parentNode->replaceChild($fragment, $element);
     599            }
     600        }
     601    }
     602
     603    protected function strip_attr($attrib, $xpath)
     604    {
     605        $elements = $xpath->query('//*[@' . $attrib . ']');
     606
     607        foreach ($elements as $element) {
     608            $element->removeAttribute($attrib);
     609        }
     610    }
     611
     612    protected function rename_attr($attrib, $xpath)
     613    {
     614        $elements = $xpath->query('//*[@' . $attrib . ']');
     615
     616        foreach ($elements as $element) {
     617            $element->setAttribute('data-sanitized-' . $attrib, $element->getAttribute($attrib));
     618            $element->removeAttribute($attrib);
     619        }
     620    }
     621
     622    protected function add_attr($tag, $valuePairs, $document)
     623    {
     624        $elements = $document->getElementsByTagName($tag);
     625        foreach ($elements as $element) {
     626            foreach ($valuePairs as $attrib => $value) {
     627                $element->setAttribute($attrib, $value);
     628            }
     629        }
     630    }
     631
     632    /**
     633     * Get a DataCache
     634     *
     635     * @param string $image_url Only needed for BC, can be removed in SimplePie 2.0.0
     636     *
     637     * @return DataCache
     638     */
     639    private function get_cache($image_url = '')
     640    {
     641        if ($this->cache === null) {
     642            // @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);
     643            $cache = $this->registry->call(Cache::class, 'get_handler', [
     644                $this->cache_location,
     645                $image_url,
     646                Base::TYPE_IMAGE
     647            ]);
     648
     649            return new BaseDataCache($cache);
     650        }
     651
     652        return $this->cache;
     653    }
    656654}
     655
     656class_alias('SimplePie\Sanitize', 'SimplePie_Sanitize');
  • 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     /**<