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// +----------------------------------------------------------------------+ 18// 19// $Id: phplib.php 174777 2004-12-15 09:09:33Z dufuz $ 20 21require_once 'Cache/Container.php'; 22 23/** 24* Stores cache data into a database table using PHPLibs DB abstraction. 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' is used by the garbage collection. Depending on 34* your databasesystem you might have to subclass fetch() and garbageCollection(). 35* 36* For _MySQL_ you need this DB table: 37* 38* CREATE TABLE cache ( 39* id CHAR(32) NOT null DEFAULT '', 40* cachegroup VARCHAR(127) NOT null DEFAULT '', 41* cachedata BLOB NOT null DEFAULT '', 42* userdata VARCHAR(255) NOT null DEFAUL '', 43* expires INT(9) NOT null DEFAULT 0, 44* 45* changed TIMESTAMP(14) NOT null, 46* 47* INDEX (expires), 48* PRIMARY KEY (id, cachegroup) 49* ) 50* 51* 52* @author Ulf Wendel <ulf.wendel@phpdoc.de>, Sebastian Bergmann <sb@sebastian-bergmann.de> 53* @version $Id: phplib.php 174777 2004-12-15 09:09:33Z dufuz $ 54* @package Cache 55* @see save() 56*/ 57class Cache_Container_phplib extends Cache_Container 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 = 'cache'; 65 66 /** 67 * PHPLib object 68 * 69 * @var object PEAR_DB 70 */ 71 var $db; 72 73 /** 74 * Name of the PHPLib DB class to use 75 * 76 * @var string 77 * @see $db_path, $local_path 78 */ 79 var $db_class = ''; 80 81 /** 82 * Filename of your local.inc 83 * 84 * If empty, 'local.inc' is assumed. 85 * 86 * @var string 87 */ 88 var $local_file = ''; 89 90 /** 91 * Include path for you local.inc 92 * 93 * HINT: If your're not using PHPLib's prepend.php you must 94 * take care that all classes (files) references by you 95 * local.inc are included automatically. So you might 96 * want to write a new local2.inc that only referrs to 97 * the database class (file) you're using and includes all required files. 98 * 99 * @var string path to your local.inc - make sure to add a trailing slash 100 * @see $local_file 101 */ 102 var $local_path = ''; 103 104 /** 105 * Creates an instance of a phplib db class to use it for storage. 106 * 107 * @param mixed If empty the object tries to used the 108 * preconfigured class variables. If given it 109 * must be an array with: 110 * db_class => name of the DB class to use 111 * optional: 112 * db_file => filename of the DB class 113 * db_path => path to the DB class 114 * local_file => kind of local.inc 115 * local_patk => path to the local.inc 116 * see $local_path for some hints.s 117 * @see $local_path 118 */ 119 function Cache_Container_phplib($options = '') 120 { 121 if (is_array($options)) { 122 $this->setOptions($options, array_merge($this->allowed_options, array('db_class', 'db_file', 'db_path', 'local_file', 'local_path'))); 123 } 124 if (!$this->db_class) { 125 return new Cache_Error('No database class specified.', __FILE__, __LINE__); 126 } 127 // include the required files 128 if ($this->db_file) { 129 include_once $this->db_path . $this->db_file; 130 } 131 if ($this->local_file) { 132 include_once $this->local_path . $this->local_file; 133 } 134 // create a db object 135 $this->db = new $this->db_class; 136 } // end constructor 137 138 function fetch($id, $group) 139 { 140 $query = sprintf("SELECT expires, cachedata, userdata FROM %s WHERE id = '%s' AND cachegroup = '%s'", 141 $this->cache_table, 142 $id, 143 $group 144 ); 145 $this->db->query($query); 146 if (!$this->db->Next_Record()) { 147 return array(null, null, null); 148 } 149 // last used required by the garbage collection 150 // WARNING: might be MySQL specific 151 $query = sprintf("UPDATE %s SET changed = (NOW() + 0) WHERE id = '%s' AND cachegroup = '%s'", 152 $this->cache_table, 153 $id, 154 $group 155 ); 156 $this->db->query($query); 157 return array($this->db->f('expires'), $this->decode($this->db->f('cachedata')), $this->db->f('userdata')); 158 } // end func fetch 159 160 /** 161 * Stores a dataset. 162 * 163 * WARNING: we use the SQL command REPLACE INTO this might be 164 * MySQL specific. As MySQL is very popular the method should 165 * work fine for 95% of you. 166 */ 167 function save($id, $data, $expires, $group) 168 { 169 $this->flushPreload($id, $group); 170 171 $query = sprintf("REPLACE INTO %s (cachedata, expires, id, cachegroup) VALUES ('%s', %d, '%s', '%s')", 172 $this->cache_table, 173 addslashes($this->encode($data)), 174 $this->getExpiresAbsolute($expires) , 175 $id, 176 $group 177 ); 178 $this->db->query($query); 179 180 return (boolean)$this->db->affected_rows(); 181 } // end func save 182 183 function remove($id, $group) 184 { 185 $this->flushPreload($id, $group); 186 $this->db->query( 187 sprintf("DELETE FROM %s WHERE id = '%s' AND cachegroup = '%s'", 188 $this->cache_table, 189 $id, 190 $group 191 ) 192 ); 193 194 return (boolean)$this->db->affected_rows(); 195 } // end func remove 196 197 function flush($group) 198 { 199 $this->flushPreload(); 200 201 if ($group) { 202 $this->db->query(sprintf("DELETE FROM %s WHERE cachegroup = '%s'", $this->cache_table, $group)); 203 } else { 204 $this->db->query(sprintf("DELETE FROM %s", $this->cache_table)); 205 } 206 207 return $this->db->affected_rows(); 208 } // end func flush 209 210 function idExists($id, $group) 211 { 212 $this->db->query( 213 sprintf("SELECT id FROM %s WHERE ID = '%s' AND cachegroup = '%s'", 214 $this->cache_table, 215 $id, 216 $group 217 ) 218 ); 219 220 return (boolean)$this->db->nf(); 221 } // end func isExists 222 223 function garbageCollection($maxlifetime) 224 { 225 $this->flushPreload(); 226 227 $this->db->query( 228 sprintf("DELETE FROM %s WHERE (expires <= %d AND expires > 0) OR changed <= (NOW() - %d)", 229 $this->cache_table, 230 time(), 231 $maxlifetime 232 ) 233 ); 234 235 //check for total size of cache 236 $query = sprintf('select sum(length(cachedata)) as CacheSize from %s', 237 $this->cache_table 238 ); 239 240 $this->db->query($query); 241 $this->db->Next_Record(); 242 $cachesize = $this->db->f('CacheSize'); 243 //if cache is to big. 244 if ($cachesize > $this->highwater) { 245 //find the lowwater mark. 246 $query = sprintf('select length(cachedata) as size, changed from %s order by changed DESC', 247 $this->cache_table 248 ); 249 $this->db->query($query); 250 251 $keep_size=0; 252 while ($keep_size < $this->lowwater && $this->db->Next_Record()) { 253 $keep_size += $this->db->f('size'); 254 } 255 //delete all entries, which were changed before the "lowwwater mark" 256 $query = sprintf('delete from %s where changed <= '.$this->db->f('changed'), 257 $this->cache_table 258 ); 259 260 $this->db->query($query); 261 } 262 } // end func garbageCollection 263} 264?>