1 <?php 2 3 declare(strict_types=1); 4 /** 5 * SimplePie 6 * 7 * A PHP-Based RSS and Atom Feed Framework. 8 * Takes the hard work out of managing a complete RSS/Atom solution. 9 * 10 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors 11 * All rights reserved. 12 * 13 * Redistribution and use in source and binary forms, with or without modification, are 14 * permitted provided that the following conditions are met: 15 * 16 * * Redistributions of source code must retain the above copyright notice, this list of 17 * conditions and the following disclaimer. 18 * 19 * * Redistributions in binary form must reproduce the above copyright notice, this list 20 * of conditions and the following disclaimer in the documentation and/or other materials 21 * provided with the distribution. 22 * 23 * * Neither the name of the SimplePie Team nor the names of its contributors may be used 24 * to endorse or promote products derived from this software without specific prior 25 * written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS 28 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 29 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS 30 * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 32 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 * 37 * @package SimplePie 38 * @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue 39 * @author Ryan Parman 40 * @author Sam Sneddon 41 * @author Ryan McCue 42 * @link http://simplepie.org/ SimplePie 43 * @license http://www.opensource.org/licenses/bsd-license.php BSD License 44 */ 45 46 namespace SimplePie\Cache; 47 48 /** 49 * Caches data to a MySQL database 50 * 51 * Registered for URLs with the "mysql" protocol 52 * 53 * For example, `mysql://root:password@localhost:3306/mydb?prefix=sp_` will 54 * connect to the `mydb` database on `localhost` on port 3306, with the user 55 * `root` and the password `password`. All tables will be prefixed with `sp_` 56 * 57 * @package SimplePie 58 * @subpackage Caching 59 * @deprecated since SimplePie 1.8.0, use implementation of "Psr\SimpleCache\CacheInterface" instead 60 */ 61 class MySQL extends DB 62 { 63 /** 64 * PDO instance 65 * 66 * @var \PDO 67 */ 68 protected $mysql; 69 70 /** 71 * Options 72 * 73 * @var array 74 */ 75 protected $options; 76 77 /** 78 * Cache ID 79 * 80 * @var string 81 */ 82 protected $id; 83 84 /** 85 * Create a new cache object 86 * 87 * @param string $location Location string (from SimplePie::$cache_location) 88 * @param string $name Unique ID for the cache 89 * @param Base::TYPE_FEED|Base::TYPE_IMAGE $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data 90 */ 91 public function __construct($location, $name, $type) 92 { 93 $this->options = [ 94 'user' => null, 95 'pass' => null, 96 'host' => '127.0.0.1', 97 'port' => '3306', 98 'path' => '', 99 'extras' => [ 100 'prefix' => '', 101 'cache_purge_time' => 2592000 102 ], 103 ]; 104 105 $this->options = array_replace_recursive($this->options, \SimplePie\Cache::parse_URL($location)); 106 107 // Path is prefixed with a "/" 108 $this->options['dbname'] = substr($this->options['path'], 1); 109 110 try { 111 $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']); 112 } catch (\PDOException $e) { 113 $this->mysql = null; 114 return; 115 } 116 117 $this->id = $name . $type; 118 119 if (!$query = $this->mysql->query('SHOW TABLES')) { 120 $this->mysql = null; 121 return; 122 } 123 124 $db = []; 125 while ($row = $query->fetchColumn()) { 126 $db[] = $row; 127 } 128 129 if (!in_array($this->options['extras']['prefix'] . 'cache_data', $db)) { 130 $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)))'); 131 if ($query === false) { 132 trigger_error("Can't create " . $this->options['extras']['prefix'] . "cache_data table, check permissions", \E_USER_WARNING); 133 $this->mysql = null; 134 return; 135 } 136 } 137 138 if (!in_array($this->options['extras']['prefix'] . 'items', $db)) { 139 $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)))'); 140 if ($query === false) { 141 trigger_error("Can't create " . $this->options['extras']['prefix'] . "items table, check permissions", \E_USER_WARNING); 142 $this->mysql = null; 143 return; 144 } 145 } 146 } 147 148 /** 149 * Save data to the cache 150 * 151 * @param array|\SimplePie\SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property 152 * @return bool Successfulness 153 */ 154 public function save($data) 155 { 156 if ($this->mysql === null) { 157 return false; 158 } 159 160 $query = $this->mysql->prepare('DELETE i, cd FROM `' . $this->options['extras']['prefix'] . 'cache_data` cd, ' . 161 '`' . $this->options['extras']['prefix'] . 'items` i ' . 162 'WHERE cd.id = i.feed_id ' . 163 'AND cd.mtime < (unix_timestamp() - :purge_time)'); 164 $query->bindValue(':purge_time', $this->options['extras']['cache_purge_time']); 165 166 if (!$query->execute()) { 167 return false; 168 } 169 170 if ($data instanceof \SimplePie\SimplePie) { 171 $data = clone $data; 172 173 $prepared = self::prepare_simplepie_object_for_cache($data); 174 175 $query = $this->mysql->prepare('SELECT COUNT(*) FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed'); 176 $query->bindValue(':feed', $this->id); 177 if ($query->execute()) { 178 if ($query->fetchColumn() > 0) { 179 $items = count($prepared[1]); 180 if ($items) { 181 $sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = :items, `data` = :data, `mtime` = :time WHERE `id` = :feed'; 182 $query = $this->mysql->prepare($sql); 183 $query->bindValue(':items', $items); 184 } else { 185 $sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `data` = :data, `mtime` = :time WHERE `id` = :feed'; 186 $query = $this->mysql->prepare($sql); 187 } 188 189 $query->bindValue(':data', $prepared[0]); 190 $query->bindValue(':time', time()); 191 $query->bindValue(':feed', $this->id); 192 if (!$query->execute()) { 193 return false; 194 } 195 } else { 196 $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:feed, :count, :data, :time)'); 197 $query->bindValue(':feed', $this->id); 198 $query->bindValue(':count', count($prepared[1])); 199 $query->bindValue(':data', $prepared[0]); 200 $query->bindValue(':time', time()); 201 if (!$query->execute()) { 202 return false; 203 } 204 } 205 206 $ids = array_keys($prepared[1]); 207 if (!empty($ids)) { 208 foreach ($ids as $id) { 209 $database_ids[] = $this->mysql->quote($id); 210 } 211 212 $query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `id` = ' . implode(' OR `id` = ', $database_ids) . ' AND `feed_id` = :feed'); 213 $query->bindValue(':feed', $this->id); 214 215 if ($query->execute()) { 216 $existing_ids = []; 217 while ($row = $query->fetchColumn()) { 218 $existing_ids[] = $row; 219 } 220 221 $new_ids = array_diff($ids, $existing_ids); 222 223 foreach ($new_ids as $new_id) { 224 if (!($date = $prepared[1][$new_id]->get_date('U'))) { 225 $date = time(); 226 } 227 228 $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'items` (`feed_id`, `id`, `data`, `posted`) VALUES(:feed, :id, :data, :date)'); 229 $query->bindValue(':feed', $this->id); 230 $query->bindValue(':id', $new_id); 231 $query->bindValue(':data', serialize($prepared[1][$new_id]->data)); 232 $query->bindValue(':date', $date); 233 if (!$query->execute()) { 234 return false; 235 } 236 } 237 return true; 238 } 239 } else { 240 return true; 241 } 242 } 243 } else { 244 $query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed'); 245 $query->bindValue(':feed', $this->id); 246 if ($query->execute()) { 247 if ($query->rowCount() > 0) { 248 $query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = 0, `data` = :data, `mtime` = :time WHERE `id` = :feed'); 249 $query->bindValue(':data', serialize($data)); 250 $query->bindValue(':time', time()); 251 $query->bindValue(':feed', $this->id); 252 if ($query->execute()) { 253 return true; 254 } 255 } else { 256 $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:id, 0, :data, :time)'); 257 $query->bindValue(':id', $this->id); 258 $query->bindValue(':data', serialize($data)); 259 $query->bindValue(':time', time()); 260 if ($query->execute()) { 261 return true; 262 } 263 } 264 } 265 } 266 return false; 267 } 268 269 /** 270 * Retrieve the data saved to the cache 271 * 272 * @return array Data for SimplePie::$data 273 */ 274 public function load() 275 { 276 if ($this->mysql === null) { 277 return false; 278 } 279 280 $query = $this->mysql->prepare('SELECT `items`, `data` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id'); 281 $query->bindValue(':id', $this->id); 282 if ($query->execute() && ($row = $query->fetch())) { 283 $data = unserialize($row[1]); 284 285 if (isset($this->options['items'][0])) { 286 $items = (int) $this->options['items'][0]; 287 } else { 288 $items = (int) $row[0]; 289 } 290 291 if ($items !== 0) { 292 if (isset($data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['feed'][0])) { 293 $feed =& $data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['feed'][0]; 294 } elseif (isset($data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['feed'][0])) { 295 $feed =& $data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['feed'][0]; 296 } elseif (isset($data['child'][\SimplePie\SimplePie::NAMESPACE_RDF]['RDF'][0])) { 297 $feed =& $data['child'][\SimplePie\SimplePie::NAMESPACE_RDF]['RDF'][0]; 298 } elseif (isset($data['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['rss'][0])) { 299 $feed =& $data['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['rss'][0]; 300 } else { 301 $feed = null; 302 } 303 304 if ($feed !== null) { 305 $sql = 'SELECT `data` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :feed ORDER BY `posted` DESC'; 306 if ($items > 0) { 307 $sql .= ' LIMIT ' . $items; 308 } 309 310 $query = $this->mysql->prepare($sql); 311 $query->bindValue(':feed', $this->id); 312 if ($query->execute()) { 313 while ($row = $query->fetchColumn()) { 314 $feed['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['entry'][] = unserialize($row); 315 } 316 } else { 317 return false; 318 } 319 } 320 } 321 return $data; 322 } 323 return false; 324 } 325 326 /** 327 * Retrieve the last modified time for the cache 328 * 329 * @return int Timestamp 330 */ 331 public function mtime() 332 { 333 if ($this->mysql === null) { 334 return false; 335 } 336 337 $query = $this->mysql->prepare('SELECT `mtime` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id'); 338 $query->bindValue(':id', $this->id); 339 if ($query->execute() && ($time = $query->fetchColumn())) { 340 return $time; 341 } 342 343 return false; 344 } 345 346 /** 347 * Set the last modified time to the current time 348 * 349 * @return bool Success status 350 */ 351 public function touch() 352 { 353 if ($this->mysql === null) { 354 return false; 355 } 356 357 $query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `mtime` = :time WHERE `id` = :id'); 358 $query->bindValue(':time', time()); 359 $query->bindValue(':id', $this->id); 360 361 return $query->execute() && $query->rowCount() > 0; 362 } 363 364 /** 365 * Remove the cache 366 * 367 * @return bool Success status 368 */ 369 public function unlink() 370 { 371 if ($this->mysql === null) { 372 return false; 373 } 374 375 $query = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id'); 376 $query->bindValue(':id', $this->id); 377 $query2 = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :id'); 378 $query2->bindValue(':id', $this->id); 379 380 return $query->execute() && $query2->execute(); 381 } 382 } 383 384 class_alias('SimplePie\Cache\MySQL', 'SimplePie_Cache_MySQL');
title
Description
Body
title
Description
Body
title
Description
Body
title
Body