angelovcom.net

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

MySQL.php (12840B)


      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-2016, Ryan Parman, Sam 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  * @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
     37  * @author Ryan Parman
     38  * @author Sam Sneddon
     39  * @author Ryan McCue
     40  * @link http://simplepie.org/ SimplePie
     41  * @license http://www.opensource.org/licenses/bsd-license.php BSD License
     42  */
     43 
     44 /**
     45  * Caches data to a MySQL database
     46  *
     47  * Registered for URLs with the "mysql" protocol
     48  *
     49  * For example, `mysql://root:password@localhost:3306/mydb?prefix=sp_` will
     50  * connect to the `mydb` database on `localhost` on port 3306, with the user
     51  * `root` and the password `password`. All tables will be prefixed with `sp_`
     52  *
     53  * @package SimplePie
     54  * @subpackage Caching
     55  */
     56 class SimplePie_Cache_MySQL extends SimplePie_Cache_DB
     57 {
     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 	}
    440 }