Make WordPress Core

Changeset 59141


Ignore:
Timestamp:
09/30/2024 10:48:16 PM (2 years 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