wp/wp-includes/SimplePie/src/Cache/MySQL.php
changeset 22 8c2e4d02f4ef
equal deleted inserted replaced
21:48c4eec2b7e6 22:8c2e4d02f4ef
       
     1 <?php
       
     2 
       
     3 /**
       
     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.
       
    35  *
       
    36  * @package SimplePie
       
    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
       
    42  * @license http://www.opensource.org/licenses/bsd-license.php BSD License
       
    43  */
       
    44 
       
    45 namespace SimplePie\Cache;
       
    46 
       
    47 /**
       
    48  * Caches data to a MySQL database
       
    49  *
       
    50  * Registered for URLs with the "mysql" protocol
       
    51  *
       
    52  * For example, `mysql://root:password@localhost:3306/mydb?prefix=sp_` will
       
    53  * connect to the `mydb` database on `localhost` on port 3306, with the user
       
    54  * `root` and the password `password`. All tables will be prefixed with `sp_`
       
    55  *
       
    56  * @package SimplePie
       
    57  * @subpackage Caching
       
    58  * @deprecated since SimplePie 1.8.0, use implementation of "Psr\SimpleCache\CacheInterface" instead
       
    59  */
       
    60 class MySQL extends DB
       
    61 {
       
    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     }
       
   381 }
       
   382 
       
   383 class_alias('SimplePie\Cache\MySQL', 'SimplePie_Cache_MySQL');