Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

DbSimple2:

 - added Cache_Lite support

git-svn-id: svn://dklab.ru/lib/DbSimple/trunk@166 78bb956b-1e24-0410-b8d0-c528fdc9eae3
  • Loading branch information...
commit d52a026ecdae81c5966222e747234a8637b53b9a 1 parent 6fff428
tit authored
40 lib/DbSimple/Generic.php
@@ -115,6 +115,26 @@ function& connect($dsn)
115 115 $object->setIdentPrefix($parsed['ident_prefix']);
116 116 }
117 117 $object->setCachePrefix(md5(serialize($parsed['dsn'])));
  118 + if (@fopen('Cache/Lite.php', 'r', true)) {
  119 + $tmp_dirs = array(
  120 + ini_get('session.save_path'),
  121 + '/tmp'
  122 + );
  123 + foreach($tmp_dirs as $dir) {
  124 + $fp = @fopen($dir.'/t', 'w');
  125 + if (is_resource($fp)) {
  126 +
  127 + require_once 'Cache/Lite.php';
  128 + $t = new Cache_Lite(array('cacheDir' => $dir.'/', 'lifeTime' => null, 'automaticSerialization' => true));
  129 + $object->setCacher(&$t);
  130 +
  131 + break;
  132 + }
  133 +
  134 + }
  135 + fclose($fp);
  136 + unlink($dir.'/t');
  137 + }
118 138 return $object;
119 139 }
120 140
@@ -501,10 +521,10 @@ function _query($query, &$total)
501 521 if ($total) {
502 522 $this->_transformQuery($query, 'CALC_TOTAL');
503 523 }
504   -
  524 + $is_cacher_callable = (is_callable($this->_cacher) || (method_exists($this->_cacher, 'get') && method_exists($this->_cacher, 'save')));
505 525 $rows = null;
506 526 $cache_it = false;
507   - if (!empty($this->attributes['CACHE']) && is_callable($this->_cacher)) {
  527 + if (!empty($this->attributes['CACHE']) && $is_cacher_callable) {
508 528
509 529 $hash = $this->_cachePrefix . md5(serialize($query));
510 530 // Getting data from cache if possible
@@ -556,9 +576,9 @@ function _query($query, &$total)
556 576 if ($ttl && $uniq_key == $invalCache) {
557 577 $this->_logQuery($query);
558 578 $this->_logQueryStat($queryTime, $fetchTime, $firstFetchTime, $rows);
  579 +
559 580 }
560 581 else $cache_it = true;
561   -
562 582 }
563 583
564 584 if (null === $rows || true === $cache_it) {
@@ -604,7 +624,7 @@ function _query($query, &$total)
604 624 $result = $this->_transformResult($rows);
605 625
606 626 // Storing data in cache
607   - if ($cache_it && is_callable($this->_cacher)) {
  627 + if ($cache_it && $is_cacher_callable) {
608 628 $this->_cache(
609 629 $hash,
610 630 array(
@@ -1059,13 +1079,19 @@ function _logQueryStat($queryTime, $fetchTime, $firstFetchTime, $rows)
1059 1079 }
1060 1080
1061 1081 /**
1062   - * mixed _cache($hash, $result=null, $ttl=null)
  1082 + * mixed _cache($hash, $result=null)
1063 1083 * Calls cache mechanism if possible.
1064 1084 */
1065   - function _cache($hash, $result=null, $ttl=null)
  1085 + function _cache($hash, $result=null)
1066 1086 {
1067 1087 if (is_callable($this->_cacher))
1068   - return call_user_func($this->_cacher, $hash, $result, $ttl);
  1088 + return call_user_func($this->_cacher, $hash, $result);
  1089 + else if (is_object($this->_cacher) && method_exists($this->_cacher, 'get') && method_exists($this->_cacher, 'save')) {
  1090 + if (null === $result)
  1091 + return $this->_cacher->get($hash);
  1092 + else
  1093 + $this->_cacher->save($result, $hash);
  1094 + }
1069 1095 else return false;
1070 1096 }
1071 1097
824 t/Cache/Lite.php
... ... @@ -0,0 +1,824 @@
  1 +<?php
  2 +
  3 +/**
  4 +* Fast, light and safe Cache Class
  5 +*
  6 +* Cache_Lite is a fast, light and safe cache system. It's optimized
  7 +* for file containers. It is fast and safe (because it uses file
  8 +* locking and/or anti-corruption tests).
  9 +*
  10 +* There are some examples in the 'docs/examples' file
  11 +* Technical choices are described in the 'docs/technical' file
  12 +*
  13 +* Memory Caching is from an original idea of
  14 +* Mike BENOIT <ipso@snappymail.ca>
  15 +*
  16 +* Nota : A chinese documentation (thanks to RainX <china_1982@163.com>) is
  17 +* available at :
  18 +* http://rainx.phpmore.com/manual/cache_lite.html
  19 +*
  20 +* @package Cache_Lite
  21 +* @category Caching
  22 +* @version $Id: Lite.php,v 1.45 2006/06/03 08:10:33 fab Exp $
  23 +* @author Fabien MARTY <fab@php.net>
  24 +*/
  25 +
  26 +define('CACHE_LITE_ERROR_RETURN', 1);
  27 +define('CACHE_LITE_ERROR_DIE', 8);
  28 +
  29 +class Cache_Lite
  30 +{
  31 +
  32 + // --- Private properties ---
  33 +
  34 + /**
  35 + * Directory where to put the cache files
  36 + * (make sure to add a trailing slash)
  37 + *
  38 + * @var string $_cacheDir
  39 + */
  40 + var $_cacheDir = '/tmp/';
  41 +
  42 + /**
  43 + * Enable / disable caching
  44 + *
  45 + * (can be very usefull for the debug of cached scripts)
  46 + *
  47 + * @var boolean $_caching
  48 + */
  49 + var $_caching = true;
  50 +
  51 + /**
  52 + * Cache lifetime (in seconds)
  53 + *
  54 + * If null, the cache is valid forever.
  55 + *
  56 + * @var int $_lifeTime
  57 + */
  58 + var $_lifeTime = 3600;
  59 +
  60 + /**
  61 + * Enable / disable fileLocking
  62 + *
  63 + * (can avoid cache corruption under bad circumstances)
  64 + *
  65 + * @var boolean $_fileLocking
  66 + */
  67 + var $_fileLocking = true;
  68 +
  69 + /**
  70 + * Timestamp of the last valid cache
  71 + *
  72 + * @var int $_refreshTime
  73 + */
  74 + var $_refreshTime;
  75 +
  76 + /**
  77 + * File name (with path)
  78 + *
  79 + * @var string $_file
  80 + */
  81 + var $_file;
  82 +
  83 + /**
  84 + * File name (without path)
  85 + *
  86 + * @var string $_fileName
  87 + */
  88 + var $_fileName;
  89 +
  90 + /**
  91 + * Enable / disable write control (the cache is read just after writing to detect corrupt entries)
  92 + *
  93 + * Enable write control will lightly slow the cache writing but not the cache reading
  94 + * Write control can detect some corrupt cache files but maybe it's not a perfect control
  95 + *
  96 + * @var boolean $_writeControl
  97 + */
  98 + var $_writeControl = true;
  99 +
  100 + /**
  101 + * Enable / disable read control
  102 + *
  103 + * If enabled, a control key is embeded in cache file and this key is compared with the one
  104 + * calculated after the reading.
  105 + *
  106 + * @var boolean $_writeControl
  107 + */
  108 + var $_readControl = true;
  109 +
  110 + /**
  111 + * Type of read control (only if read control is enabled)
  112 + *
  113 + * Available values are :
  114 + * 'md5' for a md5 hash control (best but slowest)
  115 + * 'crc32' for a crc32 hash control (lightly less safe but faster, better choice)
  116 + * 'strlen' for a length only test (fastest)
  117 + *
  118 + * @var boolean $_readControlType
  119 + */
  120 + var $_readControlType = 'crc32';
  121 +
  122 + /**
  123 + * Pear error mode (when raiseError is called)
  124 + *
  125 + * (see PEAR doc)
  126 + *
  127 + * @see setToDebug()
  128 + * @var int $_pearErrorMode
  129 + */
  130 + var $_pearErrorMode = CACHE_LITE_ERROR_RETURN;
  131 +
  132 + /**
  133 + * Current cache id
  134 + *
  135 + * @var string $_id
  136 + */
  137 + var $_id;
  138 +
  139 + /**
  140 + * Current cache group
  141 + *
  142 + * @var string $_group
  143 + */
  144 + var $_group;
  145 +
  146 + /**
  147 + * Enable / Disable "Memory Caching"
  148 + *
  149 + * NB : There is no lifetime for memory caching !
  150 + *
  151 + * @var boolean $_memoryCaching
  152 + */
  153 + var $_memoryCaching = false;
  154 +
  155 + /**
  156 + * Enable / Disable "Only Memory Caching"
  157 + * (be carefull, memory caching is "beta quality")
  158 + *
  159 + * @var boolean $_onlyMemoryCaching
  160 + */
  161 + var $_onlyMemoryCaching = false;
  162 +
  163 + /**
  164 + * Memory caching array
  165 + *
  166 + * @var array $_memoryCachingArray
  167 + */
  168 + var $_memoryCachingArray = array();
  169 +
  170 + /**
  171 + * Memory caching counter
  172 + *
  173 + * @var int $memoryCachingCounter
  174 + */
  175 + var $_memoryCachingCounter = 0;
  176 +
  177 + /**
  178 + * Memory caching limit
  179 + *
  180 + * @var int $memoryCachingLimit
  181 + */
  182 + var $_memoryCachingLimit = 1000;
  183 +
  184 + /**
  185 + * File Name protection
  186 + *
  187 + * if set to true, you can use any cache id or group name
  188 + * if set to false, it can be faster but cache ids and group names
  189 + * will be used directly in cache file names so be carefull with
  190 + * special characters...
  191 + *
  192 + * @var boolean $fileNameProtection
  193 + */
  194 + var $_fileNameProtection = true;
  195 +
  196 + /**
  197 + * Enable / disable automatic serialization
  198 + *
  199 + * it can be used to save directly datas which aren't strings
  200 + * (but it's slower)
  201 + *
  202 + * @var boolean $_serialize
  203 + */
  204 + var $_automaticSerialization = false;
  205 +
  206 + /**
  207 + * Disable / Tune the automatic cleaning process
  208 + *
  209 + * The automatic cleaning process destroy too old (for the given life time)
  210 + * cache files when a new cache file is written.
  211 + * 0 => no automatic cache cleaning
  212 + * 1 => systematic cache cleaning
  213 + * x (integer) > 1 => automatic cleaning randomly 1 times on x cache write
  214 + *
  215 + * @var int $_automaticCleaning
  216 + */
  217 + var $_automaticCleaningFactor = 0;
  218 +
  219 + /**
  220 + * Nested directory level
  221 + *
  222 + * Set the hashed directory structure level. 0 means "no hashed directory
  223 + * structure", 1 means "one level of directory", 2 means "two levels"...
  224 + * This option can speed up Cache_Lite only when you have many thousands of
  225 + * cache file. Only specific benchs can help you to choose the perfect value
  226 + * for you. Maybe, 1 or 2 is a good start.
  227 + *
  228 + * @var int $_hashedDirectoryLevel
  229 + */
  230 + var $_hashedDirectoryLevel = 0;
  231 +
  232 + /**
  233 + * Umask for hashed directory structure
  234 + *
  235 + * @var int $_hashedDirectoryUmask
  236 + */
  237 + var $_hashedDirectoryUmask = 0700;
  238 +
  239 + /**
  240 + * API break for error handling in CACHE_LITE_ERROR_RETURN mode
  241 + *
  242 + * In CACHE_LITE_ERROR_RETURN mode, error handling was not good because
  243 + * for example save() method always returned a boolean (a PEAR_Error object
  244 + * would be better in CACHE_LITE_ERROR_RETURN mode). To correct this without
  245 + * breaking the API, this option (false by default) can change this handling.
  246 + *
  247 + * @var boolean
  248 + */
  249 + var $_errorHandlingAPIBreak = false;
  250 +
  251 + // --- Public methods ---
  252 +
  253 + /**
  254 + * Constructor
  255 + *
  256 + * $options is an assoc. Available options are :
  257 + * $options = array(
  258 + * 'cacheDir' => directory where to put the cache files (string),
  259 + * 'caching' => enable / disable caching (boolean),
  260 + * 'lifeTime' => cache lifetime in seconds (int),
  261 + * 'fileLocking' => enable / disable fileLocking (boolean),
  262 + * 'writeControl' => enable / disable write control (boolean),
  263 + * 'readControl' => enable / disable read control (boolean),
  264 + * 'readControlType' => type of read control 'crc32', 'md5', 'strlen' (string),
  265 + * 'pearErrorMode' => pear error mode (when raiseError is called) (cf PEAR doc) (int),
  266 + * 'memoryCaching' => enable / disable memory caching (boolean),
  267 + * 'onlyMemoryCaching' => enable / disable only memory caching (boolean),
  268 + * 'memoryCachingLimit' => max nbr of records to store into memory caching (int),
  269 + * 'fileNameProtection' => enable / disable automatic file name protection (boolean),
  270 + * 'automaticSerialization' => enable / disable automatic serialization (boolean),
  271 + * 'automaticCleaningFactor' => distable / tune automatic cleaning process (int),
  272 + * 'hashedDirectoryLevel' => level of the hashed directory system (int),
  273 + * 'hashedDirectoryUmask' => umask for hashed directory structure (int),
  274 + * 'errorHandlingAPIBreak' => API break for better error handling ? (boolean)
  275 + * );
  276 + *
  277 + * @param array $options options
  278 + * @access public
  279 + */
  280 + function Cache_Lite($options = array(NULL))
  281 + {
  282 + foreach($options as $key => $value) {
  283 + $this->setOption($key, $value);
  284 + }
  285 + }
  286 +
  287 + /**
  288 + * Generic way to set a Cache_Lite option
  289 + *
  290 + * see Cache_Lite constructor for available options
  291 + *
  292 + * @var string $name name of the option
  293 + * @var mixed $value value of the option
  294 + * @access public
  295 + */
  296 + function setOption($name, $value)
  297 + {
  298 + $availableOptions = array('errorHandlingAPIBreak', 'hashedDirectoryUmask', 'hashedDirectoryLevel', 'automaticCleaningFactor', 'automaticSerialization', 'fileNameProtection', 'memoryCaching', 'onlyMemoryCaching', 'memoryCachingLimit', 'cacheDir', 'caching', 'lifeTime', 'fileLocking', 'writeControl', 'readControl', 'readControlType', 'pearErrorMode');
  299 + if (in_array($name, $availableOptions)) {
  300 + $property = '_'.$name;
  301 + $this->$property = $value;
  302 + }
  303 + }
  304 +
  305 + /**
  306 + * Test if a cache is available and (if yes) return it
  307 + *
  308 + * @param string $id cache id
  309 + * @param string $group name of the cache group
  310 + * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested
  311 + * @return string data of the cache (else : false)
  312 + * @access public
  313 + */
  314 + function get($id, $group = 'default', $doNotTestCacheValidity = false)
  315 + {
  316 + $this->_id = $id;
  317 + $this->_group = $group;
  318 + $data = false;
  319 + if ($this->_caching) {
  320 + $this->_setRefreshTime();
  321 + $this->_setFileName($id, $group);
  322 + clearstatcache();
  323 + if ($this->_memoryCaching) {
  324 + if (isset($this->_memoryCachingArray[$this->_file])) {
  325 + if ($this->_automaticSerialization) {
  326 + return unserialize($this->_memoryCachingArray[$this->_file]);
  327 + }
  328 + return $this->_memoryCachingArray[$this->_file];
  329 + }
  330 + if ($this->_onlyMemoryCaching) {
  331 + return false;
  332 + }
  333 + }
  334 + if (($doNotTestCacheValidity) || (is_null($this->_refreshTime))) {
  335 + if (file_exists($this->_file)) {
  336 + $data = $this->_read();
  337 + }
  338 + } else {
  339 + if ((file_exists($this->_file)) && (@filemtime($this->_file) > $this->_refreshTime)) {
  340 + $data = $this->_read();
  341 + }
  342 + }
  343 + if (($data) and ($this->_memoryCaching)) {
  344 + $this->_memoryCacheAdd($data);
  345 + }
  346 + if (($this->_automaticSerialization) and (is_string($data))) {
  347 + $data = unserialize($data);
  348 + }
  349 + return $data;
  350 + }
  351 + return false;
  352 + }
  353 +
  354 + /**
  355 + * Save some data in a cache file
  356 + *
  357 + * @param string $data data to put in cache (can be another type than strings if automaticSerialization is on)
  358 + * @param string $id cache id
  359 + * @param string $group name of the cache group
  360 + * @return boolean true if no problem (else : false or a PEAR_Error object)
  361 + * @access public
  362 + */
  363 + function save($data, $id = NULL, $group = 'default')
  364 + {
  365 + if ($this->_caching) {
  366 + if ($this->_automaticSerialization) {
  367 + $data = serialize($data);
  368 + }
  369 + if (isset($id)) {
  370 + $this->_setFileName($id, $group);
  371 + }
  372 + if ($this->_memoryCaching) {
  373 + $this->_memoryCacheAdd($data);
  374 + if ($this->_onlyMemoryCaching) {
  375 + return true;
  376 + }
  377 + }
  378 + if ($this->_automaticCleaningFactor>0) {
  379 + $rand = rand(1, $this->_automaticCleaningFactor);
  380 + if ($rand==1) {
  381 + $this->clean(false, 'old');
  382 + }
  383 + }
  384 + if ($this->_writeControl) {
  385 + $res = $this->_writeAndControl($data);
  386 + if (is_bool($res)) {
  387 + if ($res) {
  388 + return true;
  389 + }
  390 + // if $res if false, we need to invalidate the cache
  391 + @touch($this->_file, time() - 2*abs($this->_lifeTime));
  392 + return false;
  393 + }
  394 + } else {
  395 + $res = $this->_write($data);
  396 + }
  397 + if (is_object($res)) {
  398 + // $res is a PEAR_Error object
  399 + if (!($this->_errorHandlingAPIBreak)) {
  400 + return false; // we return false (old API)
  401 + }
  402 + }
  403 + return $res;
  404 + }
  405 + return false;
  406 + }
  407 +
  408 + /**
  409 + * Remove a cache file
  410 + *
  411 + * @param string $id cache id
  412 + * @param string $group name of the cache group
  413 + * @return boolean true if no problem
  414 + * @access public
  415 + */
  416 + function remove($id, $group = 'default')
  417 + {
  418 + $this->_setFileName($id, $group);
  419 + if ($this->_memoryCaching) {
  420 + if (isset($this->_memoryCachingArray[$this->_file])) {
  421 + unset($this->_memoryCachingArray[$this->_file]);
  422 + $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1;
  423 + }
  424 + if ($this->_onlyMemoryCaching) {
  425 + return true;
  426 + }
  427 + }
  428 + return $this->_unlink($this->_file);
  429 + }
  430 +
  431 + /**
  432 + * Clean the cache
  433 + *
  434 + * if no group is specified all cache files will be destroyed
  435 + * else only cache files of the specified group will be destroyed
  436 + *
  437 + * @param string $group name of the cache group
  438 + * @param string $mode flush cache mode : 'old', 'ingroup', 'notingroup',
  439 + * 'callback_myFunction'
  440 + * @return boolean true if no problem
  441 + * @access public
  442 + */
  443 + function clean($group = false, $mode = 'ingroup')
  444 + {
  445 + return $this->_cleanDir($this->_cacheDir, $group, $mode);
  446 + }
  447 +
  448 + /**
  449 + * Set to debug mode
  450 + *
  451 + * When an error is found, the script will stop and the message will be displayed
  452 + * (in debug mode only).
  453 + *
  454 + * @access public
  455 + */
  456 + function setToDebug()
  457 + {
  458 + $this->setOption('pearErrorMode', CACHE_LITE_ERROR_DIE);
  459 + }
  460 +
  461 + /**
  462 + * Set a new life time
  463 + *
  464 + * @param int $newLifeTime new life time (in seconds)
  465 + * @access public
  466 + */
  467 + function setLifeTime($newLifeTime)
  468 + {
  469 + $this->_lifeTime = $newLifeTime;
  470 + $this->_setRefreshTime();
  471 + }
  472 +
  473 + /**
  474 + * Save the state of the caching memory array into a cache file cache
  475 + *
  476 + * @param string $id cache id
  477 + * @param string $group name of the cache group
  478 + * @access public
  479 + */
  480 + function saveMemoryCachingState($id, $group = 'default')
  481 + {
  482 + if ($this->_caching) {
  483 + $array = array(
  484 + 'counter' => $this->_memoryCachingCounter,
  485 + 'array' => $this->_memoryCachingState
  486 + );
  487 + $data = serialize($array);
  488 + $this->save($data, $id, $group);
  489 + }
  490 + }
  491 +
  492 + /**
  493 + * Load the state of the caching memory array from a given cache file cache
  494 + *
  495 + * @param string $id cache id
  496 + * @param string $group name of the cache group
  497 + * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested
  498 + * @access public
  499 + */
  500 + function getMemoryCachingState($id, $group = 'default', $doNotTestCacheValidity = false)
  501 + {
  502 + if ($this->_caching) {
  503 + if ($data = $this->get($id, $group, $doNotTestCacheValidity)) {
  504 + $array = unserialize($data);
  505 + $this->_memoryCachingCounter = $array['counter'];
  506 + $this->_memoryCachingArray = $array['array'];
  507 + }
  508 + }
  509 + }
  510 +
  511 + /**
  512 + * Return the cache last modification time
  513 + *
  514 + * BE CAREFUL : THIS METHOD IS FOR HACKING ONLY !
  515 + *
  516 + * @return int last modification time
  517 + */
  518 + function lastModified()
  519 + {
  520 + return @filemtime($this->_file);
  521 + }
  522 +
  523 + /**
  524 + * Trigger a PEAR error
  525 + *
  526 + * To improve performances, the PEAR.php file is included dynamically.
  527 + * The file is so included only when an error is triggered. So, in most
  528 + * cases, the file isn't included and perfs are much better.
  529 + *
  530 + * @param string $msg error message
  531 + * @param int $code error code
  532 + * @access public
  533 + */
  534 + function raiseError($msg, $code)
  535 + {
  536 + include_once('PEAR.php');
  537 + return PEAR::raiseError($msg, $code, $this->_pearErrorMode);
  538 + }
  539 +
  540 + /**
  541 + * Extend the life of a valid cache file
  542 + *
  543 + * see http://pear.php.net/bugs/bug.php?id=6681
  544 + *
  545 + * @access public
  546 + */
  547 + function extendLife()
  548 + {
  549 + @touch($this->_file);
  550 + }
  551 +
  552 + // --- Private methods ---
  553 +
  554 + /**
  555 + * Compute & set the refresh time
  556 + *
  557 + * @access private
  558 + */
  559 + function _setRefreshTime()
  560 + {
  561 + if (is_null($this->_lifeTime)) {
  562 + $this->_refreshTime = null;
  563 + } else {
  564 + $this->_refreshTime = time() - $this->_lifeTime;
  565 + }
  566 + }
  567 +
  568 + /**
  569 + * Remove a file
  570 + *
  571 + * @param string $file complete file path and name
  572 + * @return boolean true if no problem
  573 + * @access private
  574 + */
  575 + function _unlink($file)
  576 + {
  577 + if (!@unlink($file)) {
  578 + return $this->raiseError('Cache_Lite : Unable to remove cache !', -3);
  579 + }
  580 + return true;
  581 + }
  582 +
  583 + /**
  584 + * Recursive function for cleaning cache file in the given directory
  585 + *
  586 + * @param string $dir directory complete path (with a trailing slash)
  587 + * @param string $group name of the cache group
  588 + * @param string $mode flush cache mode : 'old', 'ingroup', 'notingroup',
  589 + 'callback_myFunction'
  590 + * @return boolean true if no problem
  591 + * @access private
  592 + */
  593 + function _cleanDir($dir, $group = false, $mode = 'ingroup')
  594 + {
  595 + if ($this->_fileNameProtection) {
  596 + $motif = ($group) ? 'cache_'.md5($group).'_' : 'cache_';
  597 + } else {
  598 + $motif = ($group) ? 'cache_'.$group.'_' : 'cache_';
  599 + }
  600 + if ($this->_memoryCaching) {
  601 + while (list($key, ) = each($this->_memoryCachingArray)) {
  602 + if (strpos($key, $motif, 0)) {
  603 + unset($this->_memoryCachingArray[$key]);
  604 + $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1;
  605 + }
  606 + }
  607 + if ($this->_onlyMemoryCaching) {
  608 + return true;
  609 + }
  610 + }
  611 + if (!($dh = opendir($dir))) {
  612 + return $this->raiseError('Cache_Lite : Unable to open cache directory !', -4);
  613 + }
  614 + $result = true;
  615 + while ($file = readdir($dh)) {
  616 + if (($file != '.') && ($file != '..')) {
  617 + if (substr($file, 0, 6)=='cache_') {
  618 + $file2 = $dir . $file;
  619 + if (is_file($file2)) {
  620 + switch (substr($mode, 0, 9)) {
  621 + case 'old':
  622 + // files older than lifeTime get deleted from cache
  623 + if (!is_null($this->_lifeTime)) {
  624 + if ((mktime() - @filemtime($file2)) > $this->_lifeTime) {
  625 + $result = ($result and ($this->_unlink($file2)));
  626 + }
  627 + }
  628 + break;
  629 + case 'notingrou':
  630 + if (!strpos($file2, $motif, 0)) {
  631 + $result = ($result and ($this->_unlink($file2)));
  632 + }
  633 + break;
  634 + case 'callback_':
  635 + $func = substr($mode, 9, strlen($mode) - 9);
  636 + if ($func($file2, $group)) {
  637 + $result = ($result and ($this->_unlink($file2)));
  638 + }
  639 + break;
  640 + case 'ingroup':
  641 + default:
  642 + if (strpos($file2, $motif, 0)) {
  643 + $result = ($result and ($this->_unlink($file2)));
  644 + }
  645 + break;
  646 + }
  647 + }
  648 + if ((is_dir($file2)) and ($this->_hashedDirectoryLevel>0)) {
  649 + $result = ($result and ($this->_cleanDir($file2 . '/', $group, $mode)));
  650 + }
  651 + }
  652 + }
  653 + }
  654 + return $result;
  655 + }
  656 +
  657 + /**
  658 + * Add some date in the memory caching array
  659 + *
  660 + * @param string $data data to cache
  661 + * @access private
  662 + */
  663 + function _memoryCacheAdd($data)
  664 + {
  665 + $this->_memoryCachingArray[$this->_file] = $data;
  666 + if ($this->_memoryCachingCounter >= $this->_memoryCachingLimit) {
  667 + list($key, ) = each($this->_memoryCachingArray);
  668 + unset($this->_memoryCachingArray[$key]);
  669 + } else {
  670 + $this->_memoryCachingCounter = $this->_memoryCachingCounter + 1;
  671 + }
  672 + }
  673 +
  674 + /**
  675 + * Make a file name (with path)
  676 + *
  677 + * @param string $id cache id
  678 + * @param string $group name of the group
  679 + * @access private
  680 + */
  681 + function _setFileName($id, $group)
  682 + {
  683 +
  684 + if ($this->_fileNameProtection) {
  685 + $suffix = 'cache_'.md5($group).'_'.md5($id);
  686 + } else {
  687 + $suffix = 'cache_'.$group.'_'.$id;
  688 + }
  689 + $root = $this->_cacheDir;
  690 + if ($this->_hashedDirectoryLevel>0) {
  691 + $hash = md5($suffix);
  692 + for ($i=0 ; $i<$this->_hashedDirectoryLevel ; $i++) {
  693 + $root = $root . 'cache_' . substr($hash, 0, $i + 1) . '/';
  694 + }
  695 + }
  696 + $this->_fileName = $suffix;
  697 + $this->_file = $root.$suffix;
  698 + }
  699 +
  700 + /**
  701 + * Read the cache file and return the content
  702 + *
  703 + * @return string content of the cache file (else : false or a PEAR_Error object)
  704 + * @access private
  705 + */
  706 + function _read()
  707 + {
  708 + $fp = @fopen($this->_file, "rb");
  709 + if ($this->_fileLocking) @flock($fp, LOCK_SH);
  710 + if ($fp) {
  711 + clearstatcache();
  712 + $length = @filesize($this->_file);
  713 + $mqr = get_magic_quotes_runtime();
  714 + set_magic_quotes_runtime(0);
  715 + if ($this->_readControl) {
  716 + $hashControl = @fread($fp, 32);
  717 + $length = $length - 32;
  718 + }
  719 + if ($length) {
  720 + $data = @fread($fp, $length);
  721 + } else {
  722 + $data = '';
  723 + }
  724 + set_magic_quotes_runtime($mqr);
  725 + if ($this->_fileLocking) @flock($fp, LOCK_UN);
  726 + @fclose($fp);
  727 + if ($this->_readControl) {
  728 + $hashData = $this->_hash($data, $this->_readControlType);
  729 + if ($hashData != $hashControl) {
  730 + if (!(is_null($this->_lifeTime))) {
  731 + @touch($this->_file, time() - 2*abs($this->_lifeTime));
  732 + } else {
  733 + @unlink($this->_file);
  734 + }
  735 + return false;
  736 + }
  737 + }
  738 + return $data;
  739 + }
  740 + return $this->raiseError('Cache_Lite : Unable to read cache !', -2);
  741 + }
  742 +
  743 + /**
  744 + * Write the given data in the cache file
  745 + *
  746 + * @param string $data data to put in cache
  747 + * @return boolean true if ok (a PEAR_Error object else)
  748 + * @access private
  749 + */
  750 + function _write($data)
  751 + {
  752 + if ($this->_hashedDirectoryLevel > 0) {
  753 + $hash = md5($this->_fileName);
  754 + $root = $this->_cacheDir;
  755 + for ($i=0 ; $i<$this->_hashedDirectoryLevel ; $i++) {
  756 + $root = $root . 'cache_' . substr($hash, 0, $i + 1) . '/';
  757 + if (!(@is_dir($root))) {
  758 + @mkdir($root, $this->_hashedDirectoryUmask);
  759 + }
  760 + }
  761 + }
  762 + $fp = @fopen($this->_file, "wb");
  763 + if ($fp) {
  764 + if ($this->_fileLocking) @flock($fp, LOCK_EX);
  765 + if ($this->_readControl) {
  766 + @fwrite($fp, $this->_hash($data, $this->_readControlType), 32);
  767 + }
  768 + $len = strlen($data);
  769 + @fwrite($fp, $data, $len);
  770 + if ($this->_fileLocking) @flock($fp, LOCK_UN);
  771 + @fclose($fp);
  772 + return true;
  773 + }
  774 + return $this->raiseError('Cache_Lite : Unable to write cache file : '.$this->_file, -1);
  775 + }
  776 +
  777 + /**
  778 + * Write the given data in the cache file and control it just after to avoir corrupted cache entries
  779 + *
  780 + * @param string $data data to put in cache
  781 + * @return boolean true if the test is ok (else : false or a PEAR_Error object)
  782 + * @access private
  783 + */
  784 + function _writeAndControl($data)
  785 + {
  786 + $result = $this->_write($data);
  787 + if (is_object($result)) {
  788 + return $result; # We return the PEAR_Error object
  789 + }
  790 + $dataRead = $this->_read();
  791 + if (is_object($dataRead)) {
  792 + return $result; # We return the PEAR_Error object
  793 + }
  794 + if ((is_bool($dataRead)) && (!$dataRead)) {
  795 + return false;
  796 + }
  797 + return ($dataRead==$data);
  798 + }
  799 +
  800 + /**
  801 + * Make a control key with the string containing datas
  802 + *
  803 + * @param string $data data
  804 + * @param string $controlType type of control 'md5', 'crc32' or 'strlen'
  805 + * @return string control key
  806 + * @access private
  807 + */
  808 + function _hash($data, $controlType)
  809 + {
  810 + switch ($controlType) {
  811 + case 'md5':
  812 + return md5($data);
  813 + case 'crc32':
  814 + return sprintf('% 32d', crc32($data));
  815 + case 'strlen':
  816 + return sprintf('% 32d', strlen($data));
  817 + default:
  818 + return $this->raiseError('Unknown controlType ! (available values are only \'md5\', \'crc32\', \'strlen\')', -5);
  819 + }
  820 + }
  821 +
  822 +}
  823 +
  824 +?>
92 t/Cache/Lite/File.php
... ... @@ -0,0 +1,92 @@
  1 +<?php
  2 +
  3 +/**
  4 +* This class extends Cache_Lite and offers a cache system driven by a master file
  5 +*
  6 +* With this class, cache validity is only dependent of a given file. Cache files
  7 +* are valid only if they are older than the master file. It's a perfect way for
  8 +* caching templates results (if the template file is newer than the cache, cache
  9 +* must be rebuild...) or for config classes...
  10 +* There are some examples in the 'docs/examples' file
  11 +* Technical choices are described in the 'docs/technical' file
  12 +*
  13 +* @package Cache_Lite
  14 +* @version $Id: File.php,v 1.3 2005/12/04 16:03:55 fab Exp $
  15 +* @author Fabien MARTY <fab@php.net>
  16 +*/
  17 +
  18 +require_once('Cache/Lite.php');
  19 +
  20 +class Cache_Lite_File extends Cache_Lite
  21 +{
  22 +
  23 + // --- Private properties ---
  24 +
  25 + /**
  26 + * Complete path of the file used for controlling the cache lifetime
  27 + *
  28 + * @var string $_masterFile
  29 + */
  30 + var $_masterFile = '';
  31 +
  32 + /**
  33 + * Masterfile mtime
  34 + *
  35 + * @var int $_masterFile_mtime
  36 + */
  37 + var $_masterFile_mtime = 0;
  38 +
  39 + // --- Public methods ----
  40 +
  41 + /**
  42 + * Constructor
  43 + *
  44 + * $options is an assoc. To have a look at availables options,
  45 + * see the constructor of the Cache_Lite class in 'Cache_Lite.php'
  46 + *
  47 + * Comparing to Cache_Lite constructor, there is another option :
  48 + * $options = array(
  49 + * (...) see Cache_Lite constructor
  50 + * 'masterFile' => complete path of the file used for controlling the cache lifetime(string)
  51 + * );
  52 + *
  53 + * @param array $options options
  54 + * @access public
  55 + */
  56 + function Cache_Lite_File($options = array(NULL))
  57 + {
  58 + $options['lifetime'] = 0;
  59 + $this->Cache_Lite($options);
  60 + if (isset($options['masterFile'])) {
  61 + $this->_masterFile = $options['masterFile'];
  62 + } else {
  63 + return $this->raiseError('Cache_Lite_File : masterFile option must be set !');
  64 + }
  65 + if (!($this->_masterFile_mtime = @filemtime($this->_masterFile))) {
  66 + return $this->raiseError('Cache_Lite_File : Unable to read masterFile : '.$this->_masterFile, -3);
  67 + }
  68 + }
  69 +
  70 + /**
  71 + * Test if a cache is available and (if yes) return it
  72 + *
  73 + * @param string $id cache id
  74 + * @param string $group name of the cache group
  75 + * @return string data of the cache (or false if no cache available)
  76 + * @access public
  77 + */
  78 + function get($id, $group = 'default')
  79 + {
  80 + if ($data = parent::get($id, $group, true)) {
  81 + if ($filemtime = $this->lastModified()) {
  82 + if ($filemtime > $this->_masterFile_mtime) {
  83 + return $data;
  84 + }
  85 + }
  86 + }
  87 + return false;
  88 + }
  89 +
  90 +}
  91 +
  92 +?>
211 t/Cache/Lite/Function.php
... ... @@ -0,0 +1,211 @@
  1 +<?php
  2 +
  3 +/**
  4 +* This class extends Cache_Lite and can be used to cache the result and output of functions/methods
  5 +*
  6 +* This class is completly inspired from Sebastian Bergmann's
  7 +* PEAR/Cache_Function class. This is only an adaptation to
  8 +* Cache_Lite
  9 +*
  10 +* There are some examples in the 'docs/examples' file
  11 +* Technical choices are described in the 'docs/technical' file
  12 +*
  13 +* @package Cache_Lite
  14 +* @version $Id: Function.php,v 1.10 2006/02/04 18:36:36 fab Exp $
  15 +* @author Sebastian BERGMANN <sb@sebastian-bergmann.de>
  16 +* @author Fabien MARTY <fab@php.net>
  17 +*/
  18 +
  19 +require_once('Cache/Lite.php');
  20 +
  21 +class Cache_Lite_Function extends Cache_Lite
  22 +{
  23 +
  24 + // --- Private properties ---
  25 +
  26 + /**
  27 + * Default cache group for function caching
  28 + *
  29 + * @var string $_defaultGroup
  30 + */
  31 + var $_defaultGroup = 'Cache_Lite_Function';
  32 +
  33 + /**
  34 + * Don't cache the method call when its output contains the string "NOCACHE"
  35 + *
  36 + * if set to true, the output of the method will never be displayed (because the output is used
  37 + * to control the cache)
  38 + *
  39 + * @var boolean $_dontCacheWhenTheOutputContainsNOCACHE
  40 + */
  41 + var $_dontCacheWhenTheOutputContainsNOCACHE = false;
  42 +
  43 + /**
  44 + * Don't cache the method call when its result is false
  45 + *
  46 + * @var boolean $_dontCacheWhenTheResultIsFalse
  47 + */
  48 + var $_dontCacheWhenTheResultIsFalse = false;
  49 +
  50 + /**
  51 + * Don't cache the method call when its result is null
  52 + *
  53 + * @var boolean $_dontCacheWhenTheResultIsNull
  54 + */
  55 + var $_dontCacheWhenTheResultIsNull = false;
  56 +
  57 + /**
  58 + * Debug the Cache_Lite_Function caching process
  59 + *
  60 + * @var boolean $_debugCacheLiteFunction
  61 + */
  62 + var $_debugCacheLiteFunction = false;
  63 +
  64 + // --- Public methods ----
  65 +
  66 + /**
  67 + * Constructor
  68 + *
  69 + * $options is an assoc. To have a look at availables options,
  70 + * see the constructor of the Cache_Lite class in 'Cache_Lite.php'
  71 + *
  72 + * Comparing to Cache_Lite constructor, there is another option :
  73 + * $options = array(
  74 + * (...) see Cache_Lite constructor
  75 + * 'debugCacheLiteFunction' => (bool) debug the caching process,
  76 + * 'defaultGroup' => default cache group for function caching (string),
  77 + * 'dontCacheWhenTheOutputContainsNOCACHE' => (bool) don't cache when the function output contains "NOCACHE",
  78 + * 'dontCacheWhenTheResultIsFalse' => (bool) don't cache when the function result is false,
  79 + * 'dontCacheWhenTheResultIsNull' => (bool don't cache when the function result is null
  80 + * );
  81 + *
  82 + * @param array $options options
  83 + * @access public
  84 + */
  85 + function Cache_Lite_Function($options = array(NULL))
  86 + {
  87 + $availableOptions = array('debugCacheLiteFunction', 'defaultGroup', 'dontCacheWhenTheOutputContainsNOCACHE', 'dontCacheWhenTheResultIsFalse', 'dontCacheWhenTheResultIsNull');
  88 + while (list($name, $value) = each($options)) {
  89 + if (in_array($name, $availableOptions)) {
  90 + $property = '_'.$name;
  91 + $this->$property = $value;
  92 + }
  93 + }
  94 + reset($options);
  95 + $this->Cache_Lite($options);
  96 + }
  97 +
  98 + /**
  99 + * Calls a cacheable function or method (or not if there is already a cache for it)
  100 + *
  101 + * Arguments of this method are read with func_get_args. So it doesn't appear
  102 + * in the function definition. Synopsis :
  103 + * call('functionName', $arg1, $arg2, ...)
  104 + * (arg1, arg2... are arguments of 'functionName')
  105 + *
  106 + * @return mixed result of the function/method
  107 + * @access public
  108 + */
  109 + function call()
  110 + {
  111 + $arguments = func_get_args();
  112 + $id = $this->_makeId($arguments);
  113 + $data = $this->get($id, $this->_defaultGroup);
  114 + if ($data !== false) {
  115 + if ($this->_debugCacheLiteFunction) {
  116 + echo "Cache hit !\n";
  117 + }
  118 + $array = unserialize($data);
  119 + $output = $array['output'];
  120 + $result = $array['result'];
  121 + } else {
  122 + if ($this->_debugCacheLiteFunction) {
  123 + echo "Cache missed !\n";
  124 + }
  125 + ob_start();
  126 + ob_implicit_flush(false);
  127 + $target = array_shift($arguments);
  128 + if (is_array($target)) {
  129 + // in this case, $target is for example array($obj, 'method')
  130 + $object = $target[0];
  131 + $method = $target[1];
  132 + $result = call_user_func_array(array(&$object, $method), $arguments);
  133 + } else {
  134 + if (strstr($target, '::')) { // classname::staticMethod
  135 + list($class, $method) = explode('::', $target);
  136 + $result = call_user_func_array(array($class, $method), $arguments);
  137 + } else if (strstr($target, '->')) { // object->method
  138 + // use a stupid name ($objet_123456789 because) of problems where the object
  139 + // name is the same as this var name
  140 + list($object_123456789, $method) = explode('->', $target);
  141 + global $$object_123456789;
  142 + $result = call_user_func_array(array($$object_123456789, $method), $arguments);
  143 + } else { // function
  144 + $result = call_user_func_array($target, $arguments);
  145 + }
  146 + }
  147 + $output = ob_get_contents();
  148 + ob_end_clean();
  149 + if ($this->_dontCacheWhenTheResultIsFalse) {
  150 + if ((is_bool($result)) && (!($result))) {
  151 + echo($output);
  152 + return $result;
  153 + }
  154 + }
  155 + if ($this->_dontCacheWhenTheResultIsNull) {
  156 + if (is_null($result)) {
  157 + echo($output);
  158 + return $result;
  159 + }
  160 + }
  161 + if ($this->_dontCacheWhenTheOutputContainsNOCACHE) {
  162 + if (strpos($output, 'NOCACHE') > -1) {
  163 + return $result;
  164 + }
  165 + }
  166 + $array['output'] = $output;
  167 + $array['result'] = $result;
  168 + $this->save(serialize($array), $id, $this->_defaultGroup);
  169 + }
  170 + echo($output);
  171 + return $result;
  172 + }
  173 +
  174 + /**
  175 + * Drop a cache file
  176 + *
  177 + * Arguments of this method are read with func_get_args. So it doesn't appear
  178 + * in the function definition. Synopsis :
  179 + * remove('functionName', $arg1, $arg2, ...)
  180 + * (arg1, arg2... are arguments of 'functionName')
  181 + *
  182 + * @return boolean true if no problem
  183 + * @access public
  184 + */
  185 + function drop()
  186 + {
  187 + $id = $this->_makeId(func_get_args());
  188 + $this->remove($id, $this->_defaultGroup);
  189 + }
  190 +
  191 + /**
  192 + * Make an id for the cache
  193 + *
  194 + * @var array result of func_get_args for the call() or the remove() method
  195 + * @return string id
  196 + * @access private
  197 + */
  198 + function _makeId($arguments)
  199 + {
  200 + $id = serialize($arguments); // Generate a cache id
  201 + if (!$this->_fileNameProtection) {
  202 + $id = md5($id);
  203 + // if fileNameProtection is set to false, then the id has to be hashed
  204 + // because it's a very bad file name in most cases
  205 + }
  206 + return $id;
  207 + }
  208 +
  209 +}
  210 +
  211 +?>
72 t/Cache/Lite/Output.php
... ... @@ -0,0 +1,72 @@
  1 +<?php
  2