AC_4897, AC_4898, AC_4899: Multifile uploader fixes.
[acontent.git] / docs / include / phpCache / phpCache.inc.php
1 <?php
2 /*
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>
6
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.
11
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.
16
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.
20 */
21
22 if (defined('CACHE_DIR') && (CACHE_DIR != '')) {
23         define('CACHE_ON', 1); /* disable caching */
24 } else {
25         define('CACHE_ON', 0); /* enable caching */
26 }
27 if (!defined('CACHE_DIR')) {
28         define('CACHE_DIR', '');
29 }
30
31
32         $CACHE_DEBUG = 0;                       /* Default: 0 - Turn debugging on/off */
33
34         define('THIS_CACHE_DIR', CACHE_DIR . '/transformable_cache_' . DB_NAME);
35
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 */
42
43         $CACHE_HAS=array(       'ob_start'      => function_exists('ob_start'),
44                                                 'realpath'      => function_exists('realpath'),
45                                                 'crc32'         => function_exists('crc32')
46                                         );
47
48         define('CACHE_VERSION', '1.4.1');
49         define('CACHE_STORAGE_CHECKFILE',       THIS_CACHE_DIR 
50                                                                                 . '/.phpCache-storage-V'
51                                                                                 . CACHE_VERSION
52                                                                                 . '-HASH='
53                                                                                 . CACHE_USE_STORAGE_HASH);
54
55         define('CACHE_INFO', 'phpCache v1.4.1 By nathan@0x00.org (.1 Bug Fix By joel.kronenberg@utoronto.ca)'); 
56
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;
60
61                 cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile);
62
63                 $cache_pbufferlen = FALSE;
64                 $cache_absfile = NULL;
65                 $cache_data = array();
66                 $cache_fp = NULL;
67                 $cache_expire_cond = NULL;
68                 $cache_variables=array();
69                 $cache_headers=array();
70                 $cache_output_buffer='';
71
72                 cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile);
73         }
74
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;
80                 }
81                 print $str;
82         }
83
84         /* Saves a header state between caching */
85         function cache_header($header) {
86                 global $cache_headers;
87                 Header($header);
88                 cache_debug('Adding header '.$header);
89                 $cache_headers[]=$header;
90         }
91
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) {
94                 extract($vars);
95                 $EXPIRE=FALSE;
96                 eval($cond);
97                 return !!$EXPIRE;
98         }
99
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;
104         }
105
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");
109                 cache_variable($vn);
110         }
111
112         /* duh ? */
113         function cache_debug($s) {
114                 global $CACHE_DEBUG;
115                 if ($CACHE_DEBUG) {
116                         print "Debug: $s<br>\n";
117                 }
118         }
119
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;
125         }
126
127
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);
132         }
133
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);
139                 } else {
140                         $sfn=$SCRIPT_FILENAME;
141                 }
142                 $name="http://$SERVER_NAME/$sfn";
143                 return $name;
144         }
145
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
148                 from the cache! */
149         function cache_all($cachetime=120) {
150                 $key=cache_default_key();
151                 $object=cache_default_object();
152                 return cache($cachetime, $object, $key);
153         }
154
155         /* Same as cache_all() but it throws the session_id() into
156                 the equation */
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);
162         }
163
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);
170                 }
171                 else {
172                         $ret = false;
173                 }
174                 //cache_lock($thefile, FALSE);
175                 return $ret;
176         }
177
178         /* Manually purge all items in the cache */
179         function cache_purge_all() {
180                 return cache_gc(NULL, 1, TRUE);
181         }
182
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);
188                 if (!CACHE_ON) {
189                         cache_debug('Not caching, CACHE_ON is off');
190                         return 0;
191                 }
192                 $curtime=time();
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");
203                                 } else {
204                                         $expireit = FALSE;
205                                         if ($cache_expire_cond) {
206                                                 $expireit=cache_eval_expire($cache_expire_cond, $cdata['variables']);
207                                         }
208                                         if ($cdata['cachetime'] != $cachetime) {
209                                                 cache_debug('Expiring because cachetime changed');
210                                                 $expireit=TRUE;
211                                         }
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");
218                                                                 $GLOBALS[$k]=$v;
219                                                         }
220                                                 }
221                                                 if (is_array($cdata['headers'])) {
222                                                         while(list(,$h)=each($cdata['headers'])) {
223                                                                 cache_debug("Restoring header $h");
224                                                                 Header("$h");
225                                                         }
226                                                 }
227                                                 print $cdata['content'];
228                                                 $ret=$expirein;
229                                                 if ($cdata['cachetime']=='0') $ret='INFINITE';
230                                                 cache_reset();
231                                                 return $ret; 
232                                         }
233                                 }
234                         }
235                 } else {
236                         cache_debug(__LINE__.': Failed to open previous cache of '.$cache_absfile);
237                 }
238         
239                 $oldum = umask();
240                 umask(0077);
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!");
244                         $cache_absfile=NULL;
245                 } else {
246                         cache_debug(__LINE__.': not a symlink');
247                         cache_debug(__LINE__.': Got cache_storage: '.$cache_absfile);
248                         @touch($cache_absfile);
249         
250                         /* cases probs on win32 */
251                         //cache_lock($cache_absfile, TRUE);
252                         /* */
253                 }
254                 umask($oldum);
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;
261
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)) {
266                                 ob_start();
267                         }
268                 } else {
269                         $cache_pbufferlen=FALSE;
270                 }
271                 cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile);
272                 return 0;
273         }
274
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);
280                 if (!CACHE_ON) {
281                         cache_debug('Not caching, CACHE_ON is off');
282                         return 0;
283                 } /* else */
284
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 */
289
290                                 /* JK's fix */
291                                 if ($send_output) {
292                                         ob_end_flush();
293                                         cache_debug(__LINE__.': Content sent. flush()');
294                                 } else {
295                                         ob_end_clean();
296                                         cache_debug(__LINE__.': Content ignored. clean()');
297                                 }
298                                 cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile);
299                         } else {
300                                 /* Output buffering was already on, so get our chunk of data for caching */
301                                 $content=substr($content, $cache_pbufferlen);
302                         }
303                 } else {
304                         $content=$cache_output_buffer;
305                 }
306
307                 if (!$store) {
308                         $cache_absfile=NULL;
309                 }
310
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])) {
318                                         $val=$GLOBALS[$vn];
319                                         cache_debug(__LINE__ . ': Setting variable '.$vn.' to '.$val);
320                                         $variables[$vn]=$val;
321                                 }
322                         }
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);
328                 } else {
329                         cache_debug(__LINE__ .': no variables found');
330                         cache_debug($cache_variables[0]);
331                         cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile);
332                 }
333                 /* casues probs on win32 */
334                 cache_lock($cache_absfile, FALSE);
335                 /* */
336                 cache_reset();
337
338                 cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile);
339                 cache_debug(__LINE__. ': <b>Caching is done!</b><br>');
340         }
341
342         /* Obtain a lock on the cache storage, this can be stripped out
343                 and changed to a different handler like a database or
344                 whatever */
345         function cache_lock($file, $open=TRUE) {
346                 static $fp;
347
348                 if ($open) {
349                         cache_debug('trying to lock '.$file);
350                         $fp  = @fopen($file, 'r');
351                         if ($fp) {
352                                 $ret = @flock($fp, LOCK_SH); /* get a shared lock */
353                         }
354                 } else {
355                         cache_debug('trying to unlock '.$file);
356                         $ret = @flock($fp, LOCK_UN);
357                         @fclose($fp);
358                         $fp = NULL;
359                 }
360                 return $ret;
361         }
362
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);
366                 
367                 $fp = fopen($file, 'wb+');
368                 @flock($fp, LOCK_EX); /* get a shared lock */
369                 if (!$fp) {
370                         cache_debug('Failed to open for write out to '.$file);
371                         return FALSE;
372                 }
373                 @fwrite($fp, $data, strlen($data));
374                 @flock($fp, LOCK_UN); /* get a shared lock */
375                 fclose($fp);
376
377                 return TRUE;
378         }
379
380         /* This function reads in the cache, duh */
381         function cache_read($file) {
382                 $fp = @fopen($file, 'r');
383                 if (!$fp) {
384                         cache_debug(__LINE__.': Failed opening file '.realpath($file));
385                         return NULL;
386                 }
387                 flock($fp, 1);
388                 $buff='';
389                 while (($tmp=fread($fp, 4096))) {
390                         $buff.=$tmp;
391                 }
392                 fclose($fp);
393                 return $buff;
394         }
395
396         /* This function is called automatically by phpCache to create the cache directory structure */
397         function cache_create_storage() {
398                 $failed = 0;
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);
410                                         }
411                                 }
412                         }
413                 }
414                 return TRUE;
415         }
416
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;
424                 
425                 $thedir=THIS_CACHE_DIR . '/';
426
427                 if (CACHE_USE_STORAGE_HASH) {
428                         $chunksize=10;
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;
433                                 } else {
434                                         $thenum=substr($ustr, $i, 4);
435                                         $thenum=(ord($thenum[0]) . ord($thenum[1]) . ord($thenum[2]) . ord($thenum[3]))%CACHE_MAX_STORAGE_HASH;
436                                 }
437                                 $thedir.= $thenum . '/';
438                         }
439                 }
440                 if (CACHE_GC>0) {
441                         $precision=100000;
442                         $r=(mt_rand()%$precision)/$precision;
443                         if ($r<=(CACHE_GC/100)) {
444                                 cache_gc($thedir);
445                         }
446                 }
447                 $theloc = $thedir . $cacheobject;
448
449                 return $theloc;
450         }
451
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;
455                 if ($start==1) {
456                         cache_debug("Running GC on $dir");
457                         if (!function_exists("getcwd")) {
458                                 $cwd=substr(`pwd`, 0, -1);
459                         } else {
460                                 $cwd=getcwd();
461                         }
462                         $dirs=$files=$deleted=$ignored=$faileddelete=$empty=0;
463                 }
464                 if (cache_iftype($dir, NULL)) $dir=THIS_CACHE_DIR;
465                 $dp=opendir($dir);
466                 if (!$dp) {
467                         cache_debug("Error opening $dir for cleanup");
468                         return FALSE;
469                 }
470                 chdir($dir);
471                 $dirs++;
472                 while (!cache_iftype(($de=readdir($dp)),FALSE)) {
473                         if (is_dir($de)) {
474                                 if ($de=='.' || $de=='..') continue;
475                                 cache_gc($de, 0, $purgeall);
476                                 chdir('..');
477                                 continue;
478                         }
479
480                         if (eregi("^phpCache.", $de)) {
481                                 $files++;
482                                 $absfile=$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)) {
489                                                         $deleted++;
490                                                         cache_debug("$dir Deleted $absfile");
491                                                 } else {
492                                                         $faileddelete++;
493                                                         cache_debug("$dir Failed to delete $absfile");
494                                                 }
495                                                 cache_lock($absfile, FALSE);
496                                         } else {
497                                                 cache_debug("$dir $absfile expires in " . ($thecache["expire"]-time()));
498                                         }
499                                 } else {
500                                         cache_debug("$dir $absfile is empty, being processed in another process?");
501                                         $empty++;
502                                 }
503                         } else {
504                                 $ignored++;
505                         }
506                 }
507                 closedir($dp);
508                 if ($start==1) {
509                         $str="$dir GC Processed: $dirs/dirs     $files/files    $deleted/deleted        $ignored/ignored        $faileddelete/faileddelete      $empty/empty";
510                         cache_debug($str);
511                         chdir($cwd);
512                         return $str;
513                 }
514         }
515
516         function cache_iftype($a, $b) {
517                 if (gettype($a)==gettype($b) && $a==$b) return TRUE;
518                 return FALSE;
519         }
520
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)) {
525                         global $msg;
526                 
527                         $msg->printErrors('CACHE_DIR_BAD');
528                         exit;
529                 }
530         }
531
532         mt_srand(time(NULL));
533         cache_reset();
534
535 ?>