mdb.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. <?php
  2. // +----------------------------------------------------------------------+
  3. // | PEAR :: Cache :: MDB Container |
  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. // | Note: This is a MDB-oriented rewrite of Cache/Container/db.php. |
  16. // | Thanks to Lukas Smith for his patience in answering my questions |
  17. // +----------------------------------------------------------------------+
  18. // | Author: Lorenzo Alberton <l.alberton at quipo.it> |
  19. // +----------------------------------------------------------------------+
  20. //
  21. // $Id: mdb.php,v 1.3 2003/01/04 11:54:46 mj Exp $
  22. require_once 'MDB.php';
  23. require_once 'Cache/Container.php';
  24. /**
  25. * PEAR/MDB Cache Container.
  26. *
  27. * NB: The field 'changed' has no meaning for the Cache itself. It's just there
  28. * because it's a good idea to have an automatically updated timestamp
  29. * field for debugging in all of your tables.
  30. *
  31. * A XML MDB-compliant schema example for the table needed is provided.
  32. * Look at the file "mdb_cache_schema.xml" for that.
  33. *
  34. * ------------------------------------------
  35. * A basic usage example:
  36. * ------------------------------------------
  37. *
  38. * $dbinfo = array(
  39. * 'database' => 'dbname',
  40. * 'phptype' => 'mysql',
  41. * 'username' => 'root',
  42. * 'password' => '',
  43. * 'cache_table' => 'cache'
  44. * );
  45. *
  46. *
  47. * $cache = new Cache('mdb', $dbinfo);
  48. * $id = $cache->generateID('testentry');
  49. *
  50. * if ($data = $cache->get($id)) {
  51. * echo 'Cache hit.<br />Data: '.$data;
  52. *
  53. * } else {
  54. * $data = 'data of any kind';
  55. * $cache->save($id, $data);
  56. * echo 'Cache miss.<br />';
  57. * }
  58. *
  59. * ------------------------------------------
  60. *
  61. * @author Lorenzo Alberton <l.alberton at quipo.it>
  62. * @version $Id: mdb.php,v 1.3 2003/01/04 11:54:46 mj Exp $
  63. * @package Cache
  64. */
  65. class Cache_Container_mdb extends Cache_Container {
  66. /**
  67. * Name of the MDB table to store caching data
  68. *
  69. * @see Cache_Container_file::$filename_prefix
  70. */
  71. var $cache_table = '';
  72. /**
  73. * PEAR MDB object
  74. *
  75. * @var object PEAR_MDB
  76. */
  77. var $db;
  78. /**
  79. * Constructor
  80. *
  81. * @param mixed Array with connection info or dsn string
  82. */
  83. function Cache_Container_mdb($options)
  84. {
  85. $this->db = &MDB::Connect($options);
  86. if(MDB::isError($this->db)) {
  87. return new Cache_Error('MDB::connect failed: '
  88. . $this->db->getMessage(), __FILE__, __LINE__);
  89. } else {
  90. $this->db->setFetchMode(MDB_FETCHMODE_ASSOC);
  91. }
  92. $this->setOptions($options, array_merge($this->allowed_options,
  93. array('dsn', 'cache_table')));
  94. }
  95. /**
  96. * Fetch in the db the data that matches input parameters
  97. *
  98. * @param string dataset ID
  99. * @param string cache group
  100. * @return mixed dataset value or NULL/Cache_Error on failure
  101. * @access public
  102. */
  103. function fetch($id, $group)
  104. {
  105. $query = 'SELECT cachedata FROM ' . $this->cache_table
  106. .' WHERE id=' . $this->db->getTextValue($id)
  107. .' AND cachegroup=' . $this->db->getTextValue($group);
  108. if($res = $this->db->query($query)) {
  109. if($this->db->endOfResult($res)) {
  110. //no rows returned
  111. $data = array(NULL, NULL, NULL);
  112. } else {
  113. $clob = $this->db->fetchClob($res,0,'cachedata');
  114. if(!MDB::isError($clob)) {
  115. $cached_data = '';
  116. while(!$this->db->endOfLOB($clob)) {
  117. if(MDB::isError($error =
  118. $this->db->readLob($clob,$data,8000)<0)) {
  119. return new Cache_Error('MDB::query failed: '
  120. . $error->getMessage(), __FILE__, __LINE__);
  121. }
  122. $cached_data .= $data;
  123. }
  124. unset($data);
  125. $this->db->destroyLob($clob);
  126. $this->db->freeResult($res);
  127. //finished fetching LOB, now fetch other fields...
  128. $query = 'SELECT userdata, expires FROM ' . $this->cache_table
  129. .' WHERE id=' . $this->db->getTextValue($id)
  130. .' AND cachegroup=' . $this->db->getTextValue($group);
  131. if($res = $this->db->query($query)) {
  132. $row = $this->db->fetchInto($res);
  133. if (is_array($row)) {
  134. $data = array(
  135. $row['expires'],
  136. $this->decode($cached_data),
  137. $row['userdata']
  138. );
  139. } else {
  140. $data = array(NULL, NULL, NULL);
  141. }
  142. } else {
  143. $data = array(NULL, NULL, NULL);
  144. }
  145. } else {
  146. return new Cache_Error('MDB::query failed: '
  147. . $clob->getMessage(), __FILE__, __LINE__);
  148. }
  149. }
  150. $this->db->freeResult($res);
  151. } else {
  152. //return new Cache_Error('MDB::query failed: '
  153. // . $result->getMessage(), __FILE__, __LINE__);
  154. $data = array(NULL, NULL, NULL);
  155. }
  156. // last used required by the garbage collection
  157. $query = 'UPDATE ' . $this->cache_table
  158. .' SET changed=' . time()
  159. .' WHERE id=' . $this->db->getTextValue($id)
  160. .' AND cachegroup=' . $this->db->getTextValue($group);
  161. $res = $this->db->query($query);
  162. if (MDB::isError($res)) {
  163. return new Cache_Error('MDB::query failed: '
  164. . $this->db->errorMessage($res), __FILE__, __LINE__);
  165. }
  166. return $data;
  167. }
  168. /**
  169. * Stores a dataset in the database
  170. *
  171. * If dataset_ID already exists, overwrite it with new data,
  172. * else insert data in a new record.
  173. *
  174. * @param string dataset ID
  175. * @param mixed data to be cached
  176. * @param integer expiration time
  177. * @param string cache group
  178. * @param string userdata
  179. * @access public
  180. */
  181. function save($id, $data, $expires, $group, $userdata)
  182. {
  183. global $db;
  184. $this->flushPreload($id, $group);
  185. $fields = array(
  186. 'id' => array(
  187. 'Type' => 'text',
  188. 'Value' => $id,
  189. 'Key' => true
  190. ),
  191. 'userdata' => array(
  192. 'Type' => 'integer',
  193. 'Value' => $userdata,
  194. 'Null' => ($userdata ? false : true)
  195. ),
  196. 'expires' => array(
  197. 'Type' => 'integer',
  198. 'Value' => $this->getExpiresAbsolute($expires)
  199. ),
  200. 'cachegroup' => array(
  201. 'Type' => 'text',
  202. 'Value' => $group
  203. )
  204. );
  205. $result = $this->db->replace($this->cache_table, $fields);
  206. if(MDB::isError($result)) {
  207. //Var_Dump::display($result);
  208. return new Cache_Error('MDB::query failed: '
  209. . $this->db->errorMessage($result), __FILE__, __LINE__);
  210. }
  211. unset($fields); //end first part of query
  212. $query2 = 'UPDATE ' . $this->cache_table
  213. .' SET cachedata=?'
  214. .' WHERE id='. $this->db->getTextValue($id);
  215. if(($prepared_query = $this->db->prepareQuery($query2))) {
  216. $char_lob = array(
  217. 'Error' => '',
  218. 'Type' => 'data',
  219. 'Data' => $this->encode($data)
  220. );
  221. if(!MDB::isError($clob = $this->db->createLob($char_lob))) {
  222. $this->db->setParamClob($prepared_query,1,$clob,'cachedata');
  223. if(MDB::isError($error=$this->db->executeQuery($prepared_query))) {
  224. return new Cache_Error('MDB::query failed: '
  225. . $error->getMessage() , __FILE__, __LINE__);
  226. }
  227. $this->db->destroyLob($clob);
  228. } else {
  229. // creation of the handler object failed
  230. return new Cache_Error('MDB::query failed: '
  231. . $clob->getMessage() , __FILE__, __LINE__);
  232. }
  233. $this->db->freePreparedQuery($prepared_query);
  234. } else {
  235. //prepared query failed
  236. return new Cache_Error('MDB::query failed: '
  237. . $prepared_query->getMessage() , __FILE__, __LINE__);
  238. }
  239. }
  240. /**
  241. * Removes a dataset from the database
  242. *
  243. * @param string dataset ID
  244. * @param string cache group
  245. */
  246. function remove($id, $group)
  247. {
  248. $this->flushPreload($id, $group);
  249. $query = 'DELETE FROM ' . $this->cache_table
  250. .' WHERE id=' . $this->db->getTextValue($id)
  251. .' AND cachegroup=' . $this->db->getTextValue($group);
  252. $res = $this->db->query($query);
  253. if (MDB::isError($res)) {
  254. return new Cache_Error('MDB::query failed: '
  255. . $this->db->errorMessage($res), __FILE__, __LINE__);
  256. }
  257. }
  258. /**
  259. * Remove all cached data for a certain group, or empty
  260. * the cache table if no group is specified.
  261. *
  262. * @param string cache group
  263. */
  264. function flush($group = '')
  265. {
  266. $this->flushPreload();
  267. if ($group) {
  268. $query = 'DELETE FROM ' . $this->cache_table
  269. .' WHERE cachegroup=' . $this->db->getTextValue($group);
  270. } else {
  271. $query = 'DELETE FROM ' . $this->cache_table;
  272. }
  273. $res = $this->db->query($query);
  274. if (MDB::isError($res)) {
  275. return new Cache_Error('MDB::query failed: '
  276. . $this->db->errorMessage($res), __FILE__, __LINE__);
  277. }
  278. }
  279. /**
  280. * Check if a dataset ID/group exists.
  281. *
  282. * @param string dataset ID
  283. * @param string cache group
  284. * @return boolean
  285. */
  286. function idExists($id, $group)
  287. {
  288. $query = 'SELECT id FROM ' . $this->cache_table
  289. .' WHERE id=' . $this->db->getTextValue($id)
  290. .' AND cachegroup=' . $this->db->getTextValue($group);
  291. echo $query;
  292. $res = $this->db->query($query);
  293. if (MDB::isError($res)) {
  294. return new Cache_Error('MDB::query failed: '
  295. . $this->db->errorMessage($res), __FILE__, __LINE__);
  296. }
  297. $row = $this->db->fetchInto($res);
  298. if (is_array($row)) {
  299. return true;
  300. } else {
  301. return false;
  302. }
  303. }
  304. /**
  305. * Garbage collector.
  306. *
  307. * @param int maxlifetime
  308. */
  309. function garbageCollection($maxlifetime)
  310. {
  311. $this->flushPreload();
  312. $query = 'DELETE FROM ' . $this->cache_table
  313. .' WHERE (expires <= ' . time()
  314. .' AND expires > 0) OR changed <= '. time() - $maxlifetime;
  315. $res = $this->db->query($query);
  316. $query = 'SELECT sum(length(cachedata)) as CacheSize FROM '
  317. . $this->cache_table;
  318. $cachesize = $this->db->getOne($query);
  319. if (MDB::isError($cachesize)) {
  320. return new Cache_Error('MDB::query failed: '
  321. . $this->db->errorMessage($cachesize), __FILE__, __LINE__);
  322. }
  323. //if cache is to big.
  324. if ($cachesize > $this->highwater)
  325. {
  326. //find the lowwater mark.
  327. $query = 'SELECT length(cachedata) as size, changed FROM '
  328. . $this->cache_table .' ORDER BY changed DESC';
  329. $res = $this->db->query($query);
  330. if (MDB::isError($res)) {
  331. return new Cache_Error('MDB::query failed: '
  332. . $this->db->errorMessage($res), __FILE__, __LINE__);
  333. }
  334. $numrows = $this->db->numRows($res);
  335. $keep_size = 0;
  336. while ($keep_size < $this->lowwater && $numrows--) {
  337. $entry = $this->db->fetchInto($res,MDB_FETCHMODE_ASSOC);
  338. $keep_size += $entry['size'];
  339. }
  340. //delete all entries, which were changed before the "lowwater mark"
  341. $query = 'DELETE FROM ' . $this->cache_table
  342. .' WHERE changed<='.($entry['changed'] ? $entry['changed'] : 0);
  343. $res = $this->db->query($query);
  344. if (MDB::isError($res)) {
  345. return new Cache_Error('MDB::query failed: '
  346. . $this->db->errorMessage($res), __FILE__, __LINE__);
  347. }
  348. }
  349. }
  350. }
  351. ?>