PageRenderTime 42ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/wp-includes/SimplePie/Cache/MySQL.php

https://bitbucket.org/abnopanda/wordpress
PHP | 438 lines | 301 code | 34 blank | 103 comment | 41 complexity | e8911ece15df42ca43991a48d5785687 MD5 | raw file
  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. * 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. * Options
  66. *
  67. * @var array
  68. */
  69. protected $options;
  70. /**
  71. * Cache ID
  72. *
  73. * @var string
  74. */
  75. protected $id;
  76. /**
  77. * Create a new cache object
  78. *
  79. * @param string $location Location string (from SimplePie::$cache_location)
  80. * @param string $name Unique ID for the cache
  81. * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
  82. */
  83. public function __construct($location, $name, $type)
  84. {
  85. $this->options = array(
  86. 'user' => null,
  87. 'pass' => null,
  88. 'host' => '127.0.0.1',
  89. 'port' => '3306',
  90. 'path' => '',
  91. 'extras' => array(
  92. 'prefix' => '',
  93. ),
  94. );
  95. $this->options = array_merge_recursive($this->options, SimplePie_Cache::parse_URL($location));
  96. // Path is prefixed with a "/"
  97. $this->options['dbname'] = substr($this->options['path'], 1);
  98. try
  99. {
  100. $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'));
  101. }
  102. catch (PDOException $e)
  103. {
  104. $this->mysql = null;
  105. return;
  106. }
  107. $this->id = $name . $type;
  108. if (!$query = $this->mysql->query('SHOW TABLES'))
  109. {
  110. $this->mysql = null;
  111. return;
  112. }
  113. $db = array();
  114. while ($row = $query->fetchColumn())
  115. {
  116. $db[] = $row;
  117. }
  118. if (!in_array($this->options['extras']['prefix'] . 'cache_data', $db))
  119. {
  120. $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)))');
  121. if ($query === false)
  122. {
  123. $this->mysql = null;
  124. }
  125. }
  126. if (!in_array($this->options['extras']['prefix'] . 'items', $db))
  127. {
  128. $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)))');
  129. if ($query === false)
  130. {
  131. $this->mysql = null;
  132. }
  133. }
  134. }
  135. /**
  136. * Save data to the cache
  137. *
  138. * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
  139. * @return bool Successfulness
  140. */
  141. public function save($data)
  142. {
  143. if ($this->mysql === null)
  144. {
  145. return false;
  146. }
  147. if ($data instanceof SimplePie)
  148. {
  149. $data = clone $data;
  150. $prepared = self::prepare_simplepie_object_for_cache($data);
  151. $query = $this->mysql->prepare('SELECT COUNT(*) FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed');
  152. $query->bindValue(':feed', $this->id);
  153. if ($query->execute())
  154. {
  155. if ($query->fetchColumn() > 0)
  156. {
  157. $items = count($prepared[1]);
  158. if ($items)
  159. {
  160. $sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = :items, `data` = :data, `mtime` = :time WHERE `id` = :feed';
  161. $query = $this->mysql->prepare($sql);
  162. $query->bindValue(':items', $items);
  163. }
  164. else
  165. {
  166. $sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `data` = :data, `mtime` = :time WHERE `id` = :feed';
  167. $query = $this->mysql->prepare($sql);
  168. }
  169. $query->bindValue(':data', $prepared[0]);
  170. $query->bindValue(':time', time());
  171. $query->bindValue(':feed', $this->id);
  172. if (!$query->execute())
  173. {
  174. return false;
  175. }
  176. }
  177. else
  178. {
  179. $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:feed, :count, :data, :time)');
  180. $query->bindValue(':feed', $this->id);
  181. $query->bindValue(':count', count($prepared[1]));
  182. $query->bindValue(':data', $prepared[0]);
  183. $query->bindValue(':time', time());
  184. if (!$query->execute())
  185. {
  186. return false;
  187. }
  188. }
  189. $ids = array_keys($prepared[1]);
  190. if (!empty($ids))
  191. {
  192. foreach ($ids as $id)
  193. {
  194. $database_ids[] = $this->mysql->quote($id);
  195. }
  196. $query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `id` = ' . implode(' OR `id` = ', $database_ids) . ' AND `feed_id` = :feed');
  197. $query->bindValue(':feed', $this->id);
  198. if ($query->execute())
  199. {
  200. $existing_ids = array();
  201. while ($row = $query->fetchColumn())
  202. {
  203. $existing_ids[] = $row;
  204. }
  205. $new_ids = array_diff($ids, $existing_ids);
  206. foreach ($new_ids as $new_id)
  207. {
  208. if (!($date = $prepared[1][$new_id]->get_date('U')))
  209. {
  210. $date = time();
  211. }
  212. $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'items` (`feed_id`, `id`, `data`, `posted`) VALUES(:feed, :id, :data, :date)');
  213. $query->bindValue(':feed', $this->id);
  214. $query->bindValue(':id', $new_id);
  215. $query->bindValue(':data', serialize($prepared[1][$new_id]->data));
  216. $query->bindValue(':date', $date);
  217. if (!$query->execute())
  218. {
  219. return false;
  220. }
  221. }
  222. return true;
  223. }
  224. }
  225. else
  226. {
  227. return true;
  228. }
  229. }
  230. }
  231. else
  232. {
  233. $query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed');
  234. $query->bindValue(':feed', $this->id);
  235. if ($query->execute())
  236. {
  237. if ($query->rowCount() > 0)
  238. {
  239. $query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = 0, `data` = :data, `mtime` = :time WHERE `id` = :feed');
  240. $query->bindValue(':data', serialize($data));
  241. $query->bindValue(':time', time());
  242. $query->bindValue(':feed', $this->id);
  243. if ($this->execute())
  244. {
  245. return true;
  246. }
  247. }
  248. else
  249. {
  250. $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:id, 0, :data, :time)');
  251. $query->bindValue(':id', $this->id);
  252. $query->bindValue(':data', serialize($data));
  253. $query->bindValue(':time', time());
  254. if ($query->execute())
  255. {
  256. return true;
  257. }
  258. }
  259. }
  260. }
  261. return false;
  262. }
  263. /**
  264. * Retrieve the data saved to the cache
  265. *
  266. * @return array Data for SimplePie::$data
  267. */
  268. public function load()
  269. {
  270. if ($this->mysql === null)
  271. {
  272. return false;
  273. }
  274. $query = $this->mysql->prepare('SELECT `items`, `data` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
  275. $query->bindValue(':id', $this->id);
  276. if ($query->execute() && ($row = $query->fetch()))
  277. {
  278. $data = unserialize($row[1]);
  279. if (isset($this->options['items'][0]))
  280. {
  281. $items = (int) $this->options['items'][0];
  282. }
  283. else
  284. {
  285. $items = (int) $row[0];
  286. }
  287. if ($items !== 0)
  288. {
  289. if (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]))
  290. {
  291. $feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0];
  292. }
  293. elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]))
  294. {
  295. $feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0];
  296. }
  297. elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]))
  298. {
  299. $feed =& $data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0];
  300. }
  301. elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]))
  302. {
  303. $feed =& $data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0];
  304. }
  305. else
  306. {
  307. $feed = null;
  308. }
  309. if ($feed !== null)
  310. {
  311. $sql = 'SELECT `data` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :feed ORDER BY `posted` DESC';
  312. if ($items > 0)
  313. {
  314. $sql .= ' LIMIT ' . $items;
  315. }
  316. $query = $this->mysql->prepare($sql);
  317. $query->bindValue(':feed', $this->id);
  318. if ($query->execute())
  319. {
  320. while ($row = $query->fetchColumn())
  321. {
  322. $feed['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry'][] = unserialize($row);
  323. }
  324. }
  325. else
  326. {
  327. return false;
  328. }
  329. }
  330. }
  331. return $data;
  332. }
  333. return false;
  334. }
  335. /**
  336. * Retrieve the last modified time for the cache
  337. *
  338. * @return int Timestamp
  339. */
  340. public function mtime()
  341. {
  342. if ($this->mysql === null)
  343. {
  344. return false;
  345. }
  346. $query = $this->mysql->prepare('SELECT `mtime` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
  347. $query->bindValue(':id', $this->id);
  348. if ($query->execute() && ($time = $query->fetchColumn()))
  349. {
  350. return $time;
  351. }
  352. else
  353. {
  354. return false;
  355. }
  356. }
  357. /**
  358. * Set the last modified time to the current time
  359. *
  360. * @return bool Success status
  361. */
  362. public function touch()
  363. {
  364. if ($this->mysql === null)
  365. {
  366. return false;
  367. }
  368. $query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `mtime` = :time WHERE `id` = :id');
  369. $query->bindValue(':time', time());
  370. $query->bindValue(':id', $this->id);
  371. if ($query->execute() && $query->rowCount() > 0)
  372. {
  373. return true;
  374. }
  375. else
  376. {
  377. return false;
  378. }
  379. }
  380. /**
  381. * Remove the cache
  382. *
  383. * @return bool Success status
  384. */
  385. public function unlink()
  386. {
  387. if ($this->mysql === null)
  388. {
  389. return false;
  390. }
  391. $query = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
  392. $query->bindValue(':id', $this->id);
  393. $query2 = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :id');
  394. $query2->bindValue(':id', $this->id);
  395. if ($query->execute() && $query2->execute())
  396. {
  397. return true;
  398. }
  399. else
  400. {
  401. return false;
  402. }
  403. }
  404. }