3 phpCache v1.4.1 - PHP caching engine
4 Copyright (C) 2001 Nathan <nathan@0x00.org>
5 '.1' Bug Fix By Joel Kronenberg <joel.kronenberg@utoronto.ca>
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 if (defined('CACHE_DIR') && (CACHE_DIR != '')) {
23 define('CACHE_ON', 1); /* disable caching */
25 define('CACHE_ON', 0); /* enable caching */
27 if (!defined('CACHE_DIR')) {
28 define('CACHE_DIR', '');
32 $CACHE_DEBUG = 0; /* Default: 0 - Turn debugging on/off */
34 define('THIS_CACHE_DIR', CACHE_DIR . '/transformable_cache_' . DB_NAME);
36 define('CACHE_GC', .10); /* Default: .10 - Probability of garbage collection */
37 define('CACHE_USE_STORAGE_HASH', 0); /* Default: 1 - Use storage hashing. This will increase peformance if you are caching many pages. */
38 define('CACHE_STORAGE_CREATED', 0); /* Default: 0 - This is a peformance tweak. If you set this to 1, phpCache will not check if storage structures have been created. Don't change this unles you are *SURE* the cache storage has been created. */
39 define('CACHE_MAX_STORAGE_HASH', 23); /* Don't touch this unless you know what you're doing */
40 define('CACHE_STORAGE_PERM', 0700); /* Default: 0700 - Default permissions for storage directories. */
41 define('CACHE_MAX_FILENAME_LEN', 250); /* How long the cache storage filename can be before it will md5() the entire thing */
43 $CACHE_HAS=array( 'ob_start' => function_exists('ob_start'),
44 'realpath' => function_exists('realpath'),
45 'crc32' => function_exists('crc32')
48 define('CACHE_VERSION', '1.4.1');
49 define('CACHE_STORAGE_CHECKFILE', THIS_CACHE_DIR
50 . '/.phpCache-storage-V'
53 . CACHE_USE_STORAGE_HASH);
55 define('CACHE_INFO', 'phpCache v1.4.1 By nathan@0x00.org (.1 Bug Fix By joel.kronenberg@utoronto.ca)');
57 /* This resets the cache state */
58 function cache_reset() {
59 global $cache_pbufferlen, $cache_absfile, $cache_data, $cache_variables, $cache_headers, $cache_expire_cond, $cache_output_buffer;
61 cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile);
63 $cache_pbufferlen = FALSE;
64 $cache_absfile = NULL;
65 $cache_data = array();
67 $cache_expire_cond = NULL;
68 $cache_variables=array();
69 $cache_headers=array();
70 $cache_output_buffer='';
72 cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile);
75 /* Used to output to the cache output, should only be needed if you dont have output buffering (PHP3) */
76 function cache_output($str) {
77 global $cache_output_buffer;
78 if (!$GLOBALS["CACHE_HAS"]['ob_start']) {
79 $cache_output_buffer.=$str;
84 /* Saves a header state between caching */
85 function cache_header($header) {
86 global $cache_headers;
88 cache_debug('Adding header '.$header);
89 $cache_headers[]=$header;
92 /* This is a function used internally by phpCache to evaluate the conditional expiration. This allows the eval() to have its own simulated namespace so it doesnt conflict with any others. */
93 function cache_eval_expire($cond, &$vars) {
100 /* Call this function before a call to cache() to evaluate a dynamic expiration on cache_expire_variable()'s */
101 function cache_expire_if($expr) {
102 global $cache_expire_cond;
103 $cache_expire_cond=$expr;
106 /* Call this function to add a variable to the expire variables store */
107 function cache_expire_variable($vn) {
108 cache_debug("Adding $vn to expire variable store");
113 function cache_debug($s) {
116 print "Debug: $s<br>\n";
120 /* Saves a variable state between caching */
121 function cache_variable($vn) {
122 global $cache_variables;
123 cache_debug(__LINE__ . ": Adding $vn to the variable store");
124 $cache_variables[] = $vn;
128 /* Returns the default key used by the helper functions */
129 function cache_default_key() {
130 global $HTTP_POST_VARS, $HTTP_GET_VARS, $QUERY_STRING;
131 return md5("POST=" . serialize($HTTP_POST_VARS) . " GET=" . serialize($HTTP_GET_VARS) . "QS=" . $QUERY_STRING);
134 /* Returns the default object used by the helper functions */
135 function cache_default_object() {
136 global $REQUEST_URI, $SERVER_NAME, $SCRIPT_FILENAME;
137 if ($GLOBALS["CACHE_HAS"]["realpath"]) {
138 $sfn=realpath($SCRIPT_FILENAME);
140 $sfn=$SCRIPT_FILENAME;
142 $name="http://$SERVER_NAME/$sfn";
146 /* Caches the current page based on the page name and the GET/POST
147 variables. All must match or else it will not be fectched
149 function cache_all($cachetime=120) {
150 $key=cache_default_key();
151 $object=cache_default_object();
152 return cache($cachetime, $object, $key);
155 /* Same as cache_all() but it throws the session_id() into
157 function cache_session($cachetime=120) {
158 global $HTTP_POST_VARS, $HTTP_GET_VARS;
159 $key=cache_default_key() . 'SESSIONID=' . session_id();
160 $object=cache_default_object();
161 return cache($cachetime, $object, $key);
164 /* Manually purge an item in the cache */
165 function cache_purge($object, $key) {
166 $thefile=cache_storage($object, $key);
167 //cache_lock($thefile, TRUE);
168 if (is_file($thefile)) {
169 $ret=@unlink($thefile);
174 //cache_lock($thefile, FALSE);
178 /* Manually purge all items in the cache */
179 function cache_purge_all() {
180 return cache_gc(NULL, 1, TRUE);
183 /* Caches $object based on $key for $cachetime, will return 0 if the
184 object has expired or the object does not exist. */
185 function cache($cachetime, $object, $key=NULL) {
186 global $cache_pbufferlen, $cache_absfile, $cache_file, $cache_data, $cache_expire_cond;
187 cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile);
189 cache_debug('Not caching, CACHE_ON is off');
193 cache_debug(__LINE__.': Caching based on <b>OBJECT</b>='.$object.' <b>KEY</b>='.$key);
194 $cache_absfile=cache_storage($object, $key);
195 cache_debug(__LINE__.': Got cache_storage: '.$cache_absfile);
196 if (($buff=cache_read($cache_absfile))) {
197 cache_debug('Opened the cache file');
198 $cdata=unserialize($buff);
199 if (is_array($cdata)) {
200 $curco = $cdata['cache_object'];
201 if ($curco!=$cache_absfile) {
202 cache_debug("Holy shit that is not my cache file! why? got=$curco wanted=$cache_absfile");
205 if ($cache_expire_cond) {
206 $expireit=cache_eval_expire($cache_expire_cond, $cdata['variables']);
208 if ($cdata['cachetime'] != $cachetime) {
209 cache_debug('Expiring because cachetime changed');
212 if (!$expireit && ($cdata['cachetime']=="0" || $cdata['expire']>=$curtime)) {
213 $expirein=$cdata['expire']-$curtime+1;
214 cache_debug('Cache expires in '.$expirein);
215 if (is_array($cdata['variables'])) {
216 while (list($k,$v)=each($cdata['variables'])) {
217 cache_debug("Restoring variable $k to value $v");
221 if (is_array($cdata['headers'])) {
222 while(list(,$h)=each($cdata['headers'])) {
223 cache_debug("Restoring header $h");
227 print $cdata['content'];
229 if ($cdata['cachetime']=='0') $ret='INFINITE';
236 cache_debug(__LINE__.': Failed to open previous cache of '.$cache_absfile);
241 /* readlink() is not supported on win32, changed to is_link */
242 if (is_link($cache_absfile)) {
243 cache_debug("$cache_absfile is a symlink! not caching!");
246 cache_debug(__LINE__.': not a symlink');
247 cache_debug(__LINE__.': Got cache_storage: '.$cache_absfile);
248 @touch($cache_absfile);
250 /* cases probs on win32 */
251 //cache_lock($cache_absfile, TRUE);
255 $cache_data['expire'] = $curtime + $cachetime;
256 $cache_data['cachetime']= $cachetime;
257 $cache_data['curtime'] = $curtime;
258 $cache_data['version'] = CACHE_VERSION;
259 $cache_data['key'] = $key;
260 $cache_data['object'] = $object;
262 if ($GLOBALS['CACHE_HAS']['ob_start']) {
263 $cache_pbufferlen = ob_get_length();
264 /* If ob_get_length() returns false, output buffering was not on. turn it on. */
265 if (cache_iftype($cache_pbufferlen, FALSE)) {
269 $cache_pbufferlen=FALSE;
271 cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile);
275 /* This *MUST* be at the end of a cache() block or else the cache
276 will not be stored! */
277 function endcache($store=TRUE, $send_output = TRUE) {
278 global $cache_pbufferlen, $cache_absfile, $cache_data, $cache_variables, $cache_headers, $cache_ob_handler, $cache_output_buffer;
279 cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile);
281 cache_debug('Not caching, CACHE_ON is off');
285 if ($GLOBALS[CACHE_HAS]['ob_start']) {
286 $content=ob_get_contents();
287 if (cache_iftype($cache_pbufferlen,FALSE)) {
288 /* Output buffering was off before this, we just need to turn it off again */
293 cache_debug(__LINE__.': Content sent. flush()');
296 cache_debug(__LINE__.': Content ignored. clean()');
298 cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile);
300 /* Output buffering was already on, so get our chunk of data for caching */
301 $content=substr($content, $cache_pbufferlen);
304 $content=$cache_output_buffer;
311 if ($cache_absfile != NULL) {
312 $cache_data['content'] = $content;
313 $variables = array();
314 foreach ($cache_variables as $vn) {
315 //while(list(,$vn)=each($cache_variables)) {
316 cache_debug(__LINE__ . ': Found variable: <b>'.$vn.'</b>');
317 if (isset($GLOBALS[$vn])) {
319 cache_debug(__LINE__ . ': Setting variable '.$vn.' to '.$val);
320 $variables[$vn]=$val;
323 $cache_data['cache_object'] = $cache_absfile;
324 $cache_data['variables'] = $variables;
325 $cache_data['headers'] = $cache_headers;
326 $datas = serialize($cache_data);
327 cache_write($cache_absfile, $datas);
329 cache_debug(__LINE__ .': no variables found');
330 cache_debug($cache_variables[0]);
331 cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile);
333 /* casues probs on win32 */
334 cache_lock($cache_absfile, FALSE);
338 cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile);
339 cache_debug(__LINE__. ': <b>Caching is done!</b><br>');
342 /* Obtain a lock on the cache storage, this can be stripped out
343 and changed to a different handler like a database or
345 function cache_lock($file, $open=TRUE) {
349 cache_debug('trying to lock '.$file);
350 $fp = @fopen($file, 'r');
352 $ret = @flock($fp, LOCK_SH); /* get a shared lock */
355 cache_debug('trying to unlock '.$file);
356 $ret = @flock($fp, LOCK_UN);
363 /* This is the function that writes out the cache */
364 function cache_write($file, $data) {
365 cache_debug(__LINE__.': Writing cache data to file: '.$file);
367 $fp = fopen($file, 'wb+');
368 @flock($fp, LOCK_EX); /* get a shared lock */
370 cache_debug('Failed to open for write out to '.$file);
373 @fwrite($fp, $data, strlen($data));
374 @flock($fp, LOCK_UN); /* get a shared lock */
380 /* This function reads in the cache, duh */
381 function cache_read($file) {
382 $fp = @fopen($file, 'r');
384 cache_debug(__LINE__.': Failed opening file '.realpath($file));
389 while (($tmp=fread($fp, 4096))) {
396 /* This function is called automatically by phpCache to create the cache directory structure */
397 function cache_create_storage() {
399 $failed |= !@mkdir(THIS_CACHE_DIR, CACHE_STORAGE_PERM);
400 if (CACHE_USE_STORAGE_HASH) {
401 for ($a=0; $a<CACHE_MAX_STORAGE_HASH; $a++) {
402 $thedir=THIS_CACHE_DIR . "/$a/";
403 $failed|=!@mkdir($thedir, CACHE_STORAGE_PERM);
404 for ($b=0; $b<CACHE_MAX_STORAGE_HASH; $b++) {
405 $thedir=THIS_CACHE_DIR . "/$a/$b/";
406 $failed|=!@mkdir($thedir, CACHE_STORAGE_PERM);
407 for ($c=0; $c<CACHE_MAX_STORAGE_HASH; $c++) {
408 $thedir=THIS_CACHE_DIR . "/$a/$b/$c/";
409 $failed|=!@mkdir($thedir, CACHE_STORAGE_PERM);
417 /* This function hashes the cache object and places it in a cache dir. This function also handles the GC probability (note that it is run on only *ONE* dir to save time. */
418 function cache_storage($object, $key) {
419 $newobject=eregi_replace("[^A-Z,0-9,=]", 'X', $object);
420 $newkey=eregi_replace("[^A-Z,0-9,=]", 'X', $key);
421 $temp="${newobject}=${newkey}";
422 if (strlen($temp)>=CACHE_MAX_FILENAME_LEN) $temp="HUGE." . md5($temp);
423 $cacheobject = 'phpCache.' . $temp;
425 $thedir=THIS_CACHE_DIR . '/';
427 if (CACHE_USE_STORAGE_HASH) {
429 $ustr=md5($cacheobject);
430 for ($i=0; $i<3; $i++) {
431 if ($GLOBALS['CACHE_HAS']['crc32']) {
432 $thenum=abs(crc32(substr($ustr,$i,4)))%CACHE_MAX_STORAGE_HASH;
434 $thenum=substr($ustr, $i, 4);
435 $thenum=(ord($thenum[0]) . ord($thenum[1]) . ord($thenum[2]) . ord($thenum[3]))%CACHE_MAX_STORAGE_HASH;
437 $thedir.= $thenum . '/';
442 $r=(mt_rand()%$precision)/$precision;
443 if ($r<=(CACHE_GC/100)) {
447 $theloc = $thedir . $cacheobject;
452 /* Cache garbage collection */
453 function cache_gc($dir=NULL, $start=1, $purgeall=FALSE) {
454 static $dirs=0, $files=0, $deleted=0, $ignored=0, $faileddelete=0, $empty=0;
456 cache_debug("Running GC on $dir");
457 if (!function_exists("getcwd")) {
458 $cwd=substr(`pwd`, 0, -1);
462 $dirs=$files=$deleted=$ignored=$faileddelete=$empty=0;
464 if (cache_iftype($dir, NULL)) $dir=THIS_CACHE_DIR;
467 cache_debug("Error opening $dir for cleanup");
472 while (!cache_iftype(($de=readdir($dp)),FALSE)) {
474 if ($de=='.' || $de=='..') continue;
475 cache_gc($de, 0, $purgeall);
480 if (eregi("^phpCache.", $de)) {
483 $cachestuff=cache_read($absfile);
484 $thecache=unserialize($cachestuff);
485 if (is_array($thecache)) {
486 if ($purgeall || ($cdata["cachetime"]!="0" && $thecache["expire"]<=time())) {
487 cache_lock($absfile, TRUE);
488 if (@unlink($absfile)) {
490 cache_debug("$dir Deleted $absfile");
493 cache_debug("$dir Failed to delete $absfile");
495 cache_lock($absfile, FALSE);
497 cache_debug("$dir $absfile expires in " . ($thecache["expire"]-time()));
500 cache_debug("$dir $absfile is empty, being processed in another process?");
509 $str="$dir GC Processed: $dirs/dirs $files/files $deleted/deleted $ignored/ignored $faileddelete/faileddelete $empty/empty";
516 function cache_iftype($a, $b) {
517 if (gettype($a)==gettype($b) && $a==$b) return TRUE;
521 if (CACHE_ON && !CACHE_STORAGE_CREATED && !@stat(CACHE_STORAGE_CHECKFILE)) {
522 cache_debug('Creating cache storage');
523 cache_create_storage();
524 if (!@touch(CACHE_STORAGE_CHECKFILE)) {
527 $msg->printErrors('CACHE_DIR_BAD');
532 mt_srand(time(NULL));