Changeset 59141
- Timestamp:
- 09/30/2024 10:48:16 PM (7 months ago)
- 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 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie; 46 44 47 /** 45 48 * Manages all author-related data 46 49 * 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()} 48 51 * 49 52 * This class can be overloaded with {@see SimplePie::set_author_class()} … … 52 55 * @subpackage API 53 56 */ 54 class SimplePie_Author57 class Author 55 58 { 56 57 58 59 60 61 62 var$name;59 /** 60 * Author's name 61 * 62 * @var string 63 * @see get_name() 64 */ 65 public $name; 63 66 64 65 66 67 68 69 70 var$link;67 /** 68 * Author's link 69 * 70 * @var string 71 * @see get_link() 72 */ 73 public $link; 71 74 72 73 74 75 76 77 78 var$email;75 /** 76 * Author's email address 77 * 78 * @var string 79 * @see get_email() 80 */ 81 public $email; 79 82 80 81 82 83 84 85 86 87 88 89 90 91 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 } 93 96 94 95 96 97 98 99 100 101 102 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 } 104 107 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 } 116 118 117 118 119 return null; 120 } 119 121 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 } 131 132 132 133 133 return null; 134 } 134 135 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 } 146 146 147 148 147 return null; 148 } 149 149 } 150 151 class_alias('SimplePie\Author', 'SimplePie_Author'); -
trunk/src/wp-includes/SimplePie/src/Cache.php
r59140 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie; 46 47 use SimplePie\Cache\Base; 48 44 49 /** 45 50 * Used to create cache objects … … 51 56 * @package SimplePie 52 57 * @subpackage Caching 58 * @deprecated since SimplePie 1.8.0, use "SimplePie\SimplePie::set_cache()" instead 53 59 */ 54 class SimplePie_Cache60 class Cache 55 61 { 56 57 58 59 60 61 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 ]; 69 75 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 } 74 82 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 } 92 99 93 return new SimplePie_Cache_File($location, $filename, $extension);94 100 return new \SimplePie\Cache\File($location, $filename, $extension); 101 } 95 102 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); 106 111 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 } 117 114 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 } 134 141 } 142 143 class_alias('SimplePie\Cache', 'SimplePie_Cache'); -
trunk/src/wp-includes/SimplePie/src/Cache/Base.php
r47733 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie\Cache; 46 44 47 /** 45 48 * Base for cache objects 46 49 * 47 * Classes to be used with {@see SimplePie_Cache::register()} are expected50 * Classes to be used with {@see \SimplePie\Cache::register()} are expected 48 51 * to implement this interface. 49 52 * 50 53 * @package SimplePie 51 54 * @subpackage Caching 55 * @deprecated since SimplePie 1.8.0, use "Psr\SimpleCache\CacheInterface" instead 52 56 */ 53 interface SimplePie_Cache_Base57 interface Base 54 58 { 55 56 57 58 59 60 59 /** 60 * Feed cache type 61 * 62 * @var string 63 */ 64 public const TYPE_FEED = 'spc'; 61 65 62 63 64 65 66 67 66 /** 67 * Image cache type 68 * 69 * @var string 70 */ 71 public const TYPE_IMAGE = 'spi'; 68 72 69 70 71 72 73 74 * @param string$type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data75 76 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); 77 81 78 79 80 81 * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property82 83 84 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); 85 89 86 87 88 89 90 91 90 /** 91 * Retrieve the data saved to the cache 92 * 93 * @return array Data for SimplePie::$data 94 */ 95 public function load(); 92 96 93 94 95 96 97 98 97 /** 98 * Retrieve the last modified time for the cache 99 * 100 * @return int Timestamp 101 */ 102 public function mtime(); 99 103 100 101 102 103 104 105 104 /** 105 * Set the last modified time to the current time 106 * 107 * @return bool Success status 108 */ 109 public function touch(); 106 110 107 108 109 110 111 112 111 /** 112 * Remove the cache 113 * 114 * @return bool Success status 115 */ 116 public function unlink(); 113 117 } 118 119 class_alias('SimplePie\Cache\Base', 'SimplePie_Cache_Base'); -
trunk/src/wp-includes/SimplePie/src/Cache/DB.php
r47733 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie\Cache; 46 44 47 /** 45 48 * Base class for database-based caches … … 47 50 * @package SimplePie 48 51 * @subpackage Caching 52 * @deprecated since SimplePie 1.8.0, use implementation of "Psr\SimpleCache\CacheInterface" instead 49 53 */ 50 abstract class SimplePie_Cache_DB implements SimplePie_Cache_Base54 abstract class DB implements Base 51 55 { 52 53 54 55 56 57 * @paramSimplePie $data58 59 60 61 62 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 = []; 64 68 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 } 71 73 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 } 80 80 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 } 101 92 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 } 136 119 } 120 121 class_alias('SimplePie\Cache\DB', 'SimplePie_Cache_DB'); -
trunk/src/wp-includes/SimplePie/src/Cache/File.php
r47733 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie\Cache; 46 44 47 /** 45 48 * Caches data to the filesystem … … 47 50 * @package SimplePie 48 51 * @subpackage Caching 52 * @deprecated since SimplePie 1.8.0, use implementation of "Psr\SimpleCache\CacheInterface" instead 49 53 */ 50 class SimplePie_Cache_File implements SimplePie_Cache_Base54 class File implements Base 51 55 { 52 53 54 55 56 57 58 56 /** 57 * Location string 58 * 59 * @see SimplePie::$cache_location 60 * @var string 61 */ 62 protected $location; 59 63 60 61 62 63 64 65 64 /** 65 * Filename 66 * 67 * @var string 68 */ 69 protected $filename; 66 70 67 68 69 70 71 72 71 /** 72 * File extension 73 * 74 * @var string 75 */ 76 protected $extension; 73 77 74 75 76 77 78 79 78 /** 79 * File path 80 * 81 * @var string 82 */ 83 protected $name; 80 84 81 82 83 84 85 86 * @param string$type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data87 88 89 90 91 92 93 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 } 95 99 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 } 110 112 111 112 113 114 115 113 $data = serialize($data); 114 return (bool) file_put_contents($this->name, $data); 115 } 116 return false; 117 } 116 118 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 } 130 131 131 132 133 134 135 136 137 138 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 } 140 141 141 142 143 144 145 146 147 148 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 } 150 151 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 } 164 164 } 165 166 class_alias('SimplePie\Cache\File', 'SimplePie_Cache_File'); -
trunk/src/wp-includes/SimplePie/src/Cache/Memcache.php
r47733 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie\Cache; 46 47 use Memcache as NativeMemcache; 48 44 49 /** 45 50 * Caches data to memcache … … 54 59 * @subpackage Caching 55 60 * @uses Memcache 61 * @deprecated since SimplePie 1.8.0, use implementation of "Psr\SimpleCache\CacheInterface" instead 56 62 */ 57 class SimplePie_Cache_Memcache implements SimplePie_Cache_Base63 class Memcache implements Base 58 64 { 59 60 61 62 63 64 65 /** 66 * Memcache instance 67 * 68 * @var Memcache 69 */ 70 protected $cache; 65 71 66 67 68 69 70 71 72 /** 73 * Options 74 * 75 * @var array 76 */ 77 protected $options; 72 78 73 74 75 76 77 78 79 /** 80 * Cache name 81 * 82 * @var string 83 */ 84 protected $name; 79 85 80 81 82 83 84 85 * @param string$type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data86 87 88 89 $this->options = array( 90 91 92 'extras' => array( 93 94 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)); 98 104 99 105 $this->name = $this->options['extras']['prefix'] . md5("$name:$type"); 100 106 101 $this->cache = newMemcache();102 103 107 $this->cache = new NativeMemcache(); 108 $this->cache->addServer($this->options['host'], (int) $this->options['port']); 109 } 104 110 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 } 119 124 120 121 122 123 124 125 126 127 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); 128 133 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 } 135 139 136 137 138 139 140 141 142 143 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); 144 148 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 } 150 153 151 152 154 return false; 155 } 153 156 154 155 156 157 158 159 160 161 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); 162 165 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 } 167 169 168 169 170 return false; 171 } 170 172 171 172 173 174 175 176 177 178 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 } 180 182 } 183 184 class_alias('SimplePie\Cache\Memcache', 'SimplePie_Cache_Memcache'); -
trunk/src/wp-includes/SimplePie/src/Cache/Memcached.php
r47733 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie\Cache; 46 47 use Memcached as NativeMemcached; 48 44 49 /** 45 50 * Caches data to memcached … … 55 60 * @author Paul L. McNeely 56 61 * @uses Memcached 62 * @deprecated since SimplePie 1.8.0, use implementation of "Psr\SimpleCache\CacheInterface" instead 57 63 */ 58 class SimplePie_Cache_Memcached implements SimplePie_Cache_Base64 class Memcached implements Base 59 65 { 60 66 /** 61 * Memcached instance62 * @var Memcached67 * NativeMemcached instance 68 * @var NativeMemcached 63 69 */ 64 70 protected $cache; … … 79 85 * Create a new cache object 80 86 * @param string $location Location string (from SimplePie::$cache_location) 81 * @param string $name 82 * @param string $typeEither TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data87 * @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 83 89 */ 84 public function __construct($location, $name, $type) { 85 $this->options = array( 90 public function __construct($location, $name, $type) 91 { 92 $this->options = [ 86 93 'host' => '127.0.0.1', 87 94 'port' => 11211, 88 'extras' => array(95 'extras' => [ 89 96 'timeout' => 3600, // one hour 90 97 '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)); 94 101 95 102 $this->name = $this->options['extras']['prefix'] . md5("$name:$type"); 96 103 97 $this->cache = new Memcached();104 $this->cache = new NativeMemcached(); 98 105 $this->cache->addServer($this->options['host'], (int)$this->options['port']); 99 106 } … … 101 108 /** 102 109 * 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 property110 * @param array|\SimplePie\SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property 104 111 * @return bool Successfulness 105 112 */ 106 public function save($data) { 107 if ($data instanceof SimplePie) { 113 public function save($data) 114 { 115 if ($data instanceof \SimplePie\SimplePie) { 108 116 $data = $data->data; 109 117 } … … 116 124 * @return array Data for SimplePie::$data 117 125 */ 118 public function load() { 126 public function load() 127 { 119 128 $data = $this->cache->get($this->name); 120 129 … … 129 138 * @return int Timestamp 130 139 */ 131 public function mtime() { 140 public function mtime() 141 { 132 142 $data = $this->cache->get($this->name . '_mtime'); 133 143 return (int) $data; … … 138 148 * @return bool Success status 139 149 */ 140 public function touch() { 150 public function touch() 151 { 141 152 $data = $this->cache->get($this->name); 142 153 return $this->setData($data); … … 147 158 * @return bool Success status 148 159 */ 149 public function unlink() { 160 public function unlink() 161 { 150 162 return $this->cache->delete($this->name, 0); 151 163 } 152 164 153 165 /** 154 * Set the last modified time and data to Memcached166 * Set the last modified time and data to NativeMemcached 155 167 * @return bool Success status 156 168 */ 157 private function setData($data) {158 169 private function setData($data) 170 { 159 171 if ($data !== false) { 160 172 $this->cache->set($this->name . '_mtime', time(), (int)$this->options['extras']['timeout']); … … 165 177 } 166 178 } 179 180 class_alias('SimplePie\Cache\Memcached', 'SimplePie_Cache_Memcached'); -
trunk/src/wp-includes/SimplePie/src/Cache/MySQL.php
r47733 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie\Cache; 46 44 47 /** 45 48 * Caches data to a MySQL database … … 53 56 * @package SimplePie 54 57 * @subpackage Caching 58 * @deprecated since SimplePie 1.8.0, use implementation of "Psr\SimpleCache\CacheInterface" instead 55 59 */ 56 class SimplePie_Cache_MySQL extends SimplePie_Cache_DB60 class MySQL extends DB 57 61 { 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 } 440 381 } 382 383 class_alias('SimplePie\Cache\MySQL', 'SimplePie_Cache_MySQL'); -
trunk/src/wp-includes/SimplePie/src/Cache/Redis.php
r52393 r59141 2 2 3 3 /** 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. 5 35 * 6 36 * @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 9 42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License 10 * @version 0.2.911 43 */ 12 44 45 namespace SimplePie\Cache; 46 47 use Redis as NativeRedis; 13 48 14 49 /** … … 24 59 * @subpackage Caching 25 60 * @uses Redis 61 * @deprecated since SimplePie 1.8.0, use implementation of "Psr\SimpleCache\CacheInterface" instead 26 62 */ 27 class SimplePie_Cache_Redis implements SimplePie_Cache_Base { 63 class Redis implements Base 64 { 28 65 /** 29 66 * Redis instance 30 67 * 31 * @var \Redis68 * @var NativeRedis 32 69 */ 33 70 protected $cache; … … 46 83 */ 47 84 protected $name; 48 49 /**50 * Cache Data51 *52 * @var type53 */54 protected $data;55 85 56 86 /** … … 59 89 * @param string $location Location string (from SimplePie::$cache_location) 60 90 * @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 { 64 95 //$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(); 67 98 $redis->connect($parsed['host'], $parsed['port']); 68 99 if (isset($parsed['pass'])) { … … 77 108 $this->options = $options; 78 109 } else { 79 $this->options = array (110 $this->options = [ 80 111 'prefix' => 'rss:simple_primary:', 81 112 'expire' => 0, 82 );113 ]; 83 114 } 84 115 … … 87 118 88 119 /** 89 * @param \Redis $cache 90 */ 91 public function setRedisClient(\Redis $cache) { 120 * @param NativeRedis $cache 121 */ 122 public function setRedisClient(NativeRedis $cache) 123 { 92 124 $this->cache = $cache; 93 125 } … … 96 128 * Save data to the cache 97 129 * 98 * @param array| SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property130 * @param array|\SimplePie\SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property 99 131 * @return bool Successfulness 100 132 */ 101 public function save($data) { 102 if ($data instanceof SimplePie) { 133 public function save($data) 134 { 135 if ($data instanceof \SimplePie\SimplePie) { 103 136 $data = $data->data; 104 137 } … … 116 149 * @return array Data for SimplePie::$data 117 150 */ 118 public function load() { 151 public function load() 152 { 119 153 $data = $this->cache->get($this->name); 120 154 … … 130 164 * @return int Timestamp 131 165 */ 132 public function mtime() {133 166 public function mtime() 167 { 134 168 $data = $this->cache->get($this->name); 135 169 … … 146 180 * @return bool Success status 147 181 */ 148 public function touch() {149 182 public function touch() 183 { 150 184 $data = $this->cache->get($this->name); 151 185 … … 166 200 * @return bool Success status 167 201 */ 168 public function unlink() { 202 public function unlink() 203 { 169 204 return $this->cache->set($this->name, null); 170 205 } 171 172 206 } 207 208 class_alias('SimplePie\Cache\Redis', 'SimplePie_Cache_Redis'); -
trunk/src/wp-includes/SimplePie/src/Caption.php
r59140 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie; 44 46 45 47 /** 46 48 * Handles `<media:text>` captions as defined in Media RSS. 47 49 * 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()} 49 51 * 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()} 51 53 * 52 54 * @package SimplePie 53 55 * @subpackage API 54 56 */ 55 class SimplePie_Caption57 class Caption 56 58 { 57 58 59 60 61 62 63 var$type;59 /** 60 * Content type 61 * 62 * @var string 63 * @see get_type() 64 */ 65 public $type; 64 66 65 66 67 68 69 70 71 var$lang;67 /** 68 * Language 69 * 70 * @var string 71 * @see get_language() 72 */ 73 public $lang; 72 74 73 74 75 76 77 78 79 var$startTime;75 /** 76 * Start time 77 * 78 * @var string 79 * @see get_starttime() 80 */ 81 public $startTime; 80 82 81 82 83 84 85 86 87 var$endTime;83 /** 84 * End time 85 * 86 * @var string 87 * @see get_endtime() 88 */ 89 public $endTime; 88 90 89 90 91 92 93 94 95 var$text;91 /** 92 * Caption text 93 * 94 * @var string 95 * @see get_text() 96 */ 97 public $text; 96 98 97 98 99 100 101 102 103 104 105 106 107 108 109 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 } 111 113 112 113 114 115 116 117 118 119 120 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 } 122 124 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 } 134 135 135 136 136 return null; 137 } 137 138 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 } 150 150 151 152 151 return null; 152 } 153 153 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 } 165 164 166 167 165 return null; 166 } 168 167 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 } 180 178 181 182 179 return null; 180 } 183 181 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 } 195 192 196 197 193 return null; 194 } 198 195 } 196 197 class_alias('SimplePie\Caption', 'SimplePie_Caption'); -
trunk/src/wp-includes/SimplePie/src/Category.php
r59140 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie; 46 44 47 /** 45 48 * Manages all category-related data 46 49 * 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()} 48 51 * 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()} 50 53 * 51 54 * @package SimplePie 52 55 * @subpackage API 53 56 */ 54 class SimplePie_Category57 class Category 55 58 { 56 57 58 59 60 61 62 var$term;59 /** 60 * Category identifier 61 * 62 * @var string|null 63 * @see get_term 64 */ 65 public $term; 63 66 64 65 66 67 68 69 70 var$scheme;67 /** 68 * Categorization scheme identifier 69 * 70 * @var string|null 71 * @see get_scheme() 72 */ 73 public $scheme; 71 74 72 73 74 75 76 77 78 var$label;75 /** 76 * Human readable label 77 * 78 * @var string|null 79 * @see get_label() 80 */ 81 public $label; 79 82 80 81 82 * 83 84 85 86 87 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; 90 93 91 92 93 94 95 96 97 98 99 100 101 102 103 104 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 } 106 109 107 108 109 110 111 112 113 114 115 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 } 117 120 118 119 120 121 122 123 124 125 126 121 /** 122 * Get the category identifier 123 * 124 * @return string|null 125 */ 126 public function get_term() 127 { 128 return $this->term; 129 } 127 130 128 129 130 131 132 133 134 135 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 } 137 140 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 } 152 154 153 154 155 156 157 158 159 160 161 155 /** 156 * Get the category type 157 * 158 * @return string|null 159 */ 160 public function get_type() 161 { 162 return $this->type; 163 } 162 164 } 163 165 166 class_alias('SimplePie\Category', 'SimplePie_Category'); -
trunk/src/wp-includes/SimplePie/src/Content/Type/Sniffer.php
r49176 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie\Content\Type; 44 46 45 47 /** … … 52 54 * 53 55 * 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()} 55 57 * 56 58 * @package SimplePie 57 59 * @subpackage HTTP 58 60 */ 59 class S implePie_Content_Type_Sniffer61 class Sniffer 60 62 { 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 } 318 258 } 259 260 class_alias('SimplePie\Content\Type\Sniffer', 'SimplePie_Content_Type_Sniffer'); -
trunk/src/wp-includes/SimplePie/src/Copyright.php
r59140 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie; 46 44 47 /** 45 48 * Manages `<media:copyright>` copyright tags as defined in Media RSS 46 49 * 47 * Used by {@see SimplePie_Enclosure::get_copyright()}50 * Used by {@see \SimplePie\Enclosure::get_copyright()} 48 51 * 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()} 50 53 * 51 54 * @package SimplePie 52 55 * @subpackage API 53 56 */ 54 class SimplePie_Copyright57 class Copyright 55 58 { 56 57 58 59 60 61 62 var$url;59 /** 60 * Copyright URL 61 * 62 * @var string 63 * @see get_url() 64 */ 65 public $url; 63 66 64 65 66 67 68 69 70 var$label;67 /** 68 * Attribution 69 * 70 * @var string 71 * @see get_attribution() 72 */ 73 public $label; 71 74 72 73 74 75 76 77 78 79 80 81 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 } 83 86 84 85 86 87 88 89 90 91 92 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 } 94 97 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 } 106 108 107 108 109 return null; 110 } 109 111 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 } 121 122 122 123 123 return null; 124 } 124 125 } 126 127 class_alias('SimplePie\Copyright', 'SimplePie_Copyright'); -
trunk/src/wp-includes/SimplePie/src/Credit.php
r59140 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie; 46 44 47 /** 45 48 * Handles `<media:credit>` as defined in Media RSS 46 49 * 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()} 48 51 * 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()} 50 53 * 51 54 * @package SimplePie 52 55 * @subpackage API 53 56 */ 54 class SimplePie_Credit57 class Credit 55 58 { 56 57 58 59 60 61 62 var$role;59 /** 60 * Credited role 61 * 62 * @var string 63 * @see get_role() 64 */ 65 public $role; 63 66 64 65 66 67 68 69 70 var$scheme;67 /** 68 * Organizational scheme 69 * 70 * @var string 71 * @see get_scheme() 72 */ 73 public $scheme; 71 74 72 73 74 75 76 77 78 var$name;75 /** 76 * Credited name 77 * 78 * @var string 79 * @see get_name() 80 */ 81 public $name; 79 82 80 81 82 83 84 85 86 87 88 89 90 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 } 92 95 93 94 95 96 97 98 99 100 101 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 } 103 106 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 } 115 117 116 117 118 return null; 119 } 118 120 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 } 130 131 131 132 132 return null; 133 } 133 134 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 } 145 145 146 147 146 return null; 147 } 148 148 } 149 150 class_alias('SimplePie\Credit', 'SimplePie_Credit'); -
trunk/src/wp-includes/SimplePie/src/Enclosure.php
r59140 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie; 46 44 47 /** 45 48 * Handles everything related to enclosures (including Media RSS and iTunes RSS) 46 49 * 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()} 48 51 * 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()} 50 53 * 51 54 * @package SimplePie 52 55 * @subpackage API 53 56 */ 54 class SimplePie_Enclosure57 class Enclosure 55 58 { 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 } 1309 1209 } 1210 1211 class_alias('SimplePie\Enclosure', 'SimplePie_Enclosure'); -
trunk/src/wp-includes/SimplePie/src/Exception.php
r59140 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie; 46 47 use Exception as NativeException; 48 44 49 /** 45 50 * General SimplePie exception class … … 47 52 * @package SimplePie 48 53 */ 49 class SimplePie_Exception extendsException54 class Exception extends NativeException 50 55 { 51 56 } 57 58 class_alias('SimplePie\Exception', 'SimplePie_Exception'); -
trunk/src/wp-includes/SimplePie/src/File.php
r59140 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie; 46 44 47 /** 45 48 * Used for fetching remote files and reading local files … … 47 50 * Supports HTTP 1.0 via cURL or fsockopen, with spotty HTTP 1.1 support 48 51 * 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()} 50 53 * 51 54 * @package SimplePie … … 53 56 * @todo Move to properly supporting RFC2616 (HTTP/1.1) 54 57 */ 55 class SimplePie_File58 class File 56 59 { 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 } 301 254 } 255 256 class_alias('SimplePie\File', 'SimplePie_File'); -
trunk/src/wp-includes/SimplePie/src/HTTP/Parser.php
r52393 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie\HTTP; 44 46 45 47 /** … … 49 51 * @subpackage HTTP 50 52 */ 51 class SimplePie_HTTP_Parser53 class Parser 52 54 { 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 } 520 506 } 507 508 class_alias('SimplePie\HTTP\Parser', 'SimplePie_HTTP_Parser'); -
trunk/src/wp-includes/SimplePie/src/IRI.php
r59140 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie; 46 44 47 /** 45 48 * IRI parser/serialiser/normaliser … … 53 56 * @license http://www.opensource.org/licenses/bsd-license.php 54 57 */ 55 class SimplePie_IRI58 class IRI 56 59 { 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 } 1236 1106 } 1107 1108 class_alias('SimplePie\IRI', 'SimplePie_IRI'); -
trunk/src/wp-includes/SimplePie/src/Item.php
r59140 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie; 44 46 45 47 /** 46 48 * Manages all item-related data 47 49 * 48 * Used by {@see SimplePie::get_item()} and {@seeSimplePie::get_items()}50 * Used by {@see \SimplePie\SimplePie::get_item()} and {@see \SimplePie\SimplePie::get_items()} 49 51 * 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()} 51 53 * 52 * @package SimplePie54 * @package \SimplePie\SimplePie 53 55 * @subpackage API 54 56 */ 55 class SimplePie_Item57 class Item implements RegistryAware 56 58 { 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 } 2967 2357 } 2358 2359 class_alias('SimplePie\Item', 'SimplePie_Item'); -
trunk/src/wp-includes/SimplePie/src/Locator.php
r59140 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie; 46 44 47 /** 45 48 * Used for feed auto-discovery 46 49 * 47 50 * 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()} 49 52 * 50 53 * @package SimplePie 51 54 */ 52 class SimplePie_Locator55 class Locator implements RegistryAware 53 56 { 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 } 432 367 } 368 369 class_alias('SimplePie\Locator', 'SimplePie_Locator', false); -
trunk/src/wp-includes/SimplePie/src/Misc.php
r59140 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie; 46 47 use SimplePie\XML\Declaration\Parser; 48 44 49 /** 45 * Miscellan ous utilities50 * Miscellaneous utilities 46 51 * 47 52 * @package SimplePie 48 53 */ 49 class SimplePie_Misc54 class Misc 50 55 { 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 2153 2049 function embed_quicktime(type, bgcolor, width, height, link, placeholder, loop) { 2154 2050 if (placeholder != '') { … … 2171 2067 document.writeln('<embed type="application/x-mplayer2" src="'+link+'" autosize="1" width="'+width+'" height="'+height+'" showcontrols="1" showstatusbar="0" showdisplay="0" autostart="0"></embed>'); 2172 2068 } 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 } 2069 END; 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 } 2273 2177 } 2178 2179 class_alias('SimplePie\Misc', 'SimplePie_Misc', false); -
trunk/src/wp-includes/SimplePie/src/Net/IPv6.php
r47733 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie\Net; 44 46 45 47 /** … … 56 58 * @author Sam Sneddon <geoffers@gmail.com> 57 59 */ 58 class SimplePie_Net_IPv660 class IPv6 59 61 { 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 } 269 255 } 256 257 class_alias('SimplePie\Net\IPv6', 'SimplePie_Net_IPv6'); -
trunk/src/wp-includes/SimplePie/src/Parse/Date.php
r47733 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie\Parse; 44 46 45 47 /** … … 49 51 * @subpackage Parsing 50 52 */ 51 class SimplePie_Parse_Date53 class Date 52 54 { 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 720 PCRE; 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 } 1025 986 } 987 988 class_alias('SimplePie\Parse\Date', 'SimplePie_Parse_Date'); -
trunk/src/wp-includes/SimplePie/src/Parser.php
r59140 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie; 46 47 use SimplePie\XML\Declaration\Parser as DeclarationParser; 48 44 49 /** 45 50 * Parses XML into something sane 46 51 * 47 52 * 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()} 49 54 * 50 55 * @package SimplePie 51 56 * @subpackage Parsing 52 57 */ 53 class SimplePie_Parser58 class Parser implements RegistryAware 54 59 { 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>&</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 " "> <!ENTITY iexcl "¡"> <!ENTITY cent "¢"> <!ENTITY pound "£"> <!ENTITY curren "¤"> <!ENTITY yen "¥"> <!ENTITY brvbar "¦"> <!ENTITY sect "§"> <!ENTITY uml "¨"> <!ENTITY copy "©"> <!ENTITY ordf "ª"> <!ENTITY laquo "«"> <!ENTITY not "¬"> <!ENTITY shy "­"> <!ENTITY reg "®"> <!ENTITY macr "¯"> <!ENTITY deg "°"> <!ENTITY plusmn "±"> <!ENTITY sup2 "²"> <!ENTITY sup3 "³"> <!ENTITY acute "´"> <!ENTITY micro "µ"> <!ENTITY para "¶"> <!ENTITY middot "·"> <!ENTITY cedil "¸"> <!ENTITY sup1 "¹"> <!ENTITY ordm "º"> <!ENTITY raquo "»"> <!ENTITY frac14 "¼"> <!ENTITY frac12 "½"> <!ENTITY frac34 "¾"> <!ENTITY iquest "¿"> <!ENTITY Agrave "À"> <!ENTITY Aacute "Á"> <!ENTITY Acirc "Â"> <!ENTITY Atilde "Ã"> <!ENTITY Auml "Ä"> <!ENTITY Aring "Å"> <!ENTITY AElig "Æ"> <!ENTITY Ccedil "Ç"> <!ENTITY Egrave "È"> <!ENTITY Eacute "É"> <!ENTITY Ecirc "Ê"> <!ENTITY Euml "Ë"> <!ENTITY Igrave "Ì"> <!ENTITY Iacute "Í"> <!ENTITY Icirc "Î"> <!ENTITY Iuml "Ï"> <!ENTITY ETH "Ð"> <!ENTITY Ntilde "Ñ"> <!ENTITY Ograve "Ò"> <!ENTITY Oacute "Ó"> <!ENTITY Ocirc "Ô"> <!ENTITY Otilde "Õ"> <!ENTITY Ouml "Ö"> <!ENTITY times "×"> <!ENTITY Oslash "Ø"> <!ENTITY Ugrave "Ù"> <!ENTITY Uacute "Ú"> <!ENTITY Ucirc "Û"> <!ENTITY Uuml "Ü"> <!ENTITY Yacute "Ý"> <!ENTITY THORN "Þ"> <!ENTITY szlig "ß"> <!ENTITY agrave "à"> <!ENTITY aacute "á"> <!ENTITY acirc "â"> <!ENTITY atilde "ã"> <!ENTITY auml "ä"> <!ENTITY aring "å"> <!ENTITY aelig "æ"> <!ENTITY ccedil "ç"> <!ENTITY egrave "è"> <!ENTITY eacute "é"> <!ENTITY ecirc "ê"> <!ENTITY euml "ë"> <!ENTITY igrave "ì"> <!ENTITY iacute "í"> <!ENTITY icirc "î"> <!ENTITY iuml "ï"> <!ENTITY eth "ð"> <!ENTITY ntilde "ñ"> <!ENTITY ograve "ò"> <!ENTITY oacute "ó"> <!ENTITY ocirc "ô"> <!ENTITY otilde "õ"> <!ENTITY ouml "ö"> <!ENTITY divide "÷"> <!ENTITY oslash "ø"> <!ENTITY ugrave "ù"> <!ENTITY uacute "ú"> <!ENTITY ucirc "û"> <!ENTITY uuml "ü"> <!ENTITY yacute "ý"> <!ENTITY thorn "þ"> <!ENTITY yuml "ÿ"> <!ENTITY OElig "Œ"> <!ENTITY oelig "œ"> <!ENTITY Scaron "Š"> <!ENTITY scaron "š"> <!ENTITY Yuml "Ÿ"> <!ENTITY fnof "ƒ"> <!ENTITY circ "ˆ"> <!ENTITY tilde "˜"> <!ENTITY Alpha "Α"> <!ENTITY Beta "Β"> <!ENTITY Gamma "Γ"> <!ENTITY Epsilon "Ε"> <!ENTITY Zeta "Ζ"> <!ENTITY Eta "Η"> <!ENTITY Theta "Θ"> <!ENTITY Iota "Ι"> <!ENTITY Kappa "Κ"> <!ENTITY Lambda "Λ"> <!ENTITY Mu "Μ"> <!ENTITY Nu "Ν"> <!ENTITY Xi "Ξ"> <!ENTITY Omicron "Ο"> <!ENTITY Pi "Π"> <!ENTITY Rho "Ρ"> <!ENTITY Sigma "Σ"> <!ENTITY Tau "Τ"> <!ENTITY Upsilon "Υ"> <!ENTITY Phi "Φ"> <!ENTITY Chi "Χ"> <!ENTITY Psi "Ψ"> <!ENTITY Omega "Ω"> <!ENTITY alpha "α"> <!ENTITY beta "β"> <!ENTITY gamma "γ"> <!ENTITY delta "δ"> <!ENTITY epsilon "ε"> <!ENTITY zeta "ζ"> <!ENTITY eta "η"> <!ENTITY theta "θ"> <!ENTITY iota "ι"> <!ENTITY kappa "κ"> <!ENTITY lambda "λ"> <!ENTITY mu "μ"> <!ENTITY nu "ν"> <!ENTITY xi "ξ"> <!ENTITY omicron "ο"> <!ENTITY pi "π"> <!ENTITY rho "ρ"> <!ENTITY sigmaf "ς"> <!ENTITY sigma "σ"> <!ENTITY tau "τ"> <!ENTITY upsilon "υ"> <!ENTITY phi "φ"> <!ENTITY chi "χ"> <!ENTITY psi "ψ"> <!ENTITY omega "ω"> <!ENTITY thetasym "ϑ"> <!ENTITY upsih "ϒ"> <!ENTITY piv "ϖ"> <!ENTITY ensp " "> <!ENTITY emsp " "> <!ENTITY thinsp " "> <!ENTITY zwnj "‌"> <!ENTITY zwj "‍"> <!ENTITY lrm "‎"> <!ENTITY rlm "‏"> <!ENTITY ndash "–"> <!ENTITY mdash "—"> <!ENTITY lsquo "‘"> <!ENTITY rsquo "’"> <!ENTITY sbquo "‚"> <!ENTITY ldquo "“"> <!ENTITY rdquo "”"> <!ENTITY bdquo "„"> <!ENTITY dagger "†"> <!ENTITY Dagger "‡"> <!ENTITY bull "•"> <!ENTITY hellip "…"> <!ENTITY permil "‰"> <!ENTITY prime "′"> <!ENTITY Prime "″"> <!ENTITY lsaquo "‹"> <!ENTITY rsaquo "›"> <!ENTITY oline "‾"> <!ENTITY frasl "⁄"> <!ENTITY euro "€"> <!ENTITY image "ℑ"> <!ENTITY weierp "℘"> <!ENTITY real "ℜ"> <!ENTITY trade "™"> <!ENTITY alefsym "ℵ"> <!ENTITY larr "←"> <!ENTITY uarr "↑"> <!ENTITY rarr "→"> <!ENTITY darr "↓"> <!ENTITY harr "↔"> <!ENTITY crarr "↵"> <!ENTITY lArr "⇐"> <!ENTITY uArr "⇑"> <!ENTITY rArr "⇒"> <!ENTITY dArr "⇓"> <!ENTITY hArr "⇔"> <!ENTITY forall "∀"> <!ENTITY part "∂"> <!ENTITY exist "∃"> <!ENTITY empty "∅"> <!ENTITY nabla "∇"> <!ENTITY isin "∈"> <!ENTITY notin "∉"> <!ENTITY ni "∋"> <!ENTITY prod "∏"> <!ENTITY sum "∑"> <!ENTITY minus "−"> <!ENTITY lowast "∗"> <!ENTITY radic "√"> <!ENTITY prop "∝"> <!ENTITY infin "∞"> <!ENTITY ang "∠"> <!ENTITY and "∧"> <!ENTITY or "∨"> <!ENTITY cap "∩"> <!ENTITY cup "∪"> <!ENTITY int "∫"> <!ENTITY there4 "∴"> <!ENTITY sim "∼"> <!ENTITY cong "≅"> <!ENTITY asymp "≈"> <!ENTITY ne "≠"> <!ENTITY equiv "≡"> <!ENTITY le "≤"> <!ENTITY ge "≥"> <!ENTITY sub "⊂"> <!ENTITY sup "⊃"> <!ENTITY nsub "⊄"> <!ENTITY sube "⊆"> <!ENTITY supe "⊇"> <!ENTITY oplus "⊕"> <!ENTITY otimes "⊗"> <!ENTITY perp "⊥"> <!ENTITY sdot "⋅"> <!ENTITY lceil "⌈"> <!ENTITY rceil "⌉"> <!ENTITY lfloor "⌊"> <!ENTITY rfloor "⌋"> <!ENTITY lang "〈"> <!ENTITY rang "〉"> <!ENTITY loz "◊"> <!ENTITY spades "♠"> <!ENTITY clubs "♣"> <!ENTITY hearts "♥"> <!ENTITY diams "♦"> ]>'; 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>&</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 " "> <!ENTITY iexcl "¡"> <!ENTITY cent "¢"> <!ENTITY pound "£"> <!ENTITY curren "¤"> <!ENTITY yen "¥"> <!ENTITY brvbar "¦"> <!ENTITY sect "§"> <!ENTITY uml "¨"> <!ENTITY copy "©"> <!ENTITY ordf "ª"> <!ENTITY laquo "«"> <!ENTITY not "¬"> <!ENTITY shy "­"> <!ENTITY reg "®"> <!ENTITY macr "¯"> <!ENTITY deg "°"> <!ENTITY plusmn "±"> <!ENTITY sup2 "²"> <!ENTITY sup3 "³"> <!ENTITY acute "´"> <!ENTITY micro "µ"> <!ENTITY para "¶"> <!ENTITY middot "·"> <!ENTITY cedil "¸"> <!ENTITY sup1 "¹"> <!ENTITY ordm "º"> <!ENTITY raquo "»"> <!ENTITY frac14 "¼"> <!ENTITY frac12 "½"> <!ENTITY frac34 "¾"> <!ENTITY iquest "¿"> <!ENTITY Agrave "À"> <!ENTITY Aacute "Á"> <!ENTITY Acirc "Â"> <!ENTITY Atilde "Ã"> <!ENTITY Auml "Ä"> <!ENTITY Aring "Å"> <!ENTITY AElig "Æ"> <!ENTITY Ccedil "Ç"> <!ENTITY Egrave "È"> <!ENTITY Eacute "É"> <!ENTITY Ecirc "Ê"> <!ENTITY Euml "Ë"> <!ENTITY Igrave "Ì"> <!ENTITY Iacute "Í"> <!ENTITY Icirc "Î"> <!ENTITY Iuml "Ï"> <!ENTITY ETH "Ð"> <!ENTITY Ntilde "Ñ"> <!ENTITY Ograve "Ò"> <!ENTITY Oacute "Ó"> <!ENTITY Ocirc "Ô"> <!ENTITY Otilde "Õ"> <!ENTITY Ouml "Ö"> <!ENTITY times "×"> <!ENTITY Oslash "Ø"> <!ENTITY Ugrave "Ù"> <!ENTITY Uacute "Ú"> <!ENTITY Ucirc "Û"> <!ENTITY Uuml "Ü"> <!ENTITY Yacute "Ý"> <!ENTITY THORN "Þ"> <!ENTITY szlig "ß"> <!ENTITY agrave "à"> <!ENTITY aacute "á"> <!ENTITY acirc "â"> <!ENTITY atilde "ã"> <!ENTITY auml "ä"> <!ENTITY aring "å"> <!ENTITY aelig "æ"> <!ENTITY ccedil "ç"> <!ENTITY egrave "è"> <!ENTITY eacute "é"> <!ENTITY ecirc "ê"> <!ENTITY euml "ë"> <!ENTITY igrave "ì"> <!ENTITY iacute "í"> <!ENTITY icirc "î"> <!ENTITY iuml "ï"> <!ENTITY eth "ð"> <!ENTITY ntilde "ñ"> <!ENTITY ograve "ò"> <!ENTITY oacute "ó"> <!ENTITY ocirc "ô"> <!ENTITY otilde "õ"> <!ENTITY ouml "ö"> <!ENTITY divide "÷"> <!ENTITY oslash "ø"> <!ENTITY ugrave "ù"> <!ENTITY uacute "ú"> <!ENTITY ucirc "û"> <!ENTITY uuml "ü"> <!ENTITY yacute "ý"> <!ENTITY thorn "þ"> <!ENTITY yuml "ÿ"> <!ENTITY OElig "Œ"> <!ENTITY oelig "œ"> <!ENTITY Scaron "Š"> <!ENTITY scaron "š"> <!ENTITY Yuml "Ÿ"> <!ENTITY fnof "ƒ"> <!ENTITY circ "ˆ"> <!ENTITY tilde "˜"> <!ENTITY Alpha "Α"> <!ENTITY Beta "Β"> <!ENTITY Gamma "Γ"> <!ENTITY Epsilon "Ε"> <!ENTITY Zeta "Ζ"> <!ENTITY Eta "Η"> <!ENTITY Theta "Θ"> <!ENTITY Iota "Ι"> <!ENTITY Kappa "Κ"> <!ENTITY Lambda "Λ"> <!ENTITY Mu "Μ"> <!ENTITY Nu "Ν"> <!ENTITY Xi "Ξ"> <!ENTITY Omicron "Ο"> <!ENTITY Pi "Π"> <!ENTITY Rho "Ρ"> <!ENTITY Sigma "Σ"> <!ENTITY Tau "Τ"> <!ENTITY Upsilon "Υ"> <!ENTITY Phi "Φ"> <!ENTITY Chi "Χ"> <!ENTITY Psi "Ψ"> <!ENTITY Omega "Ω"> <!ENTITY alpha "α"> <!ENTITY beta "β"> <!ENTITY gamma "γ"> <!ENTITY delta "δ"> <!ENTITY epsilon "ε"> <!ENTITY zeta "ζ"> <!ENTITY eta "η"> <!ENTITY theta "θ"> <!ENTITY iota "ι"> <!ENTITY kappa "κ"> <!ENTITY lambda "λ"> <!ENTITY mu "μ"> <!ENTITY nu "ν"> <!ENTITY xi "ξ"> <!ENTITY omicron "ο"> <!ENTITY pi "π"> <!ENTITY rho "ρ"> <!ENTITY sigmaf "ς"> <!ENTITY sigma "σ"> <!ENTITY tau "τ"> <!ENTITY upsilon "υ"> <!ENTITY phi "φ"> <!ENTITY chi "χ"> <!ENTITY psi "ψ"> <!ENTITY omega "ω"> <!ENTITY thetasym "ϑ"> <!ENTITY upsih "ϒ"> <!ENTITY piv "ϖ"> <!ENTITY ensp " "> <!ENTITY emsp " "> <!ENTITY thinsp " "> <!ENTITY zwnj "‌"> <!ENTITY zwj "‍"> <!ENTITY lrm "‎"> <!ENTITY rlm "‏"> <!ENTITY ndash "–"> <!ENTITY mdash "—"> <!ENTITY lsquo "‘"> <!ENTITY rsquo "’"> <!ENTITY sbquo "‚"> <!ENTITY ldquo "“"> <!ENTITY rdquo "”"> <!ENTITY bdquo "„"> <!ENTITY dagger "†"> <!ENTITY Dagger "‡"> <!ENTITY bull "•"> <!ENTITY hellip "…"> <!ENTITY permil "‰"> <!ENTITY prime "′"> <!ENTITY Prime "″"> <!ENTITY lsaquo "‹"> <!ENTITY rsaquo "›"> <!ENTITY oline "‾"> <!ENTITY frasl "⁄"> <!ENTITY euro "€"> <!ENTITY image "ℑ"> <!ENTITY weierp "℘"> <!ENTITY real "ℜ"> <!ENTITY trade "™"> <!ENTITY alefsym "ℵ"> <!ENTITY larr "←"> <!ENTITY uarr "↑"> <!ENTITY rarr "→"> <!ENTITY darr "↓"> <!ENTITY harr "↔"> <!ENTITY crarr "↵"> <!ENTITY lArr "⇐"> <!ENTITY uArr "⇑"> <!ENTITY rArr "⇒"> <!ENTITY dArr "⇓"> <!ENTITY hArr "⇔"> <!ENTITY forall "∀"> <!ENTITY part "∂"> <!ENTITY exist "∃"> <!ENTITY empty "∅"> <!ENTITY nabla "∇"> <!ENTITY isin "∈"> <!ENTITY notin "∉"> <!ENTITY ni "∋"> <!ENTITY prod "∏"> <!ENTITY sum "∑"> <!ENTITY minus "−"> <!ENTITY lowast "∗"> <!ENTITY radic "√"> <!ENTITY prop "∝"> <!ENTITY infin "∞"> <!ENTITY ang "∠"> <!ENTITY and "∧"> <!ENTITY or "∨"> <!ENTITY cap "∩"> <!ENTITY cup "∪"> <!ENTITY int "∫"> <!ENTITY there4 "∴"> <!ENTITY sim "∼"> <!ENTITY cong "≅"> <!ENTITY asymp "≈"> <!ENTITY ne "≠"> <!ENTITY equiv "≡"> <!ENTITY le "≤"> <!ENTITY ge "≥"> <!ENTITY sub "⊂"> <!ENTITY sup "⊃"> <!ENTITY nsub "⊄"> <!ENTITY sube "⊆"> <!ENTITY supe "⊇"> <!ENTITY oplus "⊕"> <!ENTITY otimes "⊗"> <!ENTITY perp "⊥"> <!ENTITY sdot "⋅"> <!ENTITY lceil "⌈"> <!ENTITY rceil "⌉"> <!ENTITY lfloor "⌊"> <!ENTITY rfloor "⌋"> <!ENTITY lang "〈"> <!ENTITY rang "〉"> <!ENTITY loz "◊"> <!ENTITY spades "♠"> <!ENTITY clubs "♣"> <!ENTITY hearts "♥"> <!ENTITY diams "♦"> ]>'; 627 } 679 628 } 629 630 class_alias('SimplePie\Parser', 'SimplePie_Parser'); -
trunk/src/wp-includes/SimplePie/src/Rating.php
r59140 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie; 46 44 47 /** 45 48 * Handles `<media:rating>` or `<itunes:explicit>` tags as defined in Media RSS and iTunes RSS respectively 46 49 * 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()} 48 51 * 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()} 50 53 * 51 54 * @package SimplePie 52 55 * @subpackage API 53 56 */ 54 class SimplePie_Rating57 class Rating 55 58 { 56 57 58 59 60 61 62 var$scheme;59 /** 60 * Rating scheme 61 * 62 * @var string 63 * @see get_scheme() 64 */ 65 public $scheme; 63 66 64 65 66 67 68 69 70 var$value;67 /** 68 * Rating value 69 * 70 * @var string 71 * @see get_value() 72 */ 73 public $value; 71 74 72 73 74 75 76 77 78 79 80 81 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 } 83 86 84 85 86 87 88 89 90 91 92 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 } 94 97 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 } 106 108 107 108 109 return null; 110 } 109 111 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 } 121 122 122 123 123 return null; 124 } 124 125 } 126 127 class_alias('SimplePie\Rating', 'SimplePie_Rating'); -
trunk/src/wp-includes/SimplePie/src/Registry.php
r59140 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie; 46 47 use SimplePie\Content\Type\Sniffer; 48 use SimplePie\Parse\Date; 49 use SimplePie\XML\Declaration\Parser as DeclarationParser; 50 44 51 /** 45 52 * Handles creating objects and calling methods 46 53 * 47 * Access this via {@see SimplePie::get_registry()}54 * Access this via {@see \SimplePie\SimplePie::get_registry()} 48 55 * 49 56 * @package SimplePie 50 57 */ 51 class SimplePie_Registry58 class Registry 52 59 { 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 } 225 269 } 270 271 class_alias('SimplePie\Registry', 'SimplePie_Registry'); -
trunk/src/wp-includes/SimplePie/src/Restriction.php
r59140 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie; 46 44 47 /** 45 48 * Handles `<media:restriction>` as defined in Media RSS 46 49 * 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()} 48 51 * 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()} 50 53 * 51 54 * @package SimplePie 52 55 * @subpackage API 53 56 */ 54 class SimplePie_Restriction57 class Restriction 55 58 { 56 57 58 59 60 61 62 var$relationship;59 /** 60 * Relationship ('allow'/'deny') 61 * 62 * @var string 63 * @see get_relationship() 64 */ 65 public $relationship; 63 66 64 65 66 67 68 69 70 var$type;67 /** 68 * Type of restriction 69 * 70 * @var string 71 * @see get_type() 72 */ 73 public $type; 71 74 72 73 74 75 76 77 78 var$value;75 /** 76 * Restricted values 77 * 78 * @var string 79 * @see get_value() 80 */ 81 public $value; 79 82 80 81 82 83 84 85 86 87 88 89 90 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 } 92 95 93 94 95 96 97 98 99 100 101 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 } 103 106 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 } 115 117 116 117 118 return null; 119 } 118 120 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 } 130 131 131 132 132 return null; 133 } 133 134 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 } 145 145 146 147 146 return null; 147 } 148 148 } 149 150 class_alias('SimplePie\Restriction', 'SimplePie_Restriction'); -
trunk/src/wp-includes/SimplePie/src/Sanitize.php
r59140 r59141 1 1 <?php 2 2 3 /** 3 4 * SimplePie … … 6 7 * Takes the hard work out of managing a complete RSS/Atom solution. 7 8 * 8 * Copyright (c) 2004-20 16, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 9 10 * All rights reserved. 10 11 * … … 42 43 */ 43 44 45 namespace SimplePie; 46 47 use InvalidArgumentException; 48 use SimplePie\Cache\Base; 49 use SimplePie\Cache\BaseDataCache; 50 use SimplePie\Cache\CallableNameFilter; 51 use SimplePie\Cache\DataCache; 52 use SimplePie\Cache\NameFilter; 53 44 54 /** 45 55 * Used for data cleanup and post-processing 46 56 * 47 57 * 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()} 49 59 * 50 60 * @package SimplePie 51 61 * @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 52 62 */ 53 class S implePie_Sanitize63 class Sanitize implements RegistryAware 54 64 { 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 "<$match[1]$match[2]>$match[3]</$match[1]>"; 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 "<$match[1]$match[2]>$match[3]</$match[1]>"; 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 } 656 654 } 655 656 class_alias('SimplePie\Sanitize', 'SimplePie_Sanitize'); -
trunk/src/wp-includes/SimplePie/src/SimplePie.php
r59138 r59141 1 1 <?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.023 *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' );38 2 39 3 /** … … 43 7 * Takes the hard work out of managing a complete RSS/Atom solution. 44 8 * 45 * Copyright (c) 2004-20 17, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors9 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 46 10 * All rights reserved. 47 11 * … … 71 35 * 72 36 * @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 75 38 * @author Ryan Parman 76 39 * @author Sam Sneddon … … 80 43 */ 81 44 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 45 namespace SimplePie; 46 47 use InvalidArgumentException; 48 use Psr\SimpleCache\CacheInterface; 49 use SimplePie\Cache\Base; 50 use SimplePie\Cache\BaseDataCache; 51 use SimplePie\Cache\CallableNameFilter; 52 use SimplePie\Cache\DataCache; 53 use SimplePie\Cache\NameFilter; 54 use SimplePie\Cache\Psr16; 55 use SimplePie\Content\Type\Sniffer; 442 56 443 57 /** … … 449 63 class SimplePie 450 64 { 451 /** 452 * @var array Raw data 453 * @access private 454 */ 455 public $data = array(); 456 457 /** 458 * @var mixed Error string 459 * @access private 460 */ 461 public $error; 462 463 /** 464 * @var int HTTP status code 465 * @see SimplePie::status_code() 466 * @access private 467 */ 468 public $status_code; 469 470 /** 471 * @var object Instance of SimplePie_Sanitize (or other class) 472 * @see SimplePie::set_sanitize_class() 473 * @access private 474 */ 475 public $sanitize; 476 477 /** 478 * @var string SimplePie Useragent 479 * @see SimplePie::set_useragent() 480 * @access private 481 */ 482 public $useragent = SIMPLEPIE_USERAGENT; 483 484 /** 485 * @var string Feed URL 486 * @see SimplePie::set_feed_url() 487 * @access private 488 */ 489 public $feed_url; 490 491 /** 492 * @var string Original feed URL, or new feed URL iff HTTP 301 Moved Permanently 493 * @see SimplePie::subscribe_url() 494 * @access private 495 */ 496 public $permanent_url = null; 497 498 /** 499 * @var object Instance of SimplePie_File to use as a feed 500 * @see SimplePie::set_file() 501 * @access private 502 */ 503 public $file; 504 505 /** 506 * @var string Raw feed data 507 * @see SimplePie::set_raw_data() 508 * @access private 509 */ 510 public $raw_data; 511 512 /** 513 * @var int Timeout for fetching remote files 514 * @see SimplePie::set_timeout() 515 * @access private 516 */ 517 public $timeout = 10; 518 519 /** 520 * @var array Custom curl options 521 * @see SimplePie::set_curl_options() 522 * @access private 523 */ 524 public $curl_options = array(); 525 526 /** 527 * @var bool Forces fsockopen() to be used for remote files instead 528 * of cURL, even if a new enough version is installed 529 * @see SimplePie::force_fsockopen() 530 * @access private 531 */ 532 public $force_fsockopen = false; 533 534 /** 535 * @var bool Force the given data/URL to be treated as a feed no matter what 536 * it appears like 537 * @see SimplePie::force_feed() 538 * @access private 539 */ 540 public $force_feed = false; 541 542 /** 543 * @var bool Enable/Disable Caching 544 * @see SimplePie::enable_cache() 545 * @access private 546 */ 547 public $cache = true; 548 549 /** 550 * @var bool Force SimplePie to fallback to expired cache, if enabled, 551 * when feed is unavailable. 552 * @see SimplePie::force_cache_fallback() 553 * @access private 554 */ 555 public $force_cache_fallback = false; 556 557 /** 558 * @var int Cache duration (in seconds) 559 * @see SimplePie::set_cache_duration() 560 * @access private 561 */ 562 public $cache_duration = 3600; 563 564 /** 565 * @var int Auto-discovery cache duration (in seconds) 566 * @see SimplePie::set_autodiscovery_cache_duration() 567 * @access private 568 */ 569 public $autodiscovery_cache_duration = 604800; // 7 Days. 570 571 /** 572 * @var string Cache location (relative to executing script) 573 * @see SimplePie::set_cache_location() 574 * @access private 575 */ 576 public $cache_location = './cache'; 577 578 /** 579 * @var string Function that creates the cache filename 580 * @see SimplePie::set_cache_name_function() 581 * @access private 582 */ 583 public $cache_name_function = 'md5'; 584 585 /** 586 * @var bool Reorder feed by date descending 587 * @see SimplePie::enable_order_by_date() 588 * @access private 589 */ 590 public $order_by_date = true; 591 592 /** 593 * @var mixed Force input encoding to be set to the follow value 594 * (false, or anything type-cast to false, disables this feature) 595 * @see SimplePie::set_input_encoding() 596 * @access private 597 */ 598 public $input_encoding = false; 599 600 /** 601 * @var int Feed Autodiscovery Level 602 * @see SimplePie::set_autodiscovery_level() 603 * @access private 604 */ 605 public $autodiscovery = SIMPLEPIE_LOCATOR_ALL; 606 607 /** 608 * Class registry object 609 * 610 * @var SimplePie_Registry 611 */ 612 public $registry; 613 614 /** 615 * @var int Maximum number of feeds to check with autodiscovery 616 * @see SimplePie::set_max_checked_feeds() 617 * @access private 618 */ 619 public $max_checked_feeds = 10; 620 621 /** 622 * @var array All the feeds found during the autodiscovery process 623 * @see SimplePie::get_all_discovered_feeds() 624 * @access private 625 */ 626 public $all_discovered_feeds = array(); 627 628 /** 629 * @var string Web-accessible path to the handler_image.php file. 630 * @see SimplePie::set_image_handler() 631 * @access private 632 */ 633 public $image_handler = ''; 634 635 /** 636 * @var array Stores the URLs when multiple feeds are being initialized. 637 * @see SimplePie::set_feed_url() 638 * @access private 639 */ 640 public $multifeed_url = array(); 641 642 /** 643 * @var array Stores SimplePie objects when multiple feeds initialized. 644 * @access private 645 */ 646 public $multifeed_objects = array(); 647 648 /** 649 * @var array Stores the get_object_vars() array for use with multifeeds. 650 * @see SimplePie::set_feed_url() 651 * @access private 652 */ 653 public $config_settings = null; 654 655 /** 656 * @var integer Stores the number of items to return per-feed with multifeeds. 657 * @see SimplePie::set_item_limit() 658 * @access private 659 */ 660 public $item_limit = 0; 661 662 /** 663 * @var bool Stores if last-modified and/or etag headers were sent with the 664 * request when checking a feed. 665 */ 666 public $check_modified = false; 667 668 /** 669 * @var array Stores the default attributes to be stripped by strip_attributes(). 670 * @see SimplePie::strip_attributes() 671 * @access private 672 */ 673 public $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'); 674 675 /** 676 * @var array Stores the default attributes to add to different tags by add_attributes(). 677 * @see SimplePie::add_attributes() 678 * @access private 679 */ 680 public $add_attributes = array('audio' => array('preload' => 'none'), 'iframe' => array('sandbox' => 'allow-scripts allow-same-origin'), 'video' => array('preload' => 'none')); 681 682 /** 683 * @var array Stores the default tags to be stripped by strip_htmltags(). 684 * @see SimplePie::strip_htmltags() 685 * @access private 686 */ 687 public $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'); 688 689 /** 690 * @var bool Should we throw exceptions, or use the old-style error property? 691 * @access private 692 */ 693 public $enable_exceptions = false; 694 695 /** 696 * The SimplePie class contains feed level data and options 697 * 698 * To use SimplePie, create the SimplePie object with no parameters. You can 699 * then set configuration options using the provided methods. After setting 700 * them, you must initialise the feed using $feed->init(). At that point the 701 * object's methods and properties will be available to you. 702 * 703 * Previously, it was possible to pass in the feed URL along with cache 704 * options directly into the constructor. This has been removed as of 1.3 as 705 * it caused a lot of confusion. 706 * 707 * @since 1.0 Preview Release 708 */ 709 public function __construct() 710 { 711 if (version_compare(PHP_VERSION, '5.6', '<')) 712 { 713 trigger_error('Please upgrade to PHP 5.6 or newer.'); 714 die(); 715 } 716 717 // Other objects, instances created here so we can set options on them 718 $this->sanitize = new SimplePie_Sanitize(); 719 $this->registry = new SimplePie_Registry(); 720 721 if (func_num_args() > 0) 722 { 723 $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING; 724 trigger_error('Passing parameters to the constructor is no longer supported. Please use set_feed_url(), set_cache_location(), and set_cache_duration() directly.', $level); 725 726 $args = func_get_args(); 727 switch (count($args)) { 728 case 3: 729 $this->set_cache_duration($args[2]); 730 case 2: 731 $this->set_cache_location($args[1]); 732 case 1: 733 $this->set_feed_url($args[0]); 734 $this->init(); 735 } 736 } 737 } 738 739 /** 740 * Used for converting object to a string 741 */ 742 public function __toString() 743 { 744 return md5(serialize($this->data)); 745 } 746 747 /** 748 * Remove items that link back to this before destroying this object 749 */ 750 public function __destruct() 751 { 752 if (!gc_enabled()) 753 { 754 if (!empty($this->data['items'])) 755 { 756 foreach ($this->data['items'] as $item) 757 { 758 $item->__destruct(); 759 } 760 unset($item, $this->data['items']); 761 } 762 if (!empty($this->data['ordered_items'])) 763 { 764 foreach ($this->data['ordered_items'] as $item) 765 { 766 $item->__destruct(); 767 } 768 unset($item, $this->data['ordered_items']); 769 } 770 } 771 } 772 773 /** 774 * Force the given data/URL to be treated as a feed 775 * 776 * This tells SimplePie to ignore the content-type provided by the server. 777 * Be careful when using this option, as it will also disable autodiscovery. 778 * 779 * @since 1.1 780 * @param bool $enable Force the given data/URL to be treated as a feed 781 */ 782 public function force_feed($enable = false) 783 { 784 $this->force_feed = (bool) $enable; 785 } 786 787 /** 788 * Set the URL of the feed you want to parse 789 * 790 * This allows you to enter the URL of the feed you want to parse, or the 791 * website you want to try to use auto-discovery on. This takes priority 792 * over any set raw data. 793 * 794 * You can set multiple feeds to mash together by passing an array instead 795 * of a string for the $url. Remember that with each additional feed comes 796 * additional processing and resources. 797 * 798 * @since 1.0 Preview Release 799 * @see set_raw_data() 800 * @param string|array $url This is the URL (or array of URLs) that you want to parse. 801 */ 802 public function set_feed_url($url) 803 { 804 $this->multifeed_url = array(); 805 if (is_array($url)) 806 { 807 foreach ($url as $value) 808 { 809 $this->multifeed_url[] = $this->registry->call('Misc', 'fix_protocol', array($value, 1)); 810 } 811 } 812 else 813 { 814 $this->feed_url = $this->registry->call('Misc', 'fix_protocol', array($url, 1)); 815 $this->permanent_url = $this->feed_url; 816 } 817 } 818 819 /** 820 * Set an instance of {@see SimplePie_File} to use as a feed 821 * 822 * @param SimplePie_File &$file 823 * @return bool True on success, false on failure 824 */ 825 public function set_file(&$file) 826 { 827 if ($file instanceof SimplePie_File) 828 { 829 $this->feed_url = $file->url; 830 $this->permanent_url = $this->feed_url; 831 $this->file =& $file; 832 return true; 833 } 834 return false; 835 } 836 837 /** 838 * Set the raw XML data to parse 839 * 840 * Allows you to use a string of RSS/Atom data instead of a remote feed. 841 * 842 * If you have a feed available as a string in PHP, you can tell SimplePie 843 * to parse that data string instead of a remote feed. Any set feed URL 844 * takes precedence. 845 * 846 * @since 1.0 Beta 3 847 * @param string $data RSS or Atom data as a string. 848 * @see set_feed_url() 849 */ 850 public function set_raw_data($data) 851 { 852 $this->raw_data = $data; 853 } 854 855 /** 856 * Set the default timeout for fetching remote feeds 857 * 858 * This allows you to change the maximum time the feed's server to respond 859 * and send the feed back. 860 * 861 * @since 1.0 Beta 3 862 * @param int $timeout The maximum number of seconds to spend waiting to retrieve a feed. 863 */ 864 public function set_timeout($timeout = 10) 865 { 866 $this->timeout = (int) $timeout; 867 } 868 869 /** 870 * Set custom curl options 871 * 872 * This allows you to change default curl options 873 * 874 * @since 1.0 Beta 3 875 * @param array $curl_options Curl options to add to default settings 876 */ 877 public function set_curl_options(array $curl_options = array()) 878 { 879 $this->curl_options = $curl_options; 880 } 881 882 /** 883 * Force SimplePie to use fsockopen() instead of cURL 884 * 885 * @since 1.0 Beta 3 886 * @param bool $enable Force fsockopen() to be used 887 */ 888 public function force_fsockopen($enable = false) 889 { 890 $this->force_fsockopen = (bool) $enable; 891 } 892 893 /** 894 * Enable/disable caching in SimplePie. 895 * 896 * This option allows you to disable caching all-together in SimplePie. 897 * However, disabling the cache can lead to longer load times. 898 * 899 * @since 1.0 Preview Release 900 * @param bool $enable Enable caching 901 */ 902 public function enable_cache($enable = true) 903 { 904 $this->cache = (bool) $enable; 905 } 906 907 /** 908 * SimplePie to continue to fall back to expired cache, if enabled, when 909 * feed is unavailable. 910 * 911 * This tells SimplePie to ignore any file errors and fall back to cache 912 * instead. This only works if caching is enabled and cached content 913 * still exists. 914 915 * @param bool $enable Force use of cache on fail. 916 */ 917 public function force_cache_fallback($enable = false) 918 { 919 $this->force_cache_fallback= (bool) $enable; 920 } 921 922 /** 923 * Set the length of time (in seconds) that the contents of a feed will be 924 * cached 925 * 926 * @param int $seconds The feed content cache duration 927 */ 928 public function set_cache_duration($seconds = 3600) 929 { 930 $this->cache_duration = (int) $seconds; 931 } 932 933 /** 934 * Set the length of time (in seconds) that the autodiscovered feed URL will 935 * be cached 936 * 937 * @param int $seconds The autodiscovered feed URL cache duration. 938 */ 939 public function set_autodiscovery_cache_duration($seconds = 604800) 940 { 941 $this->autodiscovery_cache_duration = (int) $seconds; 942 } 943 944 /** 945 * Set the file system location where the cached files should be stored 946 * 947 * @param string $location The file system location. 948 */ 949 public function set_cache_location($location = './cache') 950 { 951 $this->cache_location = (string) $location; 952 } 953 954 /** 955 * Return the filename (i.e. hash, without path and without extension) of the file to cache a given URL. 956 * @param string $url The URL of the feed to be cached. 957 * @return string A filename (i.e. hash, without path and without extension). 958 */ 959 public function get_cache_filename($url) 960 { 961 // Append custom parameters to the URL to avoid cache pollution in case of multiple calls with different parameters. 962 $url .= $this->force_feed ? '#force_feed' : ''; 963 $options = array(); 964 if ($this->timeout != 10) 965 { 966 $options[CURLOPT_TIMEOUT] = $this->timeout; 967 } 968 if ($this->useragent !== SIMPLEPIE_USERAGENT) 969 { 970 $options[CURLOPT_USERAGENT] = $this->useragent; 971 } 972 if (!empty($this->curl_options)) 973 { 974 foreach ($this->curl_options as $k => $v) 975 { 976 $options[$k] = $v; 977 } 978 } 979 if (!empty($options)) 980 { 981 ksort($options); 982 $url .= '#' . urlencode(var_export($options, true)); 983 } 984 return call_user_func($this->cache_name_function, $url); 985 } 986 987 /** 988 * Set whether feed items should be sorted into reverse chronological order 989 * 990 * @param bool $enable Sort as reverse chronological order. 991 */ 992 public function enable_order_by_date($enable = true) 993 { 994 $this->order_by_date = (bool) $enable; 995 } 996 997 /** 998 * Set the character encoding used to parse the feed 999 * 1000 * This overrides the encoding reported by the feed, however it will fall 1001 * back to the normal encoding detection if the override fails 1002 * 1003 * @param string $encoding Character encoding 1004 */ 1005 public function set_input_encoding($encoding = false) 1006 { 1007 if ($encoding) 1008 { 1009 $this->input_encoding = (string) $encoding; 1010 } 1011 else 1012 { 1013 $this->input_encoding = false; 1014 } 1015 } 1016 1017 /** 1018 * Set how much feed autodiscovery to do 1019 * 1020 * @see SIMPLEPIE_LOCATOR_NONE 1021 * @see SIMPLEPIE_LOCATOR_AUTODISCOVERY 1022 * @see SIMPLEPIE_LOCATOR_LOCAL_EXTENSION 1023 * @see SIMPLEPIE_LOCATOR_LOCAL_BODY 1024 * @see SIMPLEPIE_LOCATOR_REMOTE_EXTENSION 1025 * @see SIMPLEPIE_LOCATOR_REMOTE_BODY 1026 * @see SIMPLEPIE_LOCATOR_ALL 1027 * @param int $level Feed Autodiscovery Level (level can be a combination of the above constants, see bitwise OR operator) 1028 */ 1029 public function set_autodiscovery_level($level = SIMPLEPIE_LOCATOR_ALL) 1030 { 1031 $this->autodiscovery = (int) $level; 1032 } 1033 1034 /** 1035 * Get the class registry 1036 * 1037 * Use this to override SimplePie's default classes 1038 * @see SimplePie_Registry 1039 * @return SimplePie_Registry 1040 */ 1041 public function &get_registry() 1042 { 1043 return $this->registry; 1044 } 1045 1046 /**#@+ 1047 * Useful when you are overloading or extending SimplePie's default classes. 1048 * 1049 * @deprecated Use {@see get_registry()} instead 1050 * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation 1051 * @param string $class Name of custom class 1052 * @return boolean True on success, false otherwise 1053 */ 1054 /** 1055 * Set which class SimplePie uses for caching 1056 */ 1057 public function set_cache_class($class = 'SimplePie_Cache') 1058 { 1059 return $this->registry->register('Cache', $class, true); 1060 } 1061 1062 /** 1063 * Set which class SimplePie uses for auto-discovery 1064 */ 1065 public function set_locator_class($class = 'SimplePie_Locator') 1066 { 1067 return $this->registry->register('Locator', $class, true); 1068 } 1069 1070 /** 1071 * Set which class SimplePie uses for XML parsing 1072 */ 1073 public function set_parser_class($class = 'SimplePie_Parser') 1074 { 1075 return $this->registry->register('Parser', $class, true); 1076 } 1077 1078 /** 1079 * Set which class SimplePie uses for remote file fetching 1080 */ 1081 public function set_file_class($class = 'SimplePie_File') 1082 { 1083 return $this->registry->register('File', $class, true); 1084 } 1085 1086 /** 1087 * Set which class SimplePie uses for data sanitization 1088 */ 1089 public function set_sanitize_class($class = 'SimplePie_Sanitize') 1090 { 1091 return $this->registry->register('Sanitize', $class, true); 1092 } 1093 1094 /** 1095 * Set which class SimplePie uses for handling feed items 1096 */ 1097 public function set_item_class($class = 'SimplePie_Item') 1098 { 1099 return $this->registry->register('Item', $class, true); 1100 } 1101 1102 /** 1103 * Set which class SimplePie uses for handling author data 1104 */ 1105 public function set_author_class($class = 'SimplePie_Author') 1106 { 1107 return $this->registry->register('Author', $class, true); 1108 } 1109 1110 /** 1111 * Set which class SimplePie uses for handling category data 1112 */ 1113 public function set_category_class($class = 'SimplePie_Category') 1114 { 1115 return $this->registry->register('Category', $class, true); 1116 } 1117 1118 /** 1119 * Set which class SimplePie uses for feed enclosures 1120 */ 1121 public function set_enclosure_class($class = 'SimplePie_Enclosure') 1122 { 1123 return $this->registry->register('Enclosure', $class, true); 1124 } 1125 1126 /** 1127 * Set which class SimplePie uses for `<media:text>` captions 1128 */ 1129 public function set_caption_class($class = 'SimplePie_Caption') 1130 { 1131 return $this->registry->register('Caption', $class, true); 1132 } 1133 1134 /** 1135 * Set which class SimplePie uses for `<media:copyright>` 1136 */ 1137 public function set_copyright_class($class = 'SimplePie_Copyright') 1138 { 1139 return $this->registry->register('Copyright', $class, true); 1140 } 1141 1142 /** 1143 * Set which class SimplePie uses for `<media:credit>` 1144 */ 1145 public function set_credit_class($class = 'SimplePie_Credit') 1146 { 1147 return $this->registry->register('Credit', $class, true); 1148 } 1149 1150 /** 1151 * Set which class SimplePie uses for `<media:rating>` 1152 */ 1153 public function set_rating_class($class = 'SimplePie_Rating') 1154 { 1155 return $this->registry->register('Rating', $class, true); 1156 } 1157 1158 /** 1159 * Set which class SimplePie uses for `<media:restriction>` 1160 */ 1161 public function set_restriction_class($class = 'SimplePie_Restriction') 1162 { 1163 return $this->registry->register('Restriction', $class, true); 1164 } 1165 1166 /** 1167 * Set which class SimplePie uses for content-type sniffing 1168 */ 1169 public function set_content_type_sniffer_class($class = 'SimplePie_Content_Type_Sniffer') 1170 { 1171 return $this->registry->register('Content_Type_Sniffer', $class, true); 1172 } 1173 1174 /** 1175 * Set which class SimplePie uses item sources 1176 */ 1177 public function set_source_class($class = 'SimplePie_Source') 1178 { 1179 return $this->registry->register('Source', $class, true); 1180 } 1181 /**#@-*/ 1182 1183 /** 1184 * Set the user agent string 1185 * 1186 * @param string $ua New user agent string. 1187 */ 1188 public function set_useragent($ua = SIMPLEPIE_USERAGENT) 1189 { 1190 $this->useragent = (string) $ua; 1191 } 1192 1193 /** 1194 * Set callback function to create cache filename with 1195 * 1196 * @param mixed $function Callback function 1197 */ 1198 public function set_cache_name_function($function = 'md5') 1199 { 1200 if (is_callable($function)) 1201 { 1202 $this->cache_name_function = $function; 1203 } 1204 } 1205 1206 /** 1207 * Set options to make SimplePie as fast as possible. 1208 * 1209 * Forgoes a substantial amount of data sanitization in favor of speed. 1210 * This turns SimplePie into a less clever parser of feeds. 1211 * 1212 * @param bool $set Whether to set them or not. 1213 */ 1214 public function set_stupidly_fast($set = false) 1215 { 1216 if ($set) 1217 { 1218 $this->enable_order_by_date(false); 1219 $this->remove_div(false); 1220 $this->strip_comments(false); 1221 $this->strip_htmltags(false); 1222 $this->strip_attributes(false); 1223 $this->add_attributes(false); 1224 $this->set_image_handler(false); 1225 $this->set_https_domains(array()); 1226 } 1227 } 1228 1229 /** 1230 * Set maximum number of feeds to check with autodiscovery 1231 * 1232 * @param int $max Maximum number of feeds to check 1233 */ 1234 public function set_max_checked_feeds($max = 10) 1235 { 1236 $this->max_checked_feeds = (int) $max; 1237 } 1238 1239 public function remove_div($enable = true) 1240 { 1241 $this->sanitize->remove_div($enable); 1242 } 1243 1244 public function strip_htmltags($tags = '', $encode = null) 1245 { 1246 if ($tags === '') 1247 { 1248 $tags = $this->strip_htmltags; 1249 } 1250 $this->sanitize->strip_htmltags($tags); 1251 if ($encode !== null) 1252 { 1253 $this->sanitize->encode_instead_of_strip($tags); 1254 } 1255 } 1256 1257 public function encode_instead_of_strip($enable = true) 1258 { 1259 $this->sanitize->encode_instead_of_strip($enable); 1260 } 1261 1262 public function strip_attributes($attribs = '') 1263 { 1264 if ($attribs === '') 1265 { 1266 $attribs = $this->strip_attributes; 1267 } 1268 $this->sanitize->strip_attributes($attribs); 1269 } 1270 1271 public function add_attributes($attribs = '') 1272 { 1273 if ($attribs === '') 1274 { 1275 $attribs = $this->add_attributes; 1276 } 1277 $this->sanitize->add_attributes($attribs); 1278 } 1279 1280 /** 1281 * Set the output encoding 1282 * 1283 * Allows you to override SimplePie's output to match that of your webpage. 1284 * This is useful for times when your webpages are not being served as 1285 * UTF-8. This setting will be obeyed by {@see handle_content_type()}, and 1286 * is similar to {@see set_input_encoding()}. 1287 * 1288 * It should be noted, however, that not all character encodings can support 1289 * all characters. If your page is being served as ISO-8859-1 and you try 1290 * to display a Japanese feed, you'll likely see garbled characters. 1291 * Because of this, it is highly recommended to ensure that your webpages 1292 * are served as UTF-8. 1293 * 1294 * The number of supported character encodings depends on whether your web 1295 * host supports {@link http://php.net/mbstring mbstring}, 1296 * {@link http://php.net/iconv iconv}, or both. See 1297 * {@link http://simplepie.org/wiki/faq/Supported_Character_Encodings} for 1298 * more information. 1299 * 1300 * @param string $encoding 1301 */ 1302 public function set_output_encoding($encoding = 'UTF-8') 1303 { 1304 $this->sanitize->set_output_encoding($encoding); 1305 } 1306 1307 public function strip_comments($strip = false) 1308 { 1309 $this->sanitize->strip_comments($strip); 1310 } 1311 1312 /** 1313 * Set element/attribute key/value pairs of HTML attributes 1314 * containing URLs that need to be resolved relative to the feed 1315 * 1316 * Defaults to |a|@href, |area|@href, |blockquote|@cite, |del|@cite, 1317 * |form|@action, |img|@longdesc, |img|@src, |input|@src, |ins|@cite, 1318 * |q|@cite 1319 * 1320 * @since 1.0 1321 * @param array|null $element_attribute Element/attribute key/value pairs, null for default 1322 */ 1323 public function set_url_replacements($element_attribute = null) 1324 { 1325 $this->sanitize->set_url_replacements($element_attribute); 1326 } 1327 1328 /** 1329 * Set the list of domains for which to force HTTPS. 1330 * @see SimplePie_Sanitize::set_https_domains() 1331 * @param array List of HTTPS domains. Example array('biz', 'example.com', 'example.org', 'www.example.net'). 1332 */ 1333 public function set_https_domains($domains = array()) 1334 { 1335 if (is_array($domains)) 1336 { 1337 $this->sanitize->set_https_domains($domains); 1338 } 1339 } 1340 1341 /** 1342 * Set the handler to enable the display of cached images. 1343 * 1344 * @param string $page Web-accessible path to the handler_image.php file. 1345 * @param string $qs The query string that the value should be passed to. 1346 */ 1347 public function set_image_handler($page = false, $qs = 'i') 1348 { 1349 if ($page !== false) 1350 { 1351 $this->sanitize->set_image_handler($page . '?' . $qs . '='); 1352 } 1353 else 1354 { 1355 $this->image_handler = ''; 1356 } 1357 } 1358 1359 /** 1360 * Set the limit for items returned per-feed with multifeeds 1361 * 1362 * @param integer $limit The maximum number of items to return. 1363 */ 1364 public function set_item_limit($limit = 0) 1365 { 1366 $this->item_limit = (int) $limit; 1367 } 1368 1369 /** 1370 * Enable throwing exceptions 1371 * 1372 * @param boolean $enable Should we throw exceptions, or use the old-style error property? 1373 */ 1374 public function enable_exceptions($enable = true) 1375 { 1376 $this->enable_exceptions = $enable; 1377 } 1378 1379 /** 1380 * Initialize the feed object 1381 * 1382 * This is what makes everything happen. Period. This is where all of the 1383 * configuration options get processed, feeds are fetched, cached, and 1384 * parsed, and all of that other good stuff. 1385 * 1386 * @return boolean True if successful, false otherwise 1387 */ 1388 public function init() 1389 { 1390 // Check absolute bare minimum requirements. 1391 if (!extension_loaded('xml') || !extension_loaded('pcre')) 1392 { 1393 $this->error = 'XML or PCRE extensions not loaded!'; 1394 return false; 1395 } 1396 // Then check the xml extension is sane (i.e., libxml 2.7.x issue on PHP < 5.2.9 and libxml 2.7.0 to 2.7.2 on any version) if we don't have xmlreader. 1397 elseif (!extension_loaded('xmlreader')) 1398 { 1399 static $xml_is_sane = null; 1400 if ($xml_is_sane === null) 1401 { 1402 $parser_check = xml_parser_create(); 1403 xml_parse_into_struct($parser_check, '<foo>&</foo>', $values); 1404 xml_parser_free($parser_check); 1405 $xml_is_sane = isset($values[0]['value']); 1406 } 1407 if (!$xml_is_sane) 1408 { 1409 return false; 1410 } 1411 } 1412 1413 // The default sanitize class gets set in the constructor, check if it has 1414 // changed. 1415 if ($this->registry->get_class('Sanitize') !== 'SimplePie_Sanitize') { 1416 $this->sanitize = $this->registry->create('Sanitize'); 1417 } 1418 if (method_exists($this->sanitize, 'set_registry')) 1419 { 1420 $this->sanitize->set_registry($this->registry); 1421 } 1422 1423 // Pass whatever was set with config options over to the sanitizer. 1424 // Pass the classes in for legacy support; new classes should use the registry instead 1425 $this->sanitize->pass_cache_data($this->cache, $this->cache_location, $this->cache_name_function, $this->registry->get_class('Cache')); 1426 $this->sanitize->pass_file_data($this->registry->get_class('File'), $this->timeout, $this->useragent, $this->force_fsockopen, $this->curl_options); 1427 1428 if (!empty($this->multifeed_url)) 1429 { 1430 $i = 0; 1431 $success = 0; 1432 $this->multifeed_objects = array(); 1433 $this->error = array(); 1434 foreach ($this->multifeed_url as $url) 1435 { 1436 $this->multifeed_objects[$i] = clone $this; 1437 $this->multifeed_objects[$i]->set_feed_url($url); 1438 $single_success = $this->multifeed_objects[$i]->init(); 1439 $success |= $single_success; 1440 if (!$single_success) 1441 { 1442 $this->error[$i] = $this->multifeed_objects[$i]->error(); 1443 } 1444 $i++; 1445 } 1446 return (bool) $success; 1447 } 1448 elseif ($this->feed_url === null && $this->raw_data === null) 1449 { 1450 return false; 1451 } 1452 1453 $this->error = null; 1454 $this->data = array(); 1455 $this->check_modified = false; 1456 $this->multifeed_objects = array(); 1457 $cache = false; 1458 1459 if ($this->feed_url !== null) 1460 { 1461 $parsed_feed_url = $this->registry->call('Misc', 'parse_url', array($this->feed_url)); 1462 1463 // Decide whether to enable caching 1464 if ($this->cache && $parsed_feed_url['scheme'] !== '') 1465 { 1466 $filename = $this->get_cache_filename($this->feed_url); 1467 $cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, $filename, 'spc')); 1468 } 1469 1470 // Fetch the data via SimplePie_File into $this->raw_data 1471 if (($fetched = $this->fetch_data($cache)) === true) 1472 { 1473 return true; 1474 } 1475 elseif ($fetched === false) { 1476 return false; 1477 } 1478 1479 list($headers, $sniffed) = $fetched; 1480 } 1481 1482 // Empty response check 1483 if(empty($this->raw_data)){ 1484 $this->error = "A feed could not be found at `$this->feed_url`. Empty body."; 1485 $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); 1486 return false; 1487 } 1488 1489 // Set up array of possible encodings 1490 $encodings = array(); 1491 1492 // First check to see if input has been overridden. 1493 if ($this->input_encoding !== false) 1494 { 1495 $encodings[] = strtoupper($this->input_encoding); 1496 } 1497 1498 $application_types = array('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity'); 1499 $text_types = array('text/xml', 'text/xml-external-parsed-entity'); 1500 1501 // RFC 3023 (only applies to sniffed content) 1502 if (isset($sniffed)) 1503 { 1504 if (in_array($sniffed, $application_types) || substr($sniffed, 0, 12) === 'application/' && substr($sniffed, -4) === '+xml') 1505 { 1506 if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) 1507 { 1508 $encodings[] = strtoupper($charset[1]); 1509 } 1510 $encodings = array_merge($encodings, $this->registry->call('Misc', 'xml_encoding', array($this->raw_data, &$this->registry))); 1511 $encodings[] = 'UTF-8'; 1512 } 1513 elseif (in_array($sniffed, $text_types) || substr($sniffed, 0, 5) === 'text/' && substr($sniffed, -4) === '+xml') 1514 { 1515 if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) 1516 { 1517 $encodings[] = strtoupper($charset[1]); 1518 } 1519 $encodings[] = 'US-ASCII'; 1520 } 1521 // Text MIME-type default 1522 elseif (substr($sniffed, 0, 5) === 'text/') 1523 { 1524 $encodings[] = 'UTF-8'; 1525 } 1526 } 1527 1528 // Fallback to XML 1.0 Appendix F.1/UTF-8/ISO-8859-1 1529 $encodings = array_merge($encodings, $this->registry->call('Misc', 'xml_encoding', array($this->raw_data, &$this->registry))); 1530 $encodings[] = 'UTF-8'; 1531 $encodings[] = 'ISO-8859-1'; 1532 1533 // There's no point in trying an encoding twice 1534 $encodings = array_unique($encodings); 1535 1536 // Loop through each possible encoding, till we return something, or run out of possibilities 1537 foreach ($encodings as $encoding) 1538 { 1539 // Change the encoding to UTF-8 (as we always use UTF-8 internally) 1540 if ($utf8_data = $this->registry->call('Misc', 'change_encoding', array($this->raw_data, $encoding, 'UTF-8'))) 1541 { 1542 // Create new parser 1543 $parser = $this->registry->create('Parser'); 1544 1545 // If it's parsed fine 1546 if ($parser->parse($utf8_data, 'UTF-8', $this->permanent_url)) 1547 { 1548 $this->data = $parser->get_data(); 1549 if (!($this->get_type() & ~SIMPLEPIE_TYPE_NONE)) 1550 { 1551 $this->error = "A feed could not be found at `$this->feed_url`. This does not appear to be a valid RSS or Atom feed."; 1552 $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); 1553 return false; 1554 } 1555 1556 if (isset($headers)) 1557 { 1558 $this->data['headers'] = $headers; 1559 } 1560 $this->data['build'] = SIMPLEPIE_BUILD; 1561 1562 // Cache the file if caching is enabled 1563 if ($cache && !$cache->save($this)) 1564 { 1565 trigger_error("$this->cache_location is not writable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); 1566 } 1567 return true; 1568 } 1569 } 1570 } 1571 1572 if (isset($parser)) 1573 { 1574 // We have an error, just set SimplePie_Misc::error to it and quit 1575 $this->error = $this->feed_url; 1576 $this->error .= sprintf(' is invalid XML, likely due to invalid characters. XML error: %s at line %d, column %d', $parser->get_error_string(), $parser->get_current_line(), $parser->get_current_column()); 1577 } 1578 else 1579 { 1580 $this->error = 'The data could not be converted to UTF-8.'; 1581 if (!extension_loaded('mbstring') && !extension_loaded('iconv') && !class_exists('\UConverter')) { 1582 $this->error .= ' You MUST have either the iconv, mbstring or intl (PHP 5.5+) extension installed and enabled.'; 1583 } else { 1584 $missingExtensions = array(); 1585 if (!extension_loaded('iconv')) { 1586 $missingExtensions[] = 'iconv'; 1587 } 1588 if (!extension_loaded('mbstring')) { 1589 $missingExtensions[] = 'mbstring'; 1590 } 1591 if (!class_exists('\UConverter')) { 1592 $missingExtensions[] = 'intl (PHP 5.5+)'; 1593 } 1594 $this->error .= ' Try installing/enabling the ' . implode(' or ', $missingExtensions) . ' extension.'; 1595 } 1596 } 1597 1598 $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); 1599 1600 return false; 1601 } 1602 1603 /** 1604 * Fetch the data via SimplePie_File 1605 * 1606 * If the data is already cached, attempt to fetch it from there instead 1607 * @param SimplePie_Cache_Base|false $cache Cache handler, or false to not load from the cache 1608 * @return array|true Returns true if the data was loaded from the cache, or an array of HTTP headers and sniffed type 1609 */ 1610 protected function fetch_data(&$cache) 1611 { 1612 // If it's enabled, use the cache 1613 if ($cache) 1614 { 1615 // Load the Cache 1616 $this->data = $cache->load(); 1617 if (!empty($this->data)) 1618 { 1619 // If the cache is for an outdated build of SimplePie 1620 if (!isset($this->data['build']) || $this->data['build'] !== SIMPLEPIE_BUILD) 1621 { 1622 $cache->unlink(); 1623 $this->data = array(); 1624 } 1625 // If we've hit a collision just rerun it with caching disabled 1626 elseif (isset($this->data['url']) && $this->data['url'] !== $this->feed_url) 1627 { 1628 $cache = false; 1629 $this->data = array(); 1630 } 1631 // If we've got a non feed_url stored (if the page isn't actually a feed, or is a redirect) use that URL. 1632 elseif (isset($this->data['feed_url'])) 1633 { 1634 // If the autodiscovery cache is still valid use it. 1635 if ($cache->mtime() + $this->autodiscovery_cache_duration > time()) 1636 { 1637 // Do not need to do feed autodiscovery yet. 1638 if ($this->data['feed_url'] !== $this->data['url']) 1639 { 1640 $this->set_feed_url($this->data['feed_url']); 1641 return $this->init(); 1642 } 1643 1644 $cache->unlink(); 1645 $this->data = array(); 1646 } 1647 } 1648 // Check if the cache has been updated 1649 elseif ($cache->mtime() + $this->cache_duration < time()) 1650 { 1651 // Want to know if we tried to send last-modified and/or etag headers 1652 // when requesting this file. (Note that it's up to the file to 1653 // support this, but we don't always send the headers either.) 1654 $this->check_modified = true; 1655 if (isset($this->data['headers']['last-modified']) || isset($this->data['headers']['etag'])) 1656 { 1657 $headers = array( 1658 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1', 1659 ); 1660 if (isset($this->data['headers']['last-modified'])) 1661 { 1662 $headers['if-modified-since'] = $this->data['headers']['last-modified']; 1663 } 1664 if (isset($this->data['headers']['etag'])) 1665 { 1666 $headers['if-none-match'] = $this->data['headers']['etag']; 1667 } 1668 1669 $file = $this->registry->create('File', array($this->feed_url, $this->timeout/10, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options)); 1670 $this->status_code = $file->status_code; 1671 1672 if ($file->success) 1673 { 1674 if ($file->status_code === 304) 1675 { 1676 // Set raw_data to false here too, to signify that the cache 1677 // is still valid. 1678 $this->raw_data = false; 1679 $cache->touch(); 1680 return true; 1681 } 1682 } 1683 else 1684 { 1685 $this->check_modified = false; 1686 if($this->force_cache_fallback) 1687 { 1688 $cache->touch(); 1689 return true; 1690 } 1691 1692 unset($file); 1693 } 1694 } 1695 } 1696 // If the cache is still valid, just return true 1697 else 1698 { 1699 $this->raw_data = false; 1700 return true; 1701 } 1702 } 1703 // If the cache is empty, delete it 1704 else 1705 { 1706 $cache->unlink(); 1707 $this->data = array(); 1708 } 1709 } 1710 // If we don't already have the file (it'll only exist if we've opened it to check if the cache has been modified), open it. 1711 if (!isset($file)) 1712 { 1713 if ($this->file instanceof SimplePie_File && $this->file->url === $this->feed_url) 1714 { 1715 $file =& $this->file; 1716 } 1717 else 1718 { 1719 $headers = array( 1720 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1', 1721 ); 1722 $file = $this->registry->create('File', array($this->feed_url, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options)); 1723 } 1724 } 1725 $this->status_code = $file->status_code; 1726 1727 // If the file connection has an error, set SimplePie::error to that and quit 1728 if (!$file->success && !($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300))) 1729 { 1730 $this->error = $file->error; 1731 return !empty($this->data); 1732 } 1733 1734 if (!$this->force_feed) 1735 { 1736 // Check if the supplied URL is a feed, if it isn't, look for it. 1737 $locate = $this->registry->create('Locator', array(&$file, $this->timeout, $this->useragent, $this->max_checked_feeds, $this->force_fsockopen, $this->curl_options)); 1738 1739 if (!$locate->is_feed($file)) 1740 { 1741 $copyStatusCode = $file->status_code; 1742 $copyContentType = $file->headers['content-type']; 1743 try 1744 { 1745 $microformats = false; 1746 if (class_exists('DOMXpath') && function_exists('Mf2\parse')) { 1747 $doc = new DOMDocument(); 1748 @$doc->loadHTML($file->body); 1749 $xpath = new DOMXpath($doc); 1750 // Check for both h-feed and h-entry, as both a feed with no entries 1751 // and a list of entries without an h-feed wrapper are both valid. 1752 $query = '//*[contains(concat(" ", @class, " "), " h-feed ") or '. 1753 'contains(concat(" ", @class, " "), " h-entry ")]'; 1754 $result = $xpath->query($query); 1755 $microformats = $result->length !== 0; 1756 } 1757 // Now also do feed discovery, but if microformats were found don't 1758 // overwrite the current value of file. 1759 $discovered = $locate->find($this->autodiscovery, 1760 $this->all_discovered_feeds); 1761 if ($microformats) 1762 { 1763 if ($hub = $locate->get_rel_link('hub')) 1764 { 1765 $self = $locate->get_rel_link('self'); 1766 $this->store_links($file, $hub, $self); 1767 } 1768 // Push the current file onto all_discovered feeds so the user can 1769 // be shown this as one of the options. 1770 if (isset($this->all_discovered_feeds)) { 1771 $this->all_discovered_feeds[] = $file; 1772 } 1773 } 1774 else 1775 { 1776 if ($discovered) 1777 { 1778 $file = $discovered; 1779 } 1780 else 1781 { 1782 // We need to unset this so that if SimplePie::set_file() has 1783 // been called that object is untouched 1784 unset($file); 1785 $this->error = "A feed could not be found at `$this->feed_url`; the status code is `$copyStatusCode` and content-type is `$copyContentType`"; 1786 $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); 1787 return false; 1788 } 1789 } 1790 } 1791 catch (SimplePie_Exception $e) 1792 { 1793 // We need to unset this so that if SimplePie::set_file() has been called that object is untouched 1794 unset($file); 1795 // This is usually because DOMDocument doesn't exist 1796 $this->error = $e->getMessage(); 1797 $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, $e->getFile(), $e->getLine())); 1798 return false; 1799 } 1800 if ($cache) 1801 { 1802 $this->data = array('url' => $this->feed_url, 'feed_url' => $file->url, 'build' => SIMPLEPIE_BUILD); 1803 if (!$cache->save($this)) 1804 { 1805 trigger_error("$this->cache_location is not writable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); 1806 } 1807 $cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $file->url), 'spc')); 1808 } 1809 } 1810 $this->feed_url = $file->url; 1811 $locate = null; 1812 } 1813 1814 $this->raw_data = $file->body; 1815 $this->permanent_url = $file->permanent_url; 1816 $headers = $file->headers; 1817 $sniffer = $this->registry->create('Content_Type_Sniffer', array(&$file)); 1818 $sniffed = $sniffer->get_type(); 1819 1820 return array($headers, $sniffed); 1821 } 1822 1823 /** 1824 * Get the error message for the occurred error 1825 * 1826 * @return string|array Error message, or array of messages for multifeeds 1827 */ 1828 public function error() 1829 { 1830 return $this->error; 1831 } 1832 1833 /** 1834 * Get the last HTTP status code 1835 * 1836 * @return int Status code 1837 */ 1838 public function status_code() 1839 { 1840 return $this->status_code; 1841 } 1842 1843 /** 1844 * Get the raw XML 1845 * 1846 * This is the same as the old `$feed->enable_xml_dump(true)`, but returns 1847 * the data instead of printing it. 1848 * 1849 * @return string|boolean Raw XML data, false if the cache is used 1850 */ 1851 public function get_raw_data() 1852 { 1853 return $this->raw_data; 1854 } 1855 1856 /** 1857 * Get the character encoding used for output 1858 * 1859 * @since Preview Release 1860 * @return string 1861 */ 1862 public function get_encoding() 1863 { 1864 return $this->sanitize->output_encoding; 1865 } 1866 1867 /** 1868 * Send the Content-Type header with correct encoding 1869 * 1870 * This method ensures that the SimplePie-enabled page is being served with 1871 * the correct {@link http://www.iana.org/assignments/media-types/ mime-type} 1872 * and character encoding HTTP headers (character encoding determined by the 1873 * {@see set_output_encoding} config option). 1874 * 1875 * This won't work properly if any content or whitespace has already been 1876 * sent to the browser, because it relies on PHP's 1877 * {@link http://php.net/header header()} function, and these are the 1878 * circumstances under which the function works. 1879 * 1880 * Because it's setting these settings for the entire page (as is the nature 1881 * of HTTP headers), this should only be used once per page (again, at the 1882 * top). 1883 * 1884 * @param string $mime MIME type to serve the page as 1885 */ 1886 public function handle_content_type($mime = 'text/html') 1887 { 1888 if (!headers_sent()) 1889 { 1890 $header = "Content-Type: $mime;"; 1891 if ($this->get_encoding()) 1892 { 1893 $header .= ' charset=' . $this->get_encoding(); 1894 } 1895 else 1896 { 1897 $header .= ' charset=UTF-8'; 1898 } 1899 header($header); 1900 } 1901 } 1902 1903 /** 1904 * Get the type of the feed 1905 * 1906 * This returns a SIMPLEPIE_TYPE_* constant, which can be tested against 1907 * using {@link http://php.net/language.operators.bitwise bitwise operators} 1908 * 1909 * @since 0.8 (usage changed to using constants in 1.0) 1910 * @see SIMPLEPIE_TYPE_NONE Unknown. 1911 * @see SIMPLEPIE_TYPE_RSS_090 RSS 0.90. 1912 * @see SIMPLEPIE_TYPE_RSS_091_NETSCAPE RSS 0.91 (Netscape). 1913 * @see SIMPLEPIE_TYPE_RSS_091_USERLAND RSS 0.91 (Userland). 1914 * @see SIMPLEPIE_TYPE_RSS_091 RSS 0.91. 1915 * @see SIMPLEPIE_TYPE_RSS_092 RSS 0.92. 1916 * @see SIMPLEPIE_TYPE_RSS_093 RSS 0.93. 1917 * @see SIMPLEPIE_TYPE_RSS_094 RSS 0.94. 1918 * @see SIMPLEPIE_TYPE_RSS_10 RSS 1.0. 1919 * @see SIMPLEPIE_TYPE_RSS_20 RSS 2.0.x. 1920 * @see SIMPLEPIE_TYPE_RSS_RDF RDF-based RSS. 1921 * @see SIMPLEPIE_TYPE_RSS_SYNDICATION Non-RDF-based RSS (truly intended as syndication format). 1922 * @see SIMPLEPIE_TYPE_RSS_ALL Any version of RSS. 1923 * @see SIMPLEPIE_TYPE_ATOM_03 Atom 0.3. 1924 * @see SIMPLEPIE_TYPE_ATOM_10 Atom 1.0. 1925 * @see SIMPLEPIE_TYPE_ATOM_ALL Any version of Atom. 1926 * @see SIMPLEPIE_TYPE_ALL Any known/supported feed type. 1927 * @return int SIMPLEPIE_TYPE_* constant 1928 */ 1929 public function get_type() 1930 { 1931 if (!isset($this->data['type'])) 1932 { 1933 $this->data['type'] = SIMPLEPIE_TYPE_ALL; 1934 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'])) 1935 { 1936 $this->data['type'] &= SIMPLEPIE_TYPE_ATOM_10; 1937 } 1938 elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'])) 1939 { 1940 $this->data['type'] &= SIMPLEPIE_TYPE_ATOM_03; 1941 } 1942 elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'])) 1943 { 1944 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['channel']) 1945 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['image']) 1946 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']) 1947 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['textinput'])) 1948 { 1949 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_10; 1950 } 1951 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['channel']) 1952 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['image']) 1953 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']) 1954 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['textinput'])) 1955 { 1956 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_090; 1957 } 1958 } 1959 elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'])) 1960 { 1961 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_ALL; 1962 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version'])) 1963 { 1964 switch (trim($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version'])) 1965 { 1966 case '0.91': 1967 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091; 1968 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['skiphours']['hour'][0]['data'])) 1969 { 1970 switch (trim($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['skiphours']['hour'][0]['data'])) 1971 { 1972 case '0': 1973 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_NETSCAPE; 1974 break; 1975 1976 case '24': 1977 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_USERLAND; 1978 break; 1979 } 1980 } 1981 break; 1982 1983 case '0.92': 1984 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_092; 1985 break; 1986 1987 case '0.93': 1988 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_093; 1989 break; 1990 1991 case '0.94': 1992 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_094; 1993 break; 1994 1995 case '2.0': 1996 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_20; 1997 break; 1998 } 1999 } 2000 } 2001 else 2002 { 2003 $this->data['type'] = SIMPLEPIE_TYPE_NONE; 2004 } 2005 } 2006 return $this->data['type']; 2007 } 2008 2009 /** 2010 * Get the URL for the feed 2011 * 2012 * When the 'permanent' mode is enabled, returns the original feed URL, 2013 * except in the case of an `HTTP 301 Moved Permanently` status response, 2014 * in which case the location of the first redirection is returned. 2015 * 2016 * When the 'permanent' mode is disabled (default), 2017 * may or may not be different from the URL passed to {@see set_feed_url()}, 2018 * depending on whether auto-discovery was used, and whether there were 2019 * any redirects along the way. 2020 * 2021 * @since Preview Release (previously called `get_feed_url()` since SimplePie 0.8.) 2022 * @todo Support <itunes:new-feed-url> 2023 * @todo Also, |atom:link|@rel=self 2024 * @param bool $permanent Permanent mode to return only the original URL or the first redirection 2025 * iff it is a 301 redirection 2026 * @return string|null 2027 */ 2028 public function subscribe_url($permanent = false) 2029 { 2030 if ($permanent) 2031 { 2032 if ($this->permanent_url !== null) 2033 { 2034 // sanitize encodes ampersands which are required when used in a url. 2035 return str_replace('&', '&', 2036 $this->sanitize($this->permanent_url, 2037 SIMPLEPIE_CONSTRUCT_IRI)); 2038 } 2039 } 2040 else 2041 { 2042 if ($this->feed_url !== null) 2043 { 2044 return str_replace('&', '&', 2045 $this->sanitize($this->feed_url, 2046 SIMPLEPIE_CONSTRUCT_IRI)); 2047 } 2048 } 2049 return null; 2050 } 2051 2052 /** 2053 * Get data for an feed-level element 2054 * 2055 * This method allows you to get access to ANY element/attribute that is a 2056 * sub-element of the opening feed tag. 2057 * 2058 * The return value is an indexed array of elements matching the given 2059 * namespace and tag name. Each element has `attribs`, `data` and `child` 2060 * subkeys. For `attribs` and `child`, these contain namespace subkeys. 2061 * `attribs` then has one level of associative name => value data (where 2062 * `value` is a string) after the namespace. `child` has tag-indexed keys 2063 * after the namespace, each member of which is an indexed array matching 2064 * this same format. 2065 * 2066 * For example: 2067 * <pre> 2068 * // This is probably a bad example because we already support 2069 * // <media:content> natively, but it shows you how to parse through 2070 * // the nodes. 2071 * $group = $item->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'group'); 2072 * $content = $group[0]['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content']; 2073 * $file = $content[0]['attribs']['']['url']; 2074 * echo $file; 2075 * </pre> 2076 * 2077 * @since 1.0 2078 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces 2079 * @param string $namespace The URL of the XML namespace of the elements you're trying to access 2080 * @param string $tag Tag name 2081 * @return array 2082 */ 2083 public function get_feed_tags($namespace, $tag) 2084 { 2085 $type = $this->get_type(); 2086 if ($type & SIMPLEPIE_TYPE_ATOM_10) 2087 { 2088 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag])) 2089 { 2090 return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag]; 2091 } 2092 } 2093 if ($type & SIMPLEPIE_TYPE_ATOM_03) 2094 { 2095 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag])) 2096 { 2097 return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag]; 2098 } 2099 } 2100 if ($type & SIMPLEPIE_TYPE_RSS_RDF) 2101 { 2102 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag])) 2103 { 2104 return $this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag]; 2105 } 2106 } 2107 if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION) 2108 { 2109 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag])) 2110 { 2111 return $this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag]; 2112 } 2113 } 2114 return null; 2115 } 2116 2117 /** 2118 * Get data for an channel-level element 2119 * 2120 * This method allows you to get access to ANY element/attribute in the 2121 * channel/header section of the feed. 2122 * 2123 * See {@see SimplePie::get_feed_tags()} for a description of the return value 2124 * 2125 * @since 1.0 2126 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces 2127 * @param string $namespace The URL of the XML namespace of the elements you're trying to access 2128 * @param string $tag Tag name 2129 * @return array 2130 */ 2131 public function get_channel_tags($namespace, $tag) 2132 { 2133 $type = $this->get_type(); 2134 if ($type & SIMPLEPIE_TYPE_ATOM_ALL) 2135 { 2136 if ($return = $this->get_feed_tags($namespace, $tag)) 2137 { 2138 return $return; 2139 } 2140 } 2141 if ($type & SIMPLEPIE_TYPE_RSS_10) 2142 { 2143 if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'channel')) 2144 { 2145 if (isset($channel[0]['child'][$namespace][$tag])) 2146 { 2147 return $channel[0]['child'][$namespace][$tag]; 2148 } 2149 } 2150 } 2151 if ($type & SIMPLEPIE_TYPE_RSS_090) 2152 { 2153 if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'channel')) 2154 { 2155 if (isset($channel[0]['child'][$namespace][$tag])) 2156 { 2157 return $channel[0]['child'][$namespace][$tag]; 2158 } 2159 } 2160 } 2161 if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION) 2162 { 2163 if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'channel')) 2164 { 2165 if (isset($channel[0]['child'][$namespace][$tag])) 2166 { 2167 return $channel[0]['child'][$namespace][$tag]; 2168 } 2169 } 2170 } 2171 return null; 2172 } 2173 2174 /** 2175 * Get data for an channel-level element 2176 * 2177 * This method allows you to get access to ANY element/attribute in the 2178 * image/logo section of the feed. 2179 * 2180 * See {@see SimplePie::get_feed_tags()} for a description of the return value 2181 * 2182 * @since 1.0 2183 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces 2184 * @param string $namespace The URL of the XML namespace of the elements you're trying to access 2185 * @param string $tag Tag name 2186 * @return array 2187 */ 2188 public function get_image_tags($namespace, $tag) 2189 { 2190 $type = $this->get_type(); 2191 if ($type & SIMPLEPIE_TYPE_RSS_10) 2192 { 2193 if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'image')) 2194 { 2195 if (isset($image[0]['child'][$namespace][$tag])) 2196 { 2197 return $image[0]['child'][$namespace][$tag]; 2198 } 2199 } 2200 } 2201 if ($type & SIMPLEPIE_TYPE_RSS_090) 2202 { 2203 if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'image')) 2204 { 2205 if (isset($image[0]['child'][$namespace][$tag])) 2206 { 2207 return $image[0]['child'][$namespace][$tag]; 2208 } 2209 } 2210 } 2211 if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION) 2212 { 2213 if ($image = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'image')) 2214 { 2215 if (isset($image[0]['child'][$namespace][$tag])) 2216 { 2217 return $image[0]['child'][$namespace][$tag]; 2218 } 2219 } 2220 } 2221 return null; 2222 } 2223 2224 /** 2225 * Get the base URL value from the feed 2226 * 2227 * Uses `<xml:base>` if available, otherwise uses the first link in the 2228 * feed, or failing that, the URL of the feed itself. 2229 * 2230 * @see get_link 2231 * @see subscribe_url 2232 * 2233 * @param array $element 2234 * @return string 2235 */ 2236 public function get_base($element = array()) 2237 { 2238 if (!($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION) && !empty($element['xml_base_explicit']) && isset($element['xml_base'])) 2239 { 2240 return $element['xml_base']; 2241 } 2242 elseif ($this->get_link() !== null) 2243 { 2244 return $this->get_link(); 2245 } 2246 2247 return $this->subscribe_url(); 2248 } 2249 2250 /** 2251 * Sanitize feed data 2252 * 2253 * @access private 2254 * @see SimplePie_Sanitize::sanitize() 2255 * @param string $data Data to sanitize 2256 * @param int $type One of the SIMPLEPIE_CONSTRUCT_* constants 2257 * @param string $base Base URL to resolve URLs against 2258 * @return string Sanitized data 2259 */ 2260 public function sanitize($data, $type, $base = '') 2261 { 2262 try 2263 { 2264 return $this->sanitize->sanitize($data, $type, $base); 2265 } 2266 catch (SimplePie_Exception $e) 2267 { 2268 if (!$this->enable_exceptions) 2269 { 2270 $this->error = $e->getMessage(); 2271 $this->registry->call('Misc', 'error', array($this->error, E_USER_WARNING, $e->getFile(), $e->getLine())); 2272 return ''; 2273 } 2274 2275 throw $e; 2276 } 2277 } 2278 2279 /** 2280 * Get the title of the feed 2281 * 2282 * Uses `<atom:title>`, `<title>` or `<dc:title>` 2283 * 2284 * @since 1.0 (previously called `get_feed_title` since 0.8) 2285 * @return string|null 2286 */ 2287 public function get_title() 2288 { 2289 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title')) 2290 { 2291 return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); 2292 } 2293 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title')) 2294 { 2295 return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); 2296 } 2297 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) 2298 { 2299 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); 2300 } 2301 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) 2302 { 2303 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); 2304 } 2305 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title')) 2306 { 2307 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); 2308 } 2309 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) 2310 { 2311 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); 2312 } 2313 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) 2314 { 2315 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); 2316 } 2317 2318 return null; 2319 } 2320 2321 /** 2322 * Get a category for the feed 2323 * 2324 * @since Unknown 2325 * @param int $key The category that you want to return. Remember that arrays begin with 0, not 1 2326 * @return SimplePie_Category|null 2327 */ 2328 public function get_category($key = 0) 2329 { 2330 $categories = $this->get_categories(); 2331 if (isset($categories[$key])) 2332 { 2333 return $categories[$key]; 2334 } 2335 2336 return null; 2337 } 2338 2339 /** 2340 * Get all categories for the feed 2341 * 2342 * Uses `<atom:category>`, `<category>` or `<dc:subject>` 2343 * 2344 * @since Unknown 2345 * @return array|null List of {@see SimplePie_Category} objects 2346 */ 2347 public function get_categories() 2348 { 2349 $categories = array(); 2350 2351 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category) 2352 { 2353 $term = null; 2354 $scheme = null; 2355 $label = null; 2356 if (isset($category['attribs']['']['term'])) 2357 { 2358 $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT); 2359 } 2360 if (isset($category['attribs']['']['scheme'])) 2361 { 2362 $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); 2363 } 2364 if (isset($category['attribs']['']['label'])) 2365 { 2366 $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); 2367 } 2368 $categories[] = $this->registry->create('Category', array($term, $scheme, $label)); 2369 } 2370 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category) 2371 { 2372 // This is really the label, but keep this as the term also for BC. 2373 // Label will also work on retrieving because that falls back to term. 2374 $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); 2375 if (isset($category['attribs']['']['domain'])) 2376 { 2377 $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT); 2378 } 2379 else 2380 { 2381 $scheme = null; 2382 } 2383 $categories[] = $this->registry->create('Category', array($term, $scheme, null)); 2384 } 2385 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) 2386 { 2387 $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); 2388 } 2389 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) 2390 { 2391 $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); 2392 } 2393 2394 if (!empty($categories)) 2395 { 2396 return array_unique($categories); 2397 } 2398 2399 return null; 2400 } 2401 2402 /** 2403 * Get an author for the feed 2404 * 2405 * @since 1.1 2406 * @param int $key The author that you want to return. Remember that arrays begin with 0, not 1 2407 * @return SimplePie_Author|null 2408 */ 2409 public function get_author($key = 0) 2410 { 2411 $authors = $this->get_authors(); 2412 if (isset($authors[$key])) 2413 { 2414 return $authors[$key]; 2415 } 2416 2417 return null; 2418 } 2419 2420 /** 2421 * Get all authors for the feed 2422 * 2423 * Uses `<atom:author>`, `<author>`, `<dc:creator>` or `<itunes:author>` 2424 * 2425 * @since 1.1 2426 * @return array|null List of {@see SimplePie_Author} objects 2427 */ 2428 public function get_authors() 2429 { 2430 $authors = array(); 2431 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author) 2432 { 2433 $name = null; 2434 $uri = null; 2435 $email = null; 2436 if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) 2437 { 2438 $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); 2439 } 2440 if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) 2441 { 2442 $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); 2443 } 2444 if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) 2445 { 2446 $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); 2447 } 2448 if ($name !== null || $email !== null || $uri !== null) 2449 { 2450 $authors[] = $this->registry->create('Author', array($name, $uri, $email)); 2451 } 2452 } 2453 if ($author = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author')) 2454 { 2455 $name = null; 2456 $url = null; 2457 $email = null; 2458 if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) 2459 { 2460 $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); 2461 } 2462 if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) 2463 { 2464 $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); 2465 } 2466 if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) 2467 { 2468 $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); 2469 } 2470 if ($name !== null || $email !== null || $url !== null) 2471 { 2472 $authors[] = $this->registry->create('Author', array($name, $url, $email)); 2473 } 2474 } 2475 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) 2476 { 2477 $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); 2478 } 2479 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) 2480 { 2481 $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); 2482 } 2483 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) 2484 { 2485 $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); 2486 } 2487 2488 if (!empty($authors)) 2489 { 2490 return array_unique($authors); 2491 } 2492 2493 return null; 2494 } 2495 2496 /** 2497 * Get a contributor for the feed 2498 * 2499 * @since 1.1 2500 * @param int $key The contrbutor that you want to return. Remember that arrays begin with 0, not 1 2501 * @return SimplePie_Author|null 2502 */ 2503 public function get_contributor($key = 0) 2504 { 2505 $contributors = $this->get_contributors(); 2506 if (isset($contributors[$key])) 2507 { 2508 return $contributors[$key]; 2509 } 2510 2511 return null; 2512 } 2513 2514 /** 2515 * Get all contributors for the feed 2516 * 2517 * Uses `<atom:contributor>` 2518 * 2519 * @since 1.1 2520 * @return array|null List of {@see SimplePie_Author} objects 2521 */ 2522 public function get_contributors() 2523 { 2524 $contributors = array(); 2525 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor) 2526 { 2527 $name = null; 2528 $uri = null; 2529 $email = null; 2530 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) 2531 { 2532 $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); 2533 } 2534 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) 2535 { 2536 $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); 2537 } 2538 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) 2539 { 2540 $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); 2541 } 2542 if ($name !== null || $email !== null || $uri !== null) 2543 { 2544 $contributors[] = $this->registry->create('Author', array($name, $uri, $email)); 2545 } 2546 } 2547 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor) 2548 { 2549 $name = null; 2550 $url = null; 2551 $email = null; 2552 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) 2553 { 2554 $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); 2555 } 2556 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) 2557 { 2558 $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); 2559 } 2560 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) 2561 { 2562 $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); 2563 } 2564 if ($name !== null || $email !== null || $url !== null) 2565 { 2566 $contributors[] = $this->registry->create('Author', array($name, $url, $email)); 2567 } 2568 } 2569 2570 if (!empty($contributors)) 2571 { 2572 return array_unique($contributors); 2573 } 2574 2575 return null; 2576 } 2577 2578 /** 2579 * Get a single link for the feed 2580 * 2581 * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8) 2582 * @param int $key The link that you want to return. Remember that arrays begin with 0, not 1 2583 * @param string $rel The relationship of the link to return 2584 * @return string|null Link URL 2585 */ 2586 public function get_link($key = 0, $rel = 'alternate') 2587 { 2588 $links = $this->get_links($rel); 2589 if (isset($links[$key])) 2590 { 2591 return $links[$key]; 2592 } 2593 2594 return null; 2595 } 2596 2597 /** 2598 * Get the permalink for the item 2599 * 2600 * Returns the first link available with a relationship of "alternate". 2601 * Identical to {@see get_link()} with key 0 2602 * 2603 * @see get_link 2604 * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8) 2605 * @internal Added for parity between the parent-level and the item/entry-level. 2606 * @return string|null Link URL 2607 */ 2608 public function get_permalink() 2609 { 2610 return $this->get_link(0); 2611 } 2612 2613 /** 2614 * Get all links for the feed 2615 * 2616 * Uses `<atom:link>` or `<link>` 2617 * 2618 * @since Beta 2 2619 * @param string $rel The relationship of links to return 2620 * @return array|null Links found for the feed (strings) 2621 */ 2622 public function get_links($rel = 'alternate') 2623 { 2624 if (!isset($this->data['links'])) 2625 { 2626 $this->data['links'] = array(); 2627 if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link')) 2628 { 2629 foreach ($links as $link) 2630 { 2631 if (isset($link['attribs']['']['href'])) 2632 { 2633 $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; 2634 $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); 2635 } 2636 } 2637 } 2638 if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link')) 2639 { 2640 foreach ($links as $link) 2641 { 2642 if (isset($link['attribs']['']['href'])) 2643 { 2644 $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; 2645 $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); 2646 2647 } 2648 } 2649 } 2650 if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) 2651 { 2652 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); 2653 } 2654 if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) 2655 { 2656 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); 2657 } 2658 if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link')) 2659 { 2660 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); 2661 } 2662 2663 $keys = array_keys($this->data['links']); 2664 foreach ($keys as $key) 2665 { 2666 if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key))) 2667 { 2668 if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key])) 2669 { 2670 $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]); 2671 $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]; 2672 } 2673 else 2674 { 2675 $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key]; 2676 } 2677 } 2678 elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY) 2679 { 2680 $this->data['links'][substr($key, 41)] =& $this->data['links'][$key]; 2681 } 2682 $this->data['links'][$key] = array_unique($this->data['links'][$key]); 2683 } 2684 } 2685 2686 if (isset($this->data['headers']['link'])) 2687 { 2688 $link_headers = $this->data['headers']['link']; 2689 if (is_string($link_headers)) { 2690 $link_headers = array($link_headers); 2691 } 2692 $matches = preg_filter('/<([^>]+)>; rel='.preg_quote($rel).'/', '$1', $link_headers); 2693 if (!empty($matches)) { 2694 return $matches; 2695 } 2696 } 2697 2698 if (isset($this->data['links'][$rel])) 2699 { 2700 return $this->data['links'][$rel]; 2701 } 2702 2703 return null; 2704 } 2705 2706 public function get_all_discovered_feeds() 2707 { 2708 return $this->all_discovered_feeds; 2709 } 2710 2711 /** 2712 * Get the content for the item 2713 * 2714 * Uses `<atom:subtitle>`, `<atom:tagline>`, `<description>`, 2715 * `<dc:description>`, `<itunes:summary>` or `<itunes:subtitle>` 2716 * 2717 * @since 1.0 (previously called `get_feed_description()` since 0.8) 2718 * @return string|null 2719 */ 2720 public function get_description() 2721 { 2722 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle')) 2723 { 2724 return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); 2725 } 2726 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline')) 2727 { 2728 return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); 2729 } 2730 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) 2731 { 2732 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); 2733 } 2734 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description')) 2735 { 2736 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); 2737 } 2738 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description')) 2739 { 2740 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); 2741 } 2742 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) 2743 { 2744 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); 2745 } 2746 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) 2747 { 2748 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); 2749 } 2750 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) 2751 { 2752 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); 2753 } 2754 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) 2755 { 2756 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); 2757 } 2758 2759 return null; 2760 } 2761 2762 /** 2763 * Get the copyright info for the feed 2764 * 2765 * Uses `<atom:rights>`, `<atom:copyright>` or `<dc:rights>` 2766 * 2767 * @since 1.0 (previously called `get_feed_copyright()` since 0.8) 2768 * @return string|null 2769 */ 2770 public function get_copyright() 2771 { 2772 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights')) 2773 { 2774 return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); 2775 } 2776 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright')) 2777 { 2778 return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); 2779 } 2780 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'copyright')) 2781 { 2782 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); 2783 } 2784 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights')) 2785 { 2786 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); 2787 } 2788 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights')) 2789 { 2790 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); 2791 } 2792 2793 return null; 2794 } 2795 2796 /** 2797 * Get the language for the feed 2798 * 2799 * Uses `<language>`, `<dc:language>`, or @xml_lang 2800 * 2801 * @since 1.0 (previously called `get_feed_language()` since 0.8) 2802 * @return string|null 2803 */ 2804 public function get_language() 2805 { 2806 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'language')) 2807 { 2808 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); 2809 } 2810 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language')) 2811 { 2812 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); 2813 } 2814 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language')) 2815 { 2816 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); 2817 } 2818 elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang'])) 2819 { 2820 return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); 2821 } 2822 elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang'])) 2823 { 2824 return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); 2825 } 2826 elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang'])) 2827 { 2828 return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); 2829 } 2830 elseif (isset($this->data['headers']['content-language'])) 2831 { 2832 return $this->sanitize($this->data['headers']['content-language'], SIMPLEPIE_CONSTRUCT_TEXT); 2833 } 2834 2835 return null; 2836 } 2837 2838 /** 2839 * Get the latitude coordinates for the item 2840 * 2841 * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications 2842 * 2843 * Uses `<geo:lat>` or `<georss:point>` 2844 * 2845 * @since 1.0 2846 * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo 2847 * @link http://www.georss.org/ GeoRSS 2848 * @return string|null 2849 */ 2850 public function get_latitude() 2851 { 2852 2853 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat')) 2854 { 2855 return (float) $return[0]['data']; 2856 } 2857 elseif (($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) 2858 { 2859 return (float) $match[1]; 2860 } 2861 2862 return null; 2863 } 2864 2865 /** 2866 * Get the longitude coordinates for the feed 2867 * 2868 * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications 2869 * 2870 * Uses `<geo:long>`, `<geo:lon>` or `<georss:point>` 2871 * 2872 * @since 1.0 2873 * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo 2874 * @link http://www.georss.org/ GeoRSS 2875 * @return string|null 2876 */ 2877 public function get_longitude() 2878 { 2879 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long')) 2880 { 2881 return (float) $return[0]['data']; 2882 } 2883 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon')) 2884 { 2885 return (float) $return[0]['data']; 2886 } 2887 elseif (($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) 2888 { 2889 return (float) $match[2]; 2890 } 2891 2892 return null; 2893 } 2894 2895 /** 2896 * Get the feed logo's title 2897 * 2898 * RSS 0.9.0, 1.0 and 2.0 feeds are allowed to have a "feed logo" title. 2899 * 2900 * Uses `<image><title>` or `<image><dc:title>` 2901 * 2902 * @return string|null 2903 */ 2904 public function get_image_title() 2905 { 2906 if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) 2907 { 2908 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); 2909 } 2910 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) 2911 { 2912 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); 2913 } 2914 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title')) 2915 { 2916 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); 2917 } 2918 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) 2919 { 2920 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); 2921 } 2922 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) 2923 { 2924 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); 2925 } 2926 2927 return null; 2928 } 2929 2930 /** 2931 * Get the feed logo's URL 2932 * 2933 * RSS 0.9.0, 2.0, Atom 1.0, and feeds with iTunes RSS tags are allowed to 2934 * have a "feed logo" URL. This points directly to the image itself. 2935 * 2936 * Uses `<itunes:image>`, `<atom:logo>`, `<atom:icon>`, 2937 * `<image><title>` or `<image><dc:title>` 2938 * 2939 * @return string|null 2940 */ 2941 public function get_image_url() 2942 { 2943 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image')) 2944 { 2945 return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI); 2946 } 2947 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo')) 2948 { 2949 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); 2950 } 2951 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon')) 2952 { 2953 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); 2954 } 2955 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'url')) 2956 { 2957 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); 2958 } 2959 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'url')) 2960 { 2961 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); 2962 } 2963 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url')) 2964 { 2965 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); 2966 } 2967 2968 return null; 2969 } 2970 2971 2972 /**<