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 */
29 $CACHE_DEBUG = 0; /* Default: 0 - Turn debugging on/off */
31 define('THIS_CACHE_DIR', CACHE_DIR . '/atutor_cache_' . DB_NAME);
33 define('CACHE_GC', .10); /* Default: .10 - Probability of garbage collection */
34 define('CACHE_USE_STORAGE_HASH', 0); /* Default: 1 - Use storage hashing. This will increase peformance if you are caching many pages. */
35 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. */
36 define('CACHE_MAX_STORAGE_HASH', 23); /* Don't touch this unless you know what you're doing */
37 define('CACHE_STORAGE_PERM', 0700); /* Default: 0700 - Default permissions for storage directories. */
38 define('CACHE_MAX_FILENAME_LEN', 250); /* How long the cache storage filename can be before it will md5() the entire thing */
40 $CACHE_HAS=array( 'ob_start' => function_exists('ob_start'),
41 'realpath' => function_exists('realpath'),
42 'crc32' => function_exists('crc32')
45 define('CACHE_VERSION', '1.4.1');
46 define('CACHE_STORAGE_CHECKFILE', THIS_CACHE_DIR
47 . '/.phpCache-storage-V'
50 . CACHE_USE_STORAGE_HASH);
52 define('CACHE_INFO', 'phpCache v1.4.1 By nathan@0x00.org (.1 Bug Fix By joel.kronenberg@utoronto.ca)');
54 /* This resets the cache state */
55 function cache_reset() {
56 global $cache_pbufferlen, $cache_absfile, $cache_data, $cache_variables, $cache_headers, $cache_expire_cond, $cache_output_buffer;
58 cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile);
60 $cache_pbufferlen = FALSE;
61 $cache_absfile = NULL;
62 $cache_data = array();
64 $cache_expire_cond = NULL;
65 $cache_variables=array();
66 $cache_headers=array();
67 $cache_output_buffer='';
69 cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile);
72 /* Used to output to the cache output, should only be needed if you dont have output buffering (PHP3) */
73 function cache_output($str) {
74 global $cache_output_buffer;
75 if (!$GLOBALS["CACHE_HAS"]['ob_start']) {
76 $cache_output_buffer.=$str;
81 /* Saves a header state between caching */
82 function cache_header($header) {
83 global $cache_headers;
85 cache_debug('Adding header '.$header);
86 $cache_headers[]=$header;
89 /* 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. */
90 function cache_eval_expire($cond, &$vars) {
97 /* Call this function before a call to cache() to evaluate a dynamic expiration on cache_expire_variable()'s */
98 function cache_expire_if($expr) {
99 global $cache_expire_cond;
100 $cache_expire_cond=$expr;
103 /* Call this function to add a variable to the expire variables store */
104 function cache_expire_variable($vn) {
105 cache_debug("Adding $vn to expire variable store");
110 function cache_debug($s) {
113 print "Debug: $s<br>\n";
117 /* Saves a variable state between caching */
118 function cache_variable($vn) {
119 global $cache_variables;
120 cache_debug(__LINE__ . ": Adding $vn to the variable store");
121 $cache_variables[] = $vn;
125 /* Returns the default key used by the helper functions */
126 function cache_default_key() {
127 global $HTTP_POST_VARS, $HTTP_GET_VARS, $QUERY_STRING;
128 return md5("POST=" . serialize($HTTP_POST_VARS) . " GET=" . serialize($HTTP_GET_VARS) . "QS=" . $QUERY_STRING);
131 /* Returns the default object used by the helper functions */
132 function cache_default_object() {
133 global $REQUEST_URI, $SERVER_NAME, $SCRIPT_FILENAME;
134 if ($GLOBALS["CACHE_HAS"]["realpath"]) {
135 $sfn=realpath($SCRIPT_FILENAME);
137 $sfn=$SCRIPT_FILENAME;
139 $name="http://$SERVER_NAME/$sfn";
143 /* Caches the current page based on the page name and the GET/POST
144 variables. All must match or else it will not be fectched
146 function cache_all($cachetime=120) {
147 $key=cache_default_key();
148 $object=cache_default_object();
149 return cache($cachetime, $object, $key);
152 /* Same as cache_all() but it throws the session_id() into
154 function cache_session($cachetime=120) {
155 global $HTTP_POST_VARS, $HTTP_GET_VARS;
156 $key=cache_default_key() . 'SESSIONID=' . session_id();
157 $object=cache_default_object();
158 return cache($cachetime, $object, $key);
161 /* Manually purge an item in the cache */
162 function cache_purge($object, $key) {
163 $thefile=cache_storage($object, $key);
164 //cache_lock($thefile, TRUE);
165 if (is_file($thefile)) {
166 $ret=@unlink($thefile);
171 //cache_lock($thefile, FALSE);
175 /* Manually purge all items in the cache */
176 function cache_purge_all() {
177 return cache_gc(NULL, 1, TRUE);
180 /* Caches $object based on $key for $cachetime, will return 0 if the
181 object has expired or the object does not exist. */
182 function cache($cachetime, $object, $key=NULL) {
183 global $cache_pbufferlen, $cache_absfile, $cache_file, $cache_data, $cache_expire_cond;
184 cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile);
186 cache_debug('Not caching, CACHE_ON is off');
190 cache_debug(__LINE__.': Caching based on <b>OBJECT</b>='.$object.' <b>KEY</b>='.$key);
191 $cache_absfile=cache_storage($object, $key);
192 cache_debug(__LINE__.': Got cache_storage: '.$cache_absfile);
193 if (($buff=cache_read($cache_absfile))) {
194 cache_debug('Opened the cache file');
195 $cdata=unserialize($buff);
196 if (is_array($cdata)) {
197 $curco = $cdata['cache_object'];
198 if ($curco!=$cache_absfile) {
199 cache_debug("Holy shit that is not my cache file! why? got=$curco wanted=$cache_absfile");
202 if ($cache_expire_cond) {
203 $expireit=cache_eval_expire($cache_expire_cond, $cdata['variables']);
205 if ($cdata['cachetime'] != $cachetime) {
206 cache_debug('Expiring because cachetime changed');
209 if (!$expireit && ($cdata['cachetime']=="0" || $cdata['expire']>=$curtime)) {
210 $expirein=$cdata['expire']-$curtime+1;
211 cache_debug('Cache expires in '.$expirein);
212 if (is_array($cdata['variables'])) {
213 while (list($k,$v)=each($cdata['variables'])) {
214 cache_debug("Restoring variable $k to value $v");
218 if (is_array($cdata['headers'])) {
219 while(list(,$h)=each($cdata['headers'])) {
220 cache_debug("Restoring header $h");
224 print $cdata['content'];
226 if ($cdata['cachetime']=='0') $ret='INFINITE';
233 cache_debug(__LINE__.': Failed to open previous cache of '.$cache_absfile);
238 /* readlink() is not supported on win32, changed to is_link */
239 if (is_link($cache_absfile)) {
240 cache_debug("$cache_absfile is a symlink! not caching!");
243 cache_debug(__LINE__.': not a symlink');
244 cache_debug(__LINE__.': Got cache_storage: '.$cache_absfile);
245 @touch($cache_absfile);
247 /* cases probs on win32 */
248 //cache_lock($cache_absfile, TRUE);
252 $cache_data['expire'] = $curtime + $cachetime;
253 $cache_data['cachetime']= $cachetime;
254 $cache_data['curtime'] = $curtime;
255 $cache_data['version'] = CACHE_VERSION;
256 $cache_data['key'] = $key;
257 $cache_data['object'] = $object;
259 if ($GLOBALS['CACHE_HAS']['ob_start']) {
260 $cache_pbufferlen = ob_get_length();
261 /* If ob_get_length() returns false, output buffering was not on. turn it on. */
262 if (cache_iftype($cache_pbufferlen, FALSE)) {
266 $cache_pbufferlen=FALSE;
268 cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile);
272 /* This *MUST* be at the end of a cache() block or else the cache
273 will not be stored! */
274 function endcache($store=TRUE, $send_output = TRUE) {
275 global $cache_pbufferlen, $cache_absfile, $cache_data, $cache_variables, $cache_headers, $cache_ob_handler, $cache_output_buffer;
276 cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile);
278 cache_debug('Not caching, CACHE_ON is off');
282 if ($GLOBALS[CACHE_HAS]['ob_start']) {
283 $content=ob_get_contents();
284 if (cache_iftype($cache_pbufferlen,FALSE)) {
285 /* Output buffering was off before this, we just need to turn it off again */
290 cache_debug(__LINE__.': Content sent. flush()');
293 cache_debug(__LINE__.': Content ignored. clean()');
295 cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile);
297 /* Output buffering was already on, so get our chunk of data for caching */
298 $content=substr($content, $cache_pbufferlen);
301 $content=$cache_output_buffer;
308 if ($cache_absfile != NULL) {
309 $cache_data['content'] = $content;
310 $variables = array();
311 foreach ($cache_variables as $vn) {
312 //while(list(,$vn)=each($cache_variables)) {
313 cache_debug(__LINE__ . ': Found variable: <b>'.$vn.'</b>');
314 if (isset($GLOBALS[$vn])) {
316 cache_debug(__LINE__ . ': Setting variable '.$vn.' to '.$val);
317 $variables[$vn]=$val;
320 $cache_data['cache_object'] = $cache_absfile;
321 $cache_data['variables'] = $variables;
322 $cache_data['headers'] = $cache_headers;
323 $datas = serialize($cache_data);
324 cache_write($cache_absfile, $datas);
326 cache_debug(__LINE__ .': no variables found');
327 cache_debug($cache_variables[0]);
328 cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile);
330 /* casues probs on win32 */
331 cache_lock($cache_absfile, FALSE);
335 cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile);
336 cache_debug(__LINE__. ': <b>Caching is done!</b><br>');
339 /* Obtain a lock on the cache storage, this can be stripped out
340 and changed to a different handler like a database or
342 function cache_lock($file, $open=TRUE) {
346 cache_debug('trying to lock '.$file);
347 $fp = @fopen($file, 'r');
349 $ret = @flock($fp, LOCK_SH); /* get a shared lock */
352 cache_debug('trying to unlock '.$file);
353 $ret = @flock($fp, LOCK_UN);
360 /* This is the function that writes out the cache */
361 function cache_write($file, $data) {
362 cache_debug(__LINE__.': Writing cache data to file: '.$file);
364 $fp = fopen($file, 'wb+');
365 @flock($fp, LOCK_EX); /* get a shared lock */
367 cache_debug('Failed to open for write out to '.$file);
370 @fwrite($fp, $data, strlen($data));
371 @flock($fp, LOCK_UN); /* get a shared lock */
377 /* This function reads in the cache, duh */
378 function cache_read($file) {
379 $fp = @fopen($file, 'r');
381 cache_debug(__LINE__.': Failed opening file '.realpath($file));
386 while (($tmp=fread($fp, 4096))) {
393 /* This function is called automatically by phpCache to create the cache directory structure */
394 function cache_create_storage() {
396 $failed |= !@mkdir(THIS_CACHE_DIR, CACHE_STORAGE_PERM);
397 if (CACHE_USE_STORAGE_HASH) {
398 for ($a=0; $a<CACHE_MAX_STORAGE_HASH; $a++) {
399 $thedir=THIS_CACHE_DIR . "/$a/";
400 $failed|=!@mkdir($thedir, CACHE_STORAGE_PERM);
401 for ($b=0; $b<CACHE_MAX_STORAGE_HASH; $b++) {
402 $thedir=THIS_CACHE_DIR . "/$a/$b/";
403 $failed|=!@mkdir($thedir, CACHE_STORAGE_PERM);
404 for ($c=0; $c<CACHE_MAX_STORAGE_HASH; $c++) {
405 $thedir=THIS_CACHE_DIR . "/$a/$b/$c/";
406 $failed|=!@mkdir($thedir, CACHE_STORAGE_PERM);
414 /* 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. */
415 function cache_storage($object, $key) {
416 $newobject=eregi_replace("[^A-Z,0-9,=]", 'X', $object);
417 $newkey=eregi_replace("[^A-Z,0-9,=]", 'X', $key);
418 $temp="${newobject}=${newkey}";
419 if (strlen($temp)>=CACHE_MAX_FILENAME_LEN) $temp="HUGE." . md5($temp);
420 $cacheobject = 'phpCache.' . $temp;
422 $thedir=THIS_CACHE_DIR . '/';
424 if (CACHE_USE_STORAGE_HASH) {
426 $ustr=md5($cacheobject);
427 for ($i=0; $i<3; $i++) {
428 if ($GLOBALS['CACHE_HAS']['crc32']) {
429 $thenum=abs(crc32(substr($ustr,$i,4)))%CACHE_MAX_STORAGE_HASH;
431 $thenum=substr($ustr, $i, 4);
432 $thenum=(ord($thenum[0]) . ord($thenum[1]) . ord($thenum[2]) . ord($thenum[3]))%CACHE_MAX_STORAGE_HASH;
434 $thedir.= $thenum . '/';
439 $r=(mt_rand()%$precision)/$precision;
440 if ($r<=(CACHE_GC/100)) {
444 $theloc = $thedir . $cacheobject;
449 /* Cache garbage collection */
450 function cache_gc($dir=NULL, $start=1, $purgeall=FALSE) {
451 static $dirs=0, $files=0, $deleted=0, $ignored=0, $faileddelete=0, $empty=0;
453 cache_debug("Running GC on $dir");
454 if (!function_exists("getcwd")) {
455 $cwd=substr(`pwd`, 0, -1);
459 $dirs=$files=$deleted=$ignored=$faileddelete=$empty=0;
461 if (cache_iftype($dir, NULL)) $dir=THIS_CACHE_DIR;
464 cache_debug("Error opening $dir for cleanup");
469 while (!cache_iftype(($de=readdir($dp)),FALSE)) {
471 if ($de=='.' || $de=='..') continue;
472 cache_gc($de, 0, $purgeall);
477 if (eregi("^phpCache.", $de)) {
480 $cachestuff=cache_read($absfile);
481 $thecache=unserialize($cachestuff);
482 if (is_array($thecache)) {
483 if ($purgeall || ($cdata["cachetime"]!="0" && $thecache["expire"]<=time())) {
484 cache_lock($absfile, TRUE);
485 if (@unlink($absfile)) {
487 cache_debug("$dir Deleted $absfile");
490 cache_debug("$dir Failed to delete $absfile");
492 cache_lock($absfile, FALSE);
494 cache_debug("$dir $absfile expires in " . ($thecache["expire"]-time()));
497 cache_debug("$dir $absfile is empty, being processed in another process?");
506 $str="$dir GC Processed: $dirs/dirs $files/files $deleted/deleted $ignored/ignored $faileddelete/faileddelete $empty/empty";
513 function cache_iftype($a, $b) {
514 if (gettype($a)==gettype($b) && $a==$b) return TRUE;
518 if (CACHE_ON && !CACHE_STORAGE_CREATED && !@stat(CACHE_STORAGE_CHECKFILE)) {
519 cache_debug('Creating cache storage');
520 cache_create_storage();
521 if (!@touch(CACHE_STORAGE_CHECKFILE)) {
524 $msg->printErrors('CACHE_DIR_BAD');
529 mt_srand(time(NULL));