Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

introducing new features

git-svn-id: http://svn.php.net/repository/pear/packages/Cache_Lite/trunk@173392 c90b9560-bf6c-de11-be94-00142212c4b1
  • Loading branch information...
commit eb9ca9fa93a048f1e04197f90b411b70c5e620a4 1 parent 16ad045
Fabien MARTY authored
Showing with 184 additions and 231 deletions.
  1. +165 −58 Lite.php
  2. +0 −166 TODO
  3. +19 −7 package.xml
View
223 Lite.php
@@ -80,6 +80,13 @@ class Cache_Lite
* @var string $_file
*/
var $_file;
+
+ /**
+ * File name (without path)
+ *
+ * @var string $_fileName
+ */
+ var $_fileName;
/**
* Enable / disable write control (the cache is read just after writing to detect corrupt entries)
@@ -197,6 +204,27 @@ class Cache_Lite
*/
var $_automaticSerialization = false;
+ /**
+ * Disable / Tune the automatic cleaning process
+ *
+ * The automatic cleaning process destroy too old (for the given life time)
+ * cache files when a new cache file is written.
+ * 0 => no automatic cache cleaning
+ * 1 => systematic cache cleaning
+ * x (integer) > 1 => automatic cleaning randomly 1 times on x cache write
+ *
+ * @var int $_automaticCleaning
+ */
+ var $_automaticCleaningFactor = 0;
+
+ /**
+ * Nested directory level
+ *
+ *
+ * @var int $_hashedDirectoryLevel
+ */
+ var $_hashedDirectoryLevel = 0;
+
// --- Public methods ---
/**
@@ -217,6 +245,8 @@ class Cache_Lite
* 'memoryCachingLimit' => max nbr of records to store into memory caching (int),
* 'fileNameProtection' => enable / disable automatic file name protection (boolean),
* 'automaticSerialization' => enable / disable automatic serialization (boolean)
+ * 'automaticCleaningFactor' => distable / tune automatic cleaning process (int)
+ * 'hashedDirectoryLevel' => level of the hashed directory system (int)
* );
*
* @param array $options options
@@ -224,7 +254,7 @@ class Cache_Lite
*/
function Cache_Lite($options = array(NULL))
{
- $availableOptions = array('automaticSerialization', 'fileNameProtection', 'memoryCaching', 'onlyMemoryCaching', 'memoryCachingLimit', 'cacheDir', 'caching', 'lifeTime', 'fileLocking', 'writeControl', 'readControl', 'readControlType', 'pearErrorMode');
+ $availableOptions = array('hashedDirectoryLevel', 'automaticCleaningFactor', 'automaticSerialization', 'fileNameProtection', 'memoryCaching', 'onlyMemoryCaching', 'memoryCachingLimit', 'cacheDir', 'caching', 'lifeTime', 'fileLocking', 'writeControl', 'readControl', 'readControlType', 'pearErrorMode');
foreach($options as $key => $value) {
if(in_array($key, $availableOptions)) {
$property = '_'.$key;
@@ -307,6 +337,12 @@ function save($data, $id = NULL, $group = 'default')
return true;
}
}
+ if ($this->_automaticCleaningFactor>0) {
+ $rand = rand(1, $this->_automaticCleaningFactor);
+ if ($rand==1) {
+ $this->clean(false, 'old');
+ }
+ }
if ($this->_writeControl) {
if (!$this->_writeAndControl($data)) {
@touch($this->_file, time() - 2*abs($this->_lifeTime));
@@ -315,8 +351,8 @@ function save($data, $id = NULL, $group = 'default')
return true;
}
} else {
- return $this->_write($data);
- }
+ return $this->_write($data);
+ }
}
return false;
}
@@ -341,11 +377,7 @@ function remove($id, $group = 'default')
return true;
}
}
- if (!@unlink($this->_file)) {
- $this->raiseError('Cache_Lite : Unable to remove cache !', -3);
- return false;
- }
- return true;
+ return $this->_unlink($this->_file);
}
/**
@@ -355,47 +387,15 @@ function remove($id, $group = 'default')
* else only cache files of the specified group will be destroyed
*
* @param string $group name of the cache group
+ * @param string $mode flush cache mode : 'old', 'ingroup', 'notingroup'
* @return boolean true if no problem
* @access public
*/
- function clean($group = false)
+ function clean($group = false, $mode = 'ingroup')
{
- if ($this->_fileNameProtection) {
- $motif = ($group) ? 'cache_'.md5($group).'_' : 'cache_';
- } else {
- $motif = ($group) ? 'cache_'.$group.'_' : 'cache_';
- }
- if ($this->_memoryCaching) {
- while (list($key, $value) = each($this->_memoryCachingArray)) {
- if (strpos($key, $motif, 0)) {
- unset($this->_memoryCachingArray[$key]);
- $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1;
- }
- }
- if ($this->_onlyMemoryCaching) {
- return true;
- }
- }
- if (!($dh = opendir($this->_cacheDir))) {
- $this->raiseError('Cache_Lite : Unable to open cache directory !', -4);
- return false;
- }
- while ($file = readdir($dh)) {
- if (($file != '.') && ($file != '..')) {
- $file = $this->_cacheDir . $file;
- if (is_file($file)) {
- if (strpos($file, $motif, 0)) {
- if (!@unlink($file)) {
- $this->raiseError('Cache_Lite : Unable to remove cache !', -3);
- return false;
- }
- }
- }
- }
- }
- return true;
+ $this->_cleanDir($this->_cacheDir, $group, $mode);
}
-
+
/**
* Set to debug mode
*
@@ -479,11 +479,93 @@ function raiseError($msg, $code)
include_once('PEAR.php');
PEAR::raiseError($msg, $code, $this->_pearErrorMode);
}
-
+
// --- Private methods ---
/**
+ * Remove a file
+ *
+ * @param string $file complete file path and name
+ * @return boolean true if no problem
+ * @access private
+ */
+ function _unlink($file)
+ {
+ if (!@unlink($file)) {
+ $this->raiseError('Cache_Lite : Unable to remove cache !', -3);
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ *
+ * @return boolean true if no problem
+ * @access private
+ */
+ function _cleanDir($dir, $group = false, $mode = 'ingroup')
+ {
+ if ($this->_fileNameProtection) {
+ $motif = ($group) ? 'cache_'.md5($group).'_' : 'cache_';
+ } else {
+ $motif = ($group) ? 'cache_'.$group.'_' : 'cache_';
+ }
+ if ($this->_memoryCaching) {
+ while (list($key, $value) = each($this->_memoryCachingArray)) {
+ if (strpos($key, $motif, 0)) {
+ unset($this->_memoryCachingArray[$key]);
+ $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1;
+ }
+ }
+ if ($this->_onlyMemoryCaching) {
+ return true;
+ }
+ }
+ if (!($dh = opendir($dir))) {
+ $this->raiseError('Cache_Lite : Unable to open cache directory !', -4);
+ return false;
+ }
+ $result = true;
+ while ($file = readdir($dh)) {
+ if (($file != '.') && ($file != '..')) {
+ if (substr($file, 0, 6)=='cache_') {
+ $file2 = $dir . $file;
+ if (is_file($file2)) {
+ switch ($mode) {
+ case 'old':
+ // files older than lifeTime get deleted from cache
+ if ((mktime() - filemtime($file2)) > $this->_lifeTime) {
+ $result = ($result and ($this->_unlink($file2)));
+ }
+ break;
+ case 'notingroup':
+ if (!strpos($file2, $motif, 0)) {
+ $result = ($result and ($this->_unlink($file2)));
+ }
+ break;
+ case 'ingroup':
+ default:
+ if (strpos($file2, $motif, 0)) {
+ $result = ($result and ($this->_unlink($file2)));
+ }
+ break;
+ }
+ }
+ if ((is_dir($file2)) and ($this->_hashedDirectoryLevel>0)) {
+ $result = ($result and ($this->_cleanDir($file2 . '/', $group, $mode)));
+ }
+ }
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Add some date in the memory caching array
*
+ * @param string $id cache id
+ * @param string $data data to cache
* @access private
*/
function _memoryCacheAdd($id, $data)
@@ -506,11 +588,21 @@ function _memoryCacheAdd($id, $data)
*/
function _setFileName($id, $group)
{
+
if ($this->_fileNameProtection) {
- $this->_file = ($this->_cacheDir.'cache_'.md5($group).'_'.md5($id));
+ $suffix = 'cache_'.md5($group).'_'.md5($id);
} else {
- $this->_file = $this->_cacheDir.'cache_'.$group.'_'.$id;
+ $suffix = 'cache_'.$group.'_'.$id;
+ }
+ $root = $this->_cacheDir;
+ if ($this->_hashedDirectoryLevel>0) {
+ $hash = md5($suffix);
+ for ($i=0 ; $i<$this->_hashedDirectoryLevel ; $i++) {
+ $root = $root . 'cache_' . substr($hash, 0, $i + 1) . '/';
+ }
}
+ $this->_fileName = $suffix;
+ $this->_file = $root.$suffix;
}
/**
@@ -558,19 +650,34 @@ function _read()
*/
function _write($data)
{
- $fp = @fopen($this->_file, "wb");
- if ($fp) {
- if ($this->_fileLocking) @flock($fp, LOCK_EX);
- if ($this->_readControl) {
- @fwrite($fp, $this->_hash($data, $this->_readControlType), 32);
+ $try = 1;
+ while ($try<=2) {
+ $fp = @fopen($this->_file, "wb");
+ if ($fp) {
+ if ($this->_fileLocking) @flock($fp, LOCK_EX);
+ if ($this->_readControl) {
+ @fwrite($fp, $this->_hash($data, $this->_readControlType), 32);
+ }
+ $len = strlen($data);
+ @fwrite($fp, $data, $len);
+ if ($this->_fileLocking) @flock($fp, LOCK_UN);
+ @fclose($fp);
+ return true;
+ } else {
+ if (($try==1) and ($this->_hashedDirectoryLevel>0)) {
+ $hash = md5($this->_fileName);
+ $root = $this->_cacheDir;
+ for ($i=0 ; $i<$this->_hashedDirectoryLevel ; $i++) {
+ $root = $root . 'cache_' . substr($hash, 0, $i + 1) . '/';
+ @mkdir($root, 0700);
+ }
+ $try = 2;
+ } else {
+ $try = 999;
+ }
}
- $len = strlen($data);
- @fwrite($fp, $data, $len);
- if ($this->_fileLocking) @flock($fp, LOCK_UN);
- @fclose($fp);
- return true;
}
- $this->raiseError('Cache_Lite : Unable to write cache !', -1);
+ $this->raiseError('Cache_Lite : Unable to write cache file : '.$this->_file, -1);
return false;
}
View
166 TODO
@@ -44,169 +44,3 @@ mode so that multiple readers can access the same resource at the same
time. NOTE: Direct I/O support must be compiled into PHP for these
features to work (--enable-dio).
-------------------------------------------------------------------------
-
-
-
-Another optionnal mode could be (Mike Benoit's interesting idea) :
-(not tested)
-
--------------------------------------------------------------------------
- However recently I ran in to a problem with it and one of the projects
-I'm working on (http://phpgacl.sourceforge.net/). phpGACL caches _many_
-( on the order of 10,000-100,000) very small pieces of data, when I
-tried using Cache Lite for this, my file system didn't like it much
-since there were so many files in a single directory.
-
-So I patched Cache Lite to support a hashed directory structure. ie:
-
-<root cache dir>/<group>/<number between 0-999>/<chopped CRC32>
-
-../default
-../default/081
-../default/081/081162803
-../default/215
-../default/215/215106191
-../default/333
-../default/333/33376174
-../default/366
-../default/366/366703566
-../default/500
-
-I ran a few rough benchmarks, and this method was slower until the cache
-file numbers started growing to over 30,000 or so. Another advantage to
-doing it this way is it's really easy to clear the cache for a specific
-group. :)
-
-The other change I made was to make it so cache data that was read from
-the file system was also stored in memory for subsequent reads. Scripts
-that actually do this should see quite a large increase in performance,
-if the script doesn't actually hit a cache entry more then once, it
-obviously slows things down slightly and uses more memory. Currently it
-ignores cache expire times when it stores data in memory, but in theory
-this shouldn't be a big deal.
-
-<?
-/*
- * phpGACL - Generic Access Control List - Hashed Directory Caching.
- * Copyright (c) 2002-2003 Mike Benoit
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * For questions, help, comments, discussion, etc., please join the
- * phpGACL mailing list. http://sourceforge.net/mail/?group_id=57103
- *
- * You may contact the author of phpGACL by e-mail at:
- * ipso@snappymail.ca
- *
- * The latest version of phpGACL can be obtained from:
- * http://phpgacl.sourceforge.net/
- *
- */
-require_once("Cache_Lite.php");
-
-define('DIR_SEP', DIRECTORY_SEPARATOR);
-
-class Hashed_Cache_Lite extends Cache_Lite
-{
- /**
- * Memory caching variable
- *
- * @var array $_memoryCache
- */
- var $_memoryCache = NULL;
-
- /**
- * Test if a cache is available and (if yes) return it - Original version by Fabien MARTY <fab@php.net>
- *
- * @param string $id cache id
- * @param string $group name of the cache group
- * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested
- * @return string data of the cache (or false if no cache available)
- * @access public
- */
- function get($id, $group = 'default', $doNotTestCacheValidity = false)
- {
- $this->_id = $id;
- $this->_group = $group;
-
- if ($this->_caching) {
- if ($this->_memoryCache[$group.'-'.$id]) {
- return ($this->_memoryCache[$group.'-'.$id]);
- } else {
- $this->_setFileName($id, $group);
- if ($doNotTestCacheValidity) {
- if (file_exists($this->_file)) {
- $this->_memoryCache[$group.'-'.$id] = $this->_read();
- return ( ($this->_memoryCache[$group.'-'.$id]) );
- }
- } else {
- if (@filemtime($this->_file) > $this->_refreshTime) {
- $this->_memoryCache[$group.'-'.$id] = $this->_read();
- return ( ($this->_memoryCache[$group.'-'.$id]) );
- }
- }
- }
- }
- return false;
- }
-
- /**
- * Make a file name (with path)
- *
- * @param string $id cache id
- * @param string $group name of the group
- * @access private
- */
- function _setFileName($id, $group)
- {
- //CRC32 with SUBSTR is still faster then MD5.
- $encoded_id = substr(crc32($id),1);
- //$encoded_id = md5($id);
-
- //Generate just the directory, so it can be created.
- //Groups will have there own top level directory, for quick/easy purging of an entire group.
- $dir = $this->_cacheDir.$group.'/'.substr($encoded_id,0,3);
- $this->_create_dir_structure($dir);
-
- $this->_file = $dir.'/'.$encoded_id;
- }
-
- /**
- * Create full directory structure, Ripped straight from the Smarty Template engine.
- * Version: 2.3.0
- * Copyright: 2001,2002 ispi of Lincoln, Inc.
- *
- * @param string $dir Full directory.
- * @access private
- */
- function _create_dir_structure($dir)
- {
- if (!@file_exists($dir)) {
- $dir_parts = preg_split('!\\'.DIR_SEP.'+!', $dir, -1, PREG_SPLIT_NO_EMPTY);
- $new_dir = ($dir{0} == DIR_SEP) ? DIR_SEP : '';
- foreach ($dir_parts as $dir_part) {
- $new_dir .= $dir_part;
- if (!file_exists($new_dir) && !mkdir($new_dir, 0771)) {
- Cache_Lite::raiseError('Cache_Lite : problem creating directory \"$dir\" !', -3);
- return false;
- }
- $new_dir .= DIR_SEP;
- }
- }
- }
-}
-
-?>
--------------------------------------------------------------------------
View
26 package.xml
@@ -14,15 +14,15 @@
</maintainer>
</maintainers>
<release>
- <version>1.3.1</version>
- <date>2004-08-16</date>
+ <version>1.4.0beta1</version>
+ <date>2004-11-23</date>
<license>lgpl</license>
- <state>stable</state>
+ <state>beta</state>
<notes>
- Bug fixed :
- - problem with clean() method with memoryCaching activated (thanks to Bojan Mihelac (bmihelac at mihelac dot org))
- - remove() method didn't deal with memoryCaching
- - bug fix #1758, thanks to Dave (djpenton at cs dot mu dot oz dot au)
+ New features :
+ - hashed cache directory structure with the choice of the level (thanks to Mike Benoit (ipso at snappymail dot ca))
+ - new options for cleaning the cache ("not in group", "too old"...) (thanks to dontilooksweetandinnocent@ambience.ru)
+ - automatic cleaning of too old cache files based on a customisable "random method"
</notes>
<filelist>
<dir name="/" baseinstalldir="Cache">
@@ -51,6 +51,18 @@
</release>
<changelog>
<release>
+ <version>1.3.1</version>
+ <date>2004-08-16</date>
+ <license>lgpl</license>
+ <state>stable</state>
+ <notes>
+ Bug fixed :
+ - problem with clean() method with memoryCaching activated (thanks to Bojan Mihelac (bmihelac at mihelac dot org))
+ - remove() method didn't deal with memoryCaching
+ - bug fix #1758, thanks to Dave (djpenton at cs dot mu dot oz dot au)
+ </notes>
+ </release>
+ <release>
<version>1.3</version>
<date>2004-02-07</date>
<license>lgpl</license>
Please sign in to comment.
Something went wrong with that request. Please try again.