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