[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * 4 * @package acm 5 * @version $Id$ 6 * @copyright (c) 2005, 2009 phpBB Group 7 * @license http://opensource.org/licenses/gpl-license.php GNU Public License 8 * 9 */ 10 11 /** 12 * @ignore 13 */ 14 if (!defined('IN_PHPBB')) 15 { 16 exit; 17 } 18 19 /** 20 * ACM File Based Caching 21 * @package acm 22 */ 23 class acm 24 { 25 var $vars = array(); 26 var $var_expires = array(); 27 var $is_modified = false; 28 29 var $sql_rowset = array(); 30 var $sql_row_pointer = array(); 31 var $cache_dir = ''; 32 33 /** 34 * Set cache path 35 */ 36 function acm() 37 { 38 global $phpbb_root_path; 39 $this->cache_dir = $phpbb_root_path . 'cache/'; 40 } 41 42 /** 43 * Load global cache 44 */ 45 function load() 46 { 47 return $this->_read('data_global'); 48 } 49 50 /** 51 * Unload cache object 52 */ 53 function unload() 54 { 55 $this->save(); 56 unset($this->vars); 57 unset($this->var_expires); 58 unset($this->sql_rowset); 59 unset($this->sql_row_pointer); 60 61 $this->vars = array(); 62 $this->var_expires = array(); 63 $this->sql_rowset = array(); 64 $this->sql_row_pointer = array(); 65 } 66 67 /** 68 * Save modified objects 69 */ 70 function save() 71 { 72 if (!$this->is_modified) 73 { 74 return; 75 } 76 77 global $phpEx; 78 79 if (!$this->_write('data_global')) 80 { 81 if (!function_exists('phpbb_is_writable')) 82 { 83 global $phpbb_root_path; 84 include($phpbb_root_path . 'includes/functions.' . $phpEx); 85 } 86 87 // Now, this occurred how often? ... phew, just tell the user then... 88 if (!phpbb_is_writable($this->cache_dir)) 89 { 90 // We need to use die() here, because else we may encounter an infinite loop (the message handler calls $cache->unload()) 91 die('Fatal: ' . $this->cache_dir . ' is NOT writable.'); 92 exit; 93 } 94 95 die('Fatal: Not able to open ' . $this->cache_dir . 'data_global.' . $phpEx); 96 exit; 97 } 98 99 $this->is_modified = false; 100 } 101 102 /** 103 * Tidy cache 104 */ 105 function tidy() 106 { 107 global $phpEx; 108 109 $dir = @opendir($this->cache_dir); 110 111 if (!$dir) 112 { 113 return; 114 } 115 116 $time = time(); 117 118 while (($entry = readdir($dir)) !== false) 119 { 120 if (!preg_match('/^(sql_|data_(?!global))/', $entry)) 121 { 122 continue; 123 } 124 125 if (!($handle = @fopen($this->cache_dir . $entry, 'rb'))) 126 { 127 continue; 128 } 129 130 // Skip the PHP header 131 fgets($handle); 132 133 // Skip expiration 134 $expires = (int) fgets($handle); 135 136 fclose($handle); 137 138 if ($time >= $expires) 139 { 140 $this->remove_file($this->cache_dir . $entry); 141 } 142 } 143 closedir($dir); 144 145 if (file_exists($this->cache_dir . 'data_global.' . $phpEx)) 146 { 147 if (!sizeof($this->vars)) 148 { 149 $this->load(); 150 } 151 152 foreach ($this->var_expires as $var_name => $expires) 153 { 154 if ($time >= $expires) 155 { 156 $this->destroy($var_name); 157 } 158 } 159 } 160 161 set_config('cache_last_gc', time(), true); 162 } 163 164 /** 165 * Get saved cache object 166 */ 167 function get($var_name) 168 { 169 if ($var_name[0] == '_') 170 { 171 global $phpEx; 172 173 if (!$this->_exists($var_name)) 174 { 175 return false; 176 } 177 178 return $this->_read('data' . $var_name); 179 } 180 else 181 { 182 return ($this->_exists($var_name)) ? $this->vars[$var_name] : false; 183 } 184 } 185 186 /** 187 * Put data into cache 188 */ 189 function put($var_name, $var, $ttl = 31536000) 190 { 191 if ($var_name[0] == '_') 192 { 193 $this->_write('data' . $var_name, $var, time() + $ttl); 194 } 195 else 196 { 197 $this->vars[$var_name] = $var; 198 $this->var_expires[$var_name] = time() + $ttl; 199 $this->is_modified = true; 200 } 201 } 202 203 /** 204 * Purge cache data 205 */ 206 function purge() 207 { 208 // Purge all phpbb cache files 209 $dir = @opendir($this->cache_dir); 210 211 if (!$dir) 212 { 213 return; 214 } 215 216 while (($entry = readdir($dir)) !== false) 217 { 218 if (strpos($entry, 'sql_') !== 0 && strpos($entry, 'data_') !== 0 && strpos($entry, 'ctpl_') !== 0 && strpos($entry, 'tpl_') !== 0) 219 { 220 continue; 221 } 222 223 $this->remove_file($this->cache_dir . $entry); 224 } 225 closedir($dir); 226 227 unset($this->vars); 228 unset($this->var_expires); 229 unset($this->sql_rowset); 230 unset($this->sql_row_pointer); 231 232 $this->vars = array(); 233 $this->var_expires = array(); 234 $this->sql_rowset = array(); 235 $this->sql_row_pointer = array(); 236 237 $this->is_modified = false; 238 } 239 240 /** 241 * Destroy cache data 242 */ 243 function destroy($var_name, $table = '') 244 { 245 global $phpEx; 246 247 if ($var_name == 'sql' && !empty($table)) 248 { 249 if (!is_array($table)) 250 { 251 $table = array($table); 252 } 253 254 $dir = @opendir($this->cache_dir); 255 256 if (!$dir) 257 { 258 return; 259 } 260 261 while (($entry = readdir($dir)) !== false) 262 { 263 if (strpos($entry, 'sql_') !== 0) 264 { 265 continue; 266 } 267 268 if (!($handle = @fopen($this->cache_dir . $entry, 'rb'))) 269 { 270 continue; 271 } 272 273 // Skip the PHP header 274 fgets($handle); 275 276 // Skip expiration 277 fgets($handle); 278 279 // Grab the query, remove the LF 280 $query = substr(fgets($handle), 0, -1); 281 282 fclose($handle); 283 284 foreach ($table as $check_table) 285 { 286 // Better catch partial table names than no table names. ;) 287 if (strpos($query, $check_table) !== false) 288 { 289 $this->remove_file($this->cache_dir . $entry); 290 break; 291 } 292 } 293 } 294 closedir($dir); 295 296 return; 297 } 298 299 if (!$this->_exists($var_name)) 300 { 301 return; 302 } 303 304 if ($var_name[0] == '_') 305 { 306 $this->remove_file($this->cache_dir . 'data' . $var_name . ".$phpEx", true); 307 } 308 else if (isset($this->vars[$var_name])) 309 { 310 $this->is_modified = true; 311 unset($this->vars[$var_name]); 312 unset($this->var_expires[$var_name]); 313 314 // We save here to let the following cache hits succeed 315 $this->save(); 316 } 317 } 318 319 /** 320 * Check if a given cache entry exist 321 */ 322 function _exists($var_name) 323 { 324 if ($var_name[0] == '_') 325 { 326 global $phpEx; 327 return file_exists($this->cache_dir . 'data' . $var_name . ".$phpEx"); 328 } 329 else 330 { 331 if (!sizeof($this->vars)) 332 { 333 $this->load(); 334 } 335 336 if (!isset($this->var_expires[$var_name])) 337 { 338 return false; 339 } 340 341 return (time() > $this->var_expires[$var_name]) ? false : isset($this->vars[$var_name]); 342 } 343 } 344 345 /** 346 * Load cached sql query 347 */ 348 function sql_load($query) 349 { 350 // Remove extra spaces and tabs 351 $query = preg_replace('/[\n\r\s\t]+/', ' ', $query); 352 353 if (($rowset = $this->_read('sql_' . md5($query))) === false) 354 { 355 return false; 356 } 357 358 $query_id = sizeof($this->sql_rowset); 359 $this->sql_rowset[$query_id] = $rowset; 360 $this->sql_row_pointer[$query_id] = 0; 361 362 return $query_id; 363 } 364 365 /** 366 * Save sql query 367 */ 368 function sql_save($query, &$query_result, $ttl) 369 { 370 global $db; 371 372 // Remove extra spaces and tabs 373 $query = preg_replace('/[\n\r\s\t]+/', ' ', $query); 374 375 $query_id = sizeof($this->sql_rowset); 376 $this->sql_rowset[$query_id] = array(); 377 $this->sql_row_pointer[$query_id] = 0; 378 379 while ($row = $db->sql_fetchrow($query_result)) 380 { 381 $this->sql_rowset[$query_id][] = $row; 382 } 383 $db->sql_freeresult($query_result); 384 385 if ($this->_write('sql_' . md5($query), $this->sql_rowset[$query_id], $ttl + time(), $query)) 386 { 387 $query_result = $query_id; 388 } 389 } 390 391 /** 392 * Ceck if a given sql query exist in cache 393 */ 394 function sql_exists($query_id) 395 { 396 return isset($this->sql_rowset[$query_id]); 397 } 398 399 /** 400 * Fetch row from cache (database) 401 */ 402 function sql_fetchrow($query_id) 403 { 404 if ($this->sql_row_pointer[$query_id] < sizeof($this->sql_rowset[$query_id])) 405 { 406 return $this->sql_rowset[$query_id][$this->sql_row_pointer[$query_id]++]; 407 } 408 409 return false; 410 } 411 412 /** 413 * Fetch a field from the current row of a cached database result (database) 414 */ 415 function sql_fetchfield($query_id, $field) 416 { 417 if ($this->sql_row_pointer[$query_id] < sizeof($this->sql_rowset[$query_id])) 418 { 419 return (isset($this->sql_rowset[$query_id][$this->sql_row_pointer[$query_id]][$field])) ? $this->sql_rowset[$query_id][$this->sql_row_pointer[$query_id]++][$field] : false; 420 } 421 422 return false; 423 } 424 425 /** 426 * Seek a specific row in an a cached database result (database) 427 */ 428 function sql_rowseek($rownum, $query_id) 429 { 430 if ($rownum >= sizeof($this->sql_rowset[$query_id])) 431 { 432 return false; 433 } 434 435 $this->sql_row_pointer[$query_id] = $rownum; 436 return true; 437 } 438 439 /** 440 * Free memory used for a cached database result (database) 441 */ 442 function sql_freeresult($query_id) 443 { 444 if (!isset($this->sql_rowset[$query_id])) 445 { 446 return false; 447 } 448 449 unset($this->sql_rowset[$query_id]); 450 unset($this->sql_row_pointer[$query_id]); 451 452 return true; 453 } 454 455 /** 456 * Read cached data from a specified file 457 * 458 * @access private 459 * @param string $filename Filename to write 460 * @return mixed False if an error was encountered, otherwise the data type of the cached data 461 */ 462 function _read($filename) 463 { 464 global $phpEx; 465 466 $file = "{$this->cache_dir}$filename.$phpEx"; 467 468 $type = substr($filename, 0, strpos($filename, '_')); 469 470 if (!file_exists($file)) 471 { 472 return false; 473 } 474 475 if (!($handle = @fopen($file, 'rb'))) 476 { 477 return false; 478 } 479 480 // Skip the PHP header 481 fgets($handle); 482 483 if ($filename == 'data_global') 484 { 485 $this->vars = $this->var_expires = array(); 486 487 $time = time(); 488 489 while (($expires = (int) fgets($handle)) && !feof($handle)) 490 { 491 // Number of bytes of data 492 $bytes = substr(fgets($handle), 0, -1); 493 494 if (!is_numeric($bytes) || ($bytes = (int) $bytes) === 0) 495 { 496 // We cannot process the file without a valid number of bytes 497 // so we discard it 498 fclose($handle); 499 500 $this->vars = $this->var_expires = array(); 501 $this->is_modified = false; 502 503 $this->remove_file($file); 504 505 return false; 506 } 507 508 if ($time >= $expires) 509 { 510 fseek($handle, $bytes, SEEK_CUR); 511 512 continue; 513 } 514 515 $var_name = substr(fgets($handle), 0, -1); 516 517 // Read the length of bytes that consists of data. 518 $data = fread($handle, $bytes - strlen($var_name)); 519 $data = @unserialize($data); 520 521 // Don't use the data if it was invalid 522 if ($data !== false) 523 { 524 $this->vars[$var_name] = $data; 525 $this->var_expires[$var_name] = $expires; 526 } 527 528 // Absorb the LF 529 fgets($handle); 530 } 531 532 fclose($handle); 533 534 $this->is_modified = false; 535 536 return true; 537 } 538 else 539 { 540 $data = false; 541 $line = 0; 542 543 while (($buffer = fgets($handle)) && !feof($handle)) 544 { 545 $buffer = substr($buffer, 0, -1); // Remove the LF 546 547 // $buffer is only used to read integers 548 // if it is non numeric we have an invalid 549 // cache file, which we will now remove. 550 if (!is_numeric($buffer)) 551 { 552 break; 553 } 554 555 if ($line == 0) 556 { 557 $expires = (int) $buffer; 558 559 if (time() >= $expires) 560 { 561 break; 562 } 563 564 if ($type == 'sql') 565 { 566 // Skip the query 567 fgets($handle); 568 } 569 } 570 else if ($line == 1) 571 { 572 $bytes = (int) $buffer; 573 574 // Never should have 0 bytes 575 if (!$bytes) 576 { 577 break; 578 } 579 580 // Grab the serialized data 581 $data = fread($handle, $bytes); 582 583 // Read 1 byte, to trigger EOF 584 fread($handle, 1); 585 586 if (!feof($handle)) 587 { 588 // Somebody tampered with our data 589 $data = false; 590 } 591 break; 592 } 593 else 594 { 595 // Something went wrong 596 break; 597 } 598 $line++; 599 } 600 fclose($handle); 601 602 // unserialize if we got some data 603 $data = ($data !== false) ? @unserialize($data) : $data; 604 605 if ($data === false) 606 { 607 $this->remove_file($file); 608 return false; 609 } 610 611 return $data; 612 } 613 } 614 615 /** 616 * Write cache data to a specified file 617 * 618 * 'data_global' is a special case and the generated format is different for this file: 619 * <code> 620 * <?php exit; ?> 621 * (expiration) 622 * (length of var and serialised data) 623 * (var) 624 * (serialised data) 625 * ... (repeat) 626 * </code> 627 * 628 * The other files have a similar format: 629 * <code> 630 * <?php exit; ?> 631 * (expiration) 632 * (query) [SQL files only] 633 * (length of serialised data) 634 * (serialised data) 635 * </code> 636 * 637 * @access private 638 * @param string $filename Filename to write 639 * @param mixed $data Data to store 640 * @param int $expires Timestamp when the data expires 641 * @param string $query Query when caching SQL queries 642 * @return bool True if the file was successfully created, otherwise false 643 */ 644 function _write($filename, $data = null, $expires = 0, $query = '') 645 { 646 global $phpEx; 647 648 $file = "{$this->cache_dir}$filename.$phpEx"; 649 650 if ($handle = @fopen($file, 'wb')) 651 { 652 @flock($handle, LOCK_EX); 653 654 // File header 655 fwrite($handle, '<' . '?php exit; ?' . '>'); 656 657 if ($filename == 'data_global') 658 { 659 // Global data is a different format 660 foreach ($this->vars as $var => $data) 661 { 662 if (strpos($var, "\r") !== false || strpos($var, "\n") !== false) 663 { 664 // CR/LF would cause fgets() to read the cache file incorrectly 665 // do not cache test entries, they probably won't be read back 666 // the cache keys should really be alphanumeric with a few symbols. 667 continue; 668 } 669 $data = serialize($data); 670 671 // Write out the expiration time 672 fwrite($handle, "\n" . $this->var_expires[$var] . "\n"); 673 674 // Length of the remaining data for this var (ignoring two LF's) 675 fwrite($handle, strlen($data . $var) . "\n"); 676 fwrite($handle, $var . "\n"); 677 fwrite($handle, $data); 678 } 679 } 680 else 681 { 682 fwrite($handle, "\n" . $expires . "\n"); 683 684 if (strpos($filename, 'sql_') === 0) 685 { 686 fwrite($handle, $query . "\n"); 687 } 688 $data = serialize($data); 689 690 fwrite($handle, strlen($data) . "\n"); 691 fwrite($handle, $data); 692 } 693 694 @flock($handle, LOCK_UN); 695 fclose($handle); 696 697 if (!function_exists('phpbb_chmod')) 698 { 699 global $phpbb_root_path; 700 include($phpbb_root_path . 'includes/functions.' . $phpEx); 701 } 702 703 phpbb_chmod($file, CHMOD_READ | CHMOD_WRITE); 704 705 return true; 706 } 707 708 return false; 709 } 710 711 /** 712 * Removes/unlinks file 713 */ 714 function remove_file($filename, $check = false) 715 { 716 if (!function_exists('phpbb_is_writable')) 717 { 718 global $phpbb_root_path, $phpEx; 719 include($phpbb_root_path . 'includes/functions.' . $phpEx); 720 } 721 722 if ($check && !phpbb_is_writable($this->cache_dir)) 723 { 724 // E_USER_ERROR - not using language entry - intended. 725 trigger_error('Unable to remove files within ' . $this->cache_dir . '. Please check directory permissions.', E_USER_ERROR); 726 } 727 728 return @unlink($filename); 729 } 730 } 731 732 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Oct 2 15:03:47 2013 | Cross-referenced by PHPXref 0.7.1 |