db.php 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. <?php
  2. // +----------------------------------------------------------------------+
  3. // | PEAR :: Cache |
  4. // +----------------------------------------------------------------------+
  5. // | Copyright (c) 1997-2003 The PHP Group |
  6. // +----------------------------------------------------------------------+
  7. // | This source file is subject to version 2.0 of the PHP license, |
  8. // | that is bundled with this package in the file LICENSE, and is |
  9. // | available at through the world-wide-web at |
  10. // | http://www.php.net/license/2_02.txt. |
  11. // | If you did not receive a copy of the PHP license and are unable to |
  12. // | obtain it through the world-wide-web, please send a note to |
  13. // | license@php.net so we can mail you a copy immediately. |
  14. // +----------------------------------------------------------------------+
  15. // | Authors: Ulf Wendel <ulf.wendel@phpdoc.de> |
  16. // | Sebastian Bergmann <sb@sebastian-bergmann.de> |
  17. // | Chuck Hagenbuch <chuck@horde.org> |
  18. // +----------------------------------------------------------------------+
  19. //
  20. // $Id: db.php,v 1.5 2003/01/04 11:54:46 mj Exp $
  21. require_once 'DB.php';
  22. require_once 'Cache/Container.php';
  23. /**
  24. * PEAR/DB Cache Container.
  25. *
  26. * WARNING: Other systems might or might not support certain datatypes of
  27. * the tables shown. As far as I know there's no large binary
  28. * type in SQL-92 or SQL-99. Postgres seems to lack any
  29. * BLOB or TEXT type, for MS-SQL you could use IMAGE, don't know
  30. * about other databases. Please add sugestions for other databases to
  31. * the inline docs.
  32. *
  33. * The field 'changed' has no meaning for the Cache itself. It's just there
  34. * because it's a good idea to have an automatically updated timestamp
  35. * field for debugging in all of your tables.
  36. *
  37. * For _MySQL_ you need this DB table:
  38. *
  39. * CREATE TABLE cache (
  40. * id CHAR(32) NOT NULL DEFAULT '',
  41. * cachegroup VARCHAR(127) NOT NULL DEFAULT '',
  42. * cachedata BLOB NOT NULL DEFAULT '',
  43. * userdata VARCHAR(255) NOT NULL DEFAUL '',
  44. * expires INT(9) NOT NULL DEFAULT 0,
  45. *
  46. * changed TIMESTAMP(14) NOT NULL,
  47. *
  48. * INDEX (expires),
  49. * PRIMARY KEY (id, cachegroup)
  50. * )
  51. *
  52. * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
  53. * @version $Id: db.php,v 1.5 2003/01/04 11:54:46 mj Exp $
  54. * @package Cache
  55. */
  56. class Cache_Container_db extends Cache_Container {
  57. /**
  58. * Name of the DB table to store caching data
  59. *
  60. * @see Cache_Container_file::$filename_prefix
  61. */
  62. var $cache_table = '';
  63. /**
  64. * PEAR DB dsn to use.
  65. *
  66. * @var string
  67. */
  68. var $dsn = '';
  69. /**
  70. * PEAR DB object
  71. *
  72. * @var object PEAR_DB
  73. */
  74. var $db;
  75. function Cache_Container_db($options)
  76. {
  77. if (!is_array($options) || !isset($options['dsn'])) {
  78. return new Cache_Error('No dsn specified!', __FILE__, __LINE__);
  79. }
  80. $this->setOptions($options, array_merge($this->allowed_options, array('dsn', 'cache_table')));
  81. if (!$this->dsn)
  82. return new Cache_Error('No dsn specified!', __FILE__, __LINE__);
  83. $this->db = DB::connect($this->dsn, true);
  84. if (DB::isError($this->db)) {
  85. return new Cache_Error('DB::connect failed: ' . DB::errorMessage($this->db), __FILE__, __LINE__);
  86. } else {
  87. $this->db->setFetchMode(DB_FETCHMODE_ASSOC);
  88. }
  89. }
  90. function fetch($id, $group)
  91. {
  92. $query = sprintf("SELECT cachedata, userdata, expires FROM %s WHERE id = '%s' AND cachegroup = '%s'",
  93. $this->cache_table,
  94. addslashes($id),
  95. addslashes($group)
  96. );
  97. $res = $this->db->query($query);
  98. if (DB::isError($res))
  99. return new Cache_Error('DB::query failed: ' . DB::errorMessage($res), __FILE__, __LINE__);
  100. $row = $res->fetchRow();
  101. if (is_array($row))
  102. $data = array($row['expires'], $this->decode($row['cachedata']), $row['userdata']);
  103. else
  104. $data = array(NULL, NULL, NULL);
  105. // last used required by the garbage collection
  106. // WARNING: might be MySQL specific
  107. $query = sprintf("UPDATE %s SET changed = (NOW() + 0) WHERE id = '%s' AND cachegroup = '%s'",
  108. $this->cache_table,
  109. addslashes($id),
  110. addslashes($group)
  111. );
  112. $res = $this->db->query($query);
  113. if (DB::isError($res))
  114. return new Cache_Error('DB::query failed: ' . DB::errorMessage($res), __FILE__, __LINE__);
  115. return $data;
  116. }
  117. /**
  118. * Stores a dataset.
  119. *
  120. * WARNING: we use the SQL command REPLACE INTO this might be
  121. * MySQL specific. As MySQL is very popular the method should
  122. * work fine for 95% of you.
  123. */
  124. function save($id, $data, $expires, $group, $userdata)
  125. {
  126. $this->flushPreload($id, $group);
  127. $query = sprintf("REPLACE INTO %s (userdata, cachedata, expires, id, cachegroup) VALUES ('%s', '%s', %d, '%s', '%s')",
  128. $this->cache_table,
  129. addslashes($userdata),
  130. addslashes($this->encode($data)),
  131. $this->getExpiresAbsolute($expires) ,
  132. addslashes($id),
  133. addslashes($group)
  134. );
  135. $res = $this->db->query($query);
  136. if (DB::isError($res)) {
  137. return new Cache_Error('DB::query failed: ' . DB::errorMessage($res) , __FILE__, __LINE__);
  138. }
  139. }
  140. function remove($id, $group)
  141. {
  142. $this->flushPreload($id, $group);
  143. $query = sprintf("DELETE FROM %s WHERE id = '%s' and cachegroup = '%s'",
  144. $this->cache_table,
  145. addslashes($id),
  146. addslashes($group)
  147. );
  148. $res = $this->db->query($query);
  149. if (DB::isError($res))
  150. return new Cache_Error('DB::query failed: ' . DB::errorMessage($res), __FILE__, __LINE__);
  151. }
  152. function flush($group = '')
  153. {
  154. $this->flushPreload();
  155. if ($group) {
  156. $query = sprintf("DELETE FROM %s WHERE cachegroup = '%s'", $this->cache_table, addslashes($group));
  157. } else {
  158. $query = sprintf("DELETE FROM %s", $this->cache_table);
  159. }
  160. $res = $this->db->query($query);
  161. if (DB::isError($res))
  162. return new Cache_Error('DB::query failed: ' . DB::errorMessage($res), __FILE__, __LINE__);
  163. }
  164. function idExists($id, $group)
  165. {
  166. $query = sprintf("SELECT id FROM %s WHERE ID = '%s' AND cachegroup = '%s'",
  167. $this->cache_table,
  168. addslashes($id),
  169. addslashes($group)
  170. );
  171. $res = $this->db->query($query);
  172. if (DB::isError($res))
  173. return new Cache_Error('DB::query failed: ' . DB::errorMessage($res), __FILE__, __LINE__);
  174. $row = $res->fetchRow();
  175. if (is_array($row)) {
  176. return true;
  177. } else {
  178. return false;
  179. }
  180. }
  181. function garbageCollection($maxlifetime)
  182. {
  183. $this->flushPreload();
  184. $query = sprintf('DELETE FROM %s WHERE (expires <= %d AND expires > 0) OR changed <= %d',
  185. $this->cache_table,
  186. time(),
  187. time() - $maxlifetime
  188. );
  189. $res = $this->db->query($query);
  190. $query = sprintf('select sum(length(cachedata)) as CacheSize from %s',
  191. $this->cache_table
  192. );
  193. $cachesize = $this->db->GetOne($query);
  194. if (DB::isError($cachesize)) {
  195. return new Cache_Error('DB::query failed: ' . DB::errorMessage($cachesize), __FILE__, __LINE__);
  196. }
  197. //if cache is to big.
  198. if ($cachesize > $this->highwater)
  199. {
  200. //find the lowwater mark.
  201. $query = sprintf('select length(cachedata) as size, changed from %s order by changed DESC',
  202. $this->cache_table
  203. );
  204. $res = $this->db->query($query);
  205. if (DB::isError($res)) {
  206. return new Cache_Error('DB::query failed: ' . DB::errorMessage($res), __FILE__, __LINE__);
  207. }
  208. $numrows = $this->db->numRows($res);
  209. $keep_size = 0;
  210. while ($keep_size < $this->lowwater && $numrows--) {
  211. $entry = $res->fetchRow(DB_FETCHMODE_ASSOC);
  212. $keep_size += $entry['size'];
  213. }
  214. //delete all entries, which were changed before the "lowwwater mark"
  215. $query = sprintf('delete from %s where changed <= '.($entry['changed'] ? $entry['changed'] : 0),
  216. $this->cache_table
  217. );
  218. $res = $this->db->query($query);
  219. }
  220. if (DB::isError($res)) {
  221. return new Cache_Error('DB::query failed: ' . DB::errorMessage($res), __FILE__, __LINE__);
  222. }
  223. }
  224. }
  225. ?>