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: Christian Stocker <chregu@phant.ch> | 16// +----------------------------------------------------------------------+ 17// 18// $Id: dbx.php 268860 2008-11-12 14:56:26Z clockwerx $ 19 20 21require_once 'Cache/Container.php'; 22 23/** 24* ext/dbx 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 DEFAULT '', 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 Christian Stocker <chregu@phant.ch> 53* @version $Id: dbx.php 268860 2008-11-12 14:56:26Z clockwerx $ 54* @package Cache 55*/ 56class Cache_Container_dbx extends Cache_Container 57{ 58 59 /** 60 * Name of the DB table to store caching data 61 * 62 * @see Cache_Container_file::$filename_prefix 63 */ 64 var $cache_table = ''; 65 66 /** 67 * DBx module to use 68 * 69 * at the moment only mysql or odbc 70 * 71 * @var string 72 */ 73 var $module = ''; 74 75 /** 76 * DB host to use 77 * 78 * @var string 79 */ 80 var $host = ''; 81 82 /** 83 * DB database to use 84 * 85 * @var string 86 */ 87 var $db = ''; 88 89 /** 90 * DB username to use 91 * 92 * @var string 93 */ 94 var $username = ''; 95 96 /** 97 * DB password to use 98 * 99 * @var string 100 */ 101 var $password = ''; 102 103 104 /** 105 * Establish a persistent connection? 106 * 107 * @var boolean 108 */ 109 var $persistent = true; 110 111 112 function Cache_Container_dbx($options) 113 { 114 if (!is_array($options) ) { 115 return new Cache_Error('No options specified!', __FILE__, __LINE__); 116 } 117 118 $this->setOptions($options, array_merge($this->allowed_options, array('module','host','db','username','password', 'cache_table', 'persistent'))); 119 120 if (!$this->module) 121 return new Cache_Error('No module specified!', __FILE__, __LINE__); 122 123 $this->db = dbx_connect($this->module, $this->host, $this->db, $this->username, $this->password, $this->persistent); 124 125 if (dbx_error($this->db)) { 126 return new Cache_Error('DBx connect failed: ' . dbx_error($this->db), __FILE__, __LINE__); 127 } else { 128 //not implemented yet in dbx 129 //$this->db->setFetchMode(DB_FETCHMODE_ASSOC); 130 } 131 } 132 133 function fetch($id, $group) 134 { 135 $query = sprintf("SELECT cachedata, userdata, expires FROM %s WHERE id = '%s' AND cachegroup = '%s'", 136 $this->cache_table, 137 addslashes($id), 138 addslashes($group) 139 ); 140 141 $res = dbx_query($this->db, $query); 142 if (dbx_error($this->db)) { 143 return new Cache_Error('DBx query failed: ' . dbx_error($this->db), __FILE__, __LINE__); 144 } 145 $row = $res->data[0]; 146 147 if (is_array($row)) { 148 $data = array($row['expires'], $this->decode($row['cachedata']), $row['userdata']); 149 } else { 150 $data = array(null, null, null); 151 } 152 // last used required by the garbage collection 153 // WARNING: might be MySQL specific 154 $query = sprintf("UPDATE %s SET changed = (NOW() + 0) WHERE id = '%s' AND cachegroup = '%s'", 155 $this->cache_table, 156 addslashes($id), 157 addslashes($group) 158 ); 159 160 $res = dbx_query($this->db, $query); 161 if (dbx_error($this->db)) { 162 return new Cache_Error('DBx query failed: ' . dbx_error($this->db), __FILE__, __LINE__); 163 } 164 return $data; 165 } 166 167 /** 168 * Stores a dataset. 169 * 170 * WARNING: we use the SQL command REPLACE INTO this might be 171 * MySQL specific. As MySQL is very popular the method should 172 * work fine for 95% of you. 173 */ 174 function save($id, $data, $expires, $group, $userdata) 175 { 176 $this->flushPreload($id, $group); 177 178 $query = sprintf("REPLACE INTO %s (userdata, cachedata, expires, id, cachegroup) VALUES ('%s', '%s', %d, '%s', '%s')", 179 $this->cache_table, 180 addslashes($userdata), 181 addslashes($this->encode($data)), 182 $this->getExpiresAbsolute($expires) , 183 addslashes($id), 184 addslashes($group) 185 ); 186 187 $res = dbx_query($this->db, $query); 188 189 if (dbx_error($this->db)) { 190 return new Cache_Error('DBx query failed: ' . dbx_error($this->db) , __FILE__, __LINE__); 191 } 192 } 193 194 function remove($id, $group) 195 { 196 $this->flushPreload($id, $group); 197 198 $query = sprintf("DELETE FROM %s WHERE id = '%s' and cachegroup = '%s'", 199 $this->cache_table, 200 addslashes($id), 201 addslashes($group) 202 ); 203 204 $res = dbx_query($this->db, $query); 205 206 if (dbx_error($this->db)) { 207 return new Cache_Error('DBx query failed: ' . dbx_error($this->db), __FILE__, __LINE__); 208 } 209 } 210 211 function flush($group = '') 212 { 213 $this->flushPreload(); 214 215 if ($group) { 216 $query = sprintf("DELETE FROM %s WHERE cachegroup = '%s'", $this->cache_table, addslashes($group)); 217 } else { 218 $query = sprintf("DELETE FROM %s", $this->cache_table); 219 } 220 221 $res = dbx_query($this->db,$query); 222 223 if (dbx_error($this->db)) { 224 return new Cache_Error('DBx query failed: ' . dbx_error($this->db), __FILE__, __LINE__); 225 } 226 } 227 228 function idExists($id, $group) 229 { 230 $query = sprintf("SELECT id FROM %s WHERE ID = '%s' AND cachegroup = '%s'", 231 $this->cache_table, 232 addslashes($id), 233 addslashes($group) 234 ); 235 236 $res = dbx_query($this->db, $query); 237 238 if (dbx_error($this->db)) { 239 return new Cache_Error('DBx query failed: ' . dbx_error($this->db), __FILE__, __LINE__); 240 } 241 242 $row = $res[0]; 243 244 if (is_array($row)) { 245 return true; 246 } 247 return false; 248 } 249 250 function garbageCollection($maxlifetime) 251 { 252 $this->flushPreload(); 253 254 $query = sprintf('DELETE FROM %s WHERE (expires <= %d AND expires > 0) OR changed <= (NOW() - %d)', 255 $this->cache_table, 256 time(), 257 $maxlifetime 258 ); 259 260 261 $res = dbx_query($this->db, $query); 262 263 if (dbx_error($this->db)) { 264 return new Cache_Error('DBx query failed: ' . dbx_error($this->db), __FILE__, __LINE__); 265 } 266 $query = sprintf('select sum(length(cachedata)) as CacheSize from %s', 267 $this->cache_table 268 ); 269 270 $res = dbx_query($this->db, $query); 271 //if cache is to big. 272 if ($res->data[0][CacheSize] > $this->highwater) { 273 //find the lowwater mark. 274 $query = sprintf('select length(cachedata) as size, changed from %s order by changed DESC', 275 $this->cache_table 276 ); 277 278 $res = dbx_query($this->db, $query); 279 $keep_size = 0; 280 $i = 0; 281 while ($keep_size < $this->lowwater && $i < $res->rows ) { 282 $keep_size += $res->data[$i][size]; 283 $i++; 284 } 285 286 //delete all entries, which were changed before the "lowwwater mark" 287 $query = sprintf('delete from %s where changed <= %s', 288 $this->cache_table, 289 $res->data[$i][changed] 290 ); 291 $res = dbx_query($this->db, $query); 292 } 293 } 294} 295?> 296