[Svn-src-all:3675] [version-2_12-dev 22587] #2167 汎用のキャッシュ機能

pineray admin @ mail.ec-cube.net
2013年 2月 28日 (木) 15:30:58 JST


Subversion committed to /home/svn/open 22587
http://svn.ec-cube.net/open_trac/changeset/22587
┌────────────────────────────┐
│更新者 :  pineray                                      │
│更新日時:  2013-02-28 15:30:57 +0900 (木, 28  2月 2013)│
└────────────────────────────┘

Log:
--------------------------------------------------------
#2167 汎用のキャッシュ機能

Changed:                      [U:修正,A:追加,D:削除]
--------------------------------------------------------
A   branches/version-2_12-dev/data/class/SC_Cache.php
A   branches/version-2_12-dev/data/class_extends/SC_Cache_Ex.php
A   branches/version-2_12-dev/data/module/Cache/
A   branches/version-2_12-dev/data/module/Cache/Lite/
A   branches/version-2_12-dev/data/module/Cache/Lite/File.php
A   branches/version-2_12-dev/data/module/Cache/Lite/Function.php
A   branches/version-2_12-dev/data/module/Cache/Lite/NestedOutput.php
A   branches/version-2_12-dev/data/module/Cache/Lite/Output.php
A   branches/version-2_12-dev/data/module/Cache/Lite.php

追加: branches/version-2_12-dev/data/class/SC_Cache.php
===================================================================
--- branches/version-2_12-dev/data/class/SC_Cache.php	                        (rev 0)
+++ branches/version-2_12-dev/data/class/SC_Cache.php	2013-02-28 06:30:57 UTC (rev 22587)
@@ -0,0 +1,124 @@
+<?php
+/*
+ * This file is part of EC-CUBE
+ *
+ * Copyright(c) 2000-2013 LOCKON CO.,LTD. All Rights Reserved.
+ *
+ * http://www.lockon.co.jp/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+require DATA_REALDIR . 'module/Cache/Lite.php';
+
+/**
+ * Cache controll using PEAR::Cache_Lite.
+ */
+class SC_Cache
+{
+
+    /**
+     * Instance of PEAR::Cache_Lite class.
+     * @var object 
+     */
+    static $_instance = NULL;
+
+    /**
+     * Default cache lifetime. 
+     */
+    const LIFETIME = MAX_LIFETIME;
+
+    /**
+     * Directory to save cache files.
+     */
+    const CACHEDIR = MASTER_DATA_REALDIR;
+
+    /**
+     * Create Cache_Lite object and set it to static variable.
+     * 
+     * @return void
+     */
+    public static function forge()
+    {
+        $options = array(
+            'cacheDir' => SC_Cache_Ex::CACHEDIR,
+            'lifeTime' => SC_Cache_Ex::LIFETIME,
+            'automaticSerialization' => TRUE
+        );
+        SC_Cache_Ex::$_instance = new Cache_Lite($options);
+    }
+
+    /**
+     * Get Cache_Lite object.
+     *
+     * @return void
+     */
+    public static function getInstance()
+    {
+        is_null(SC_Cache_Ex::$_instance) and SC_Cache_Ex::forge();
+        return SC_Cache_Ex::$_instance;
+    }
+
+    /**
+     * Get data from cache.
+     *
+     * @param   string  $id         cache id
+     * @param   string  $group      name of the cache group
+     * @param   int     $lifeTime   custom lifetime
+     * @return  mixed   data of cache (else : false)
+     */
+    public static function get($id, $group = 'default', $lifeTime = NULL)
+    {
+        $processor = SC_Cache_Ex::getInstance();
+
+        // set custom lifetime.
+        !is_null($lifeTime) and $processor->setOption('lifeTime', $lifeTime);
+
+        $cache = $processor->get($id, $group);
+
+        // set back to default lifetime.
+        !is_null($lifeTime) and $processor->setOption('lifeTime', SC_Cache_Ex::$_lifetime);
+
+        return $cache;
+    }
+
+    /**
+     * Save data into cache.
+     *
+     * @param   mixed   $data   data of cache
+     * @param   string  $id     cache id
+     * @param   string  $group  name of the cache group
+     * @return  void
+     */
+    public static function save($data, $id, $group = 'default')
+    {
+        $processor = SC_Cache_Ex::getInstance();
+
+        $processor->save($data, $id, $group);
+    }
+
+    /**
+     * Clean cache.
+     *
+     * @param string $group name of the cache group
+     * @return void
+     */
+    public static function clean($group = FALSE)
+    {
+        $processor = SC_Cache_Ex::getInstance();
+
+        $processor->clean($group);
+    }
+}

追加: branches/version-2_12-dev/data/class_extends/SC_Cache_Ex.php
===================================================================
--- branches/version-2_12-dev/data/class_extends/SC_Cache_Ex.php	                        (rev 0)
+++ branches/version-2_12-dev/data/class_extends/SC_Cache_Ex.php	2013-02-28 06:30:57 UTC (rev 22587)
@@ -0,0 +1,28 @@
+<?php
+/*
+ * This file is part of EC-CUBE
+ *
+ * Copyright(c) 2000-2013 LOCKON CO.,LTD. All Rights Reserved.
+ *
+ * http://www.lockon.co.jp/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+require_once CLASS_REALDIR . 'SC_Cache.php';
+
+class SC_Cache_Ex extends SC_Cache
+{
+}

追加: branches/version-2_12-dev/data/module/Cache/Lite/File.php
===================================================================
--- branches/version-2_12-dev/data/module/Cache/Lite/File.php	                        (rev 0)
+++ branches/version-2_12-dev/data/module/Cache/Lite/File.php	2013-02-28 06:30:57 UTC (rev 22587)
@@ -0,0 +1,90 @@
+<?php
+
+/**
+* This class extends Cache_Lite and offers a cache system driven by a master file
+*
+* With this class, cache validity is only dependent of a given file. Cache files
+* are valid only if they are older than the master file. It's a perfect way for
+* caching templates results (if the template file is newer than the cache, cache
+* must be rebuild...) or for config classes...
+* There are some examples in the 'docs/examples' file
+* Technical choices are described in the 'docs/technical' file
+*
+* @package Cache_Lite
+* @author Fabien MARTY <fab @ php.net>
+*/
+ 
+require_once('Cache/Lite.php');
+
+class Cache_Lite_File extends Cache_Lite
+{
+
+    // --- Private properties ---
+    
+    /**
+    * Complete path of the file used for controlling the cache lifetime
+    *
+    * @var string $_masterFile
+    */
+    var $_masterFile = '';
+    
+    /**
+    * Masterfile mtime
+    *
+    * @var int $_masterFile_mtime
+    */
+    var $_masterFile_mtime = 0;
+    
+    // --- Public methods ----
+    
+    /**
+    * Constructor
+    *
+    * $options is an assoc. To have a look at availables options,
+    * see the constructor of the Cache_Lite class in 'Cache_Lite.php'
+    *
+    * Comparing to Cache_Lite constructor, there is another option :
+    * $options = array(
+    *     (...) see Cache_Lite constructor
+    *     'masterFile' => complete path of the file used for controlling the cache lifetime(string)
+    * );
+    *
+    * @param array $options options
+    * @access public
+    */
+    function Cache_Lite_File($options = array(NULL))
+    {   
+        $options['lifetime'] = 0;
+        $this->Cache_Lite($options);
+        if (isset($options['masterFile'])) {
+            $this->_masterFile = $options['masterFile'];
+        } else {
+            return $this->raiseError('Cache_Lite_File : masterFile option must be set !');
+        }
+        if (!($this->_masterFile_mtime = @filemtime($this->_masterFile))) {
+            return $this->raiseError('Cache_Lite_File : Unable to read masterFile : '.$this->_masterFile, -3);
+        }
+    }
+    
+    /**
+    * Test if a cache is available and (if yes) return it
+    *
+    * @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 (else : false)
+    * @access public
+    */
+    function get($id, $group = 'default', $doNotTestCacheValidity = false)
+    {
+        if ($data = parent::get($id, $group, true)) {
+            if ($filemtime = $this->lastModified()) {
+                if ($filemtime > $this->_masterFile_mtime) {
+                    return $data;
+                }
+            }
+        }
+        return false;
+    }
+
+}

追加: branches/version-2_12-dev/data/module/Cache/Lite/Function.php
===================================================================
--- branches/version-2_12-dev/data/module/Cache/Lite/Function.php	                        (rev 0)
+++ branches/version-2_12-dev/data/module/Cache/Lite/Function.php	2013-02-28 06:30:57 UTC (rev 22587)
@@ -0,0 +1,208 @@
+<?php
+
+/**
+* This class extends Cache_Lite and can be used to cache the result and output of functions/methods
+*
+* This class is completly inspired from Sebastian Bergmann's
+* PEAR/Cache_Function class. This is only an adaptation to
+* Cache_Lite
+*
+* There are some examples in the 'docs/examples' file
+* Technical choices are described in the 'docs/technical' file
+*
+* @package Cache_Lite
+* @author Sebastian BERGMANN <sb @ sebastian-bergmann.de>
+* @author Fabien MARTY <fab @ php.net>
+*/
+
+require_once('Cache/Lite.php');
+
+class Cache_Lite_Function extends Cache_Lite
+{
+
+    // --- Private properties ---
+
+    /**
+     * Default cache group for function caching
+     *
+     * @var string $_defaultGroup
+     */
+    var $_defaultGroup = 'Cache_Lite_Function';
+
+    /**
+     * Don't cache the method call when its output contains the string "NOCACHE"
+     *
+     * if set to true, the output of the method will never be displayed (because the output is used
+     * to control the cache)
+     *
+     * @var boolean $_dontCacheWhenTheOutputContainsNOCACHE
+     */
+    var $_dontCacheWhenTheOutputContainsNOCACHE = false;
+
+    /**
+     * Don't cache the method call when its result is false
+     *
+     * @var boolean $_dontCacheWhenTheResultIsFalse
+     */
+    var $_dontCacheWhenTheResultIsFalse = false;
+
+    /**
+     * Don't cache the method call when its result is null
+     *
+     * @var boolean $_dontCacheWhenTheResultIsNull
+     */
+    var $_dontCacheWhenTheResultIsNull = false;
+
+    /**
+     * Debug the Cache_Lite_Function caching process
+     *
+     * @var boolean $_debugCacheLiteFunction
+     */
+    var $_debugCacheLiteFunction = false;
+
+    // --- Public methods ----
+
+    /**
+    * Constructor
+    *
+    * $options is an assoc. To have a look at availables options,
+    * see the constructor of the Cache_Lite class in 'Cache_Lite.php'
+    *
+    * Comparing to Cache_Lite constructor, there is another option :
+    * $options = array(
+    *     (...) see Cache_Lite constructor
+    *     'debugCacheLiteFunction' => (bool) debug the caching process,
+    *     'defaultGroup' => default cache group for function caching (string),
+    *     'dontCacheWhenTheOutputContainsNOCACHE' => (bool) don't cache when the function output contains "NOCACHE",
+    *     'dontCacheWhenTheResultIsFalse' => (bool) don't cache when the function result is false,
+    *     'dontCacheWhenTheResultIsNull' => (bool don't cache when the function result is null
+    * );
+    *
+    * @param array $options options
+    * @access public
+    */
+    function Cache_Lite_Function($options = array(NULL))
+    {
+        $availableOptions = array('debugCacheLiteFunction', 'defaultGroup', 'dontCacheWhenTheOutputContainsNOCACHE', 'dontCacheWhenTheResultIsFalse', 'dontCacheWhenTheResultIsNull');
+        while (list($name, $value) = each($options)) {
+            if (in_array($name, $availableOptions)) {
+                $property = '_'.$name;
+                $this->$property = $value;
+            }
+        }
+        reset($options);
+        $this->Cache_Lite($options);
+    }
+
+    /**
+    * Calls a cacheable function or method (or not if there is already a cache for it)
+    *
+    * Arguments of this method are read with func_get_args. So it doesn't appear
+    * in the function definition. Synopsis :
+    * call('functionName', $arg1, $arg2, ...)
+    * (arg1, arg2... are arguments of 'functionName')
+    *
+    * @return mixed result of the function/method
+    * @access public
+    */
+    function call()
+    {
+        $arguments = func_get_args();
+        $id = $this->_makeId($arguments);
+        $data = $this->get($id, $this->_defaultGroup);
+        if ($data !== false) {
+            if ($this->_debugCacheLiteFunction) {
+                echo "Cache hit !\n";
+            }
+            $array = unserialize($data);
+            $output = $array['output'];
+            $result = $array['result'];
+        } else {
+            if ($this->_debugCacheLiteFunction) {
+                echo "Cache missed !\n";
+            }
+            ob_start();
+            ob_implicit_flush(false);
+            $target = array_shift($arguments);
+            if (is_array($target)) {
+                // in this case, $target is for example array($obj, 'method')
+                $object = $target[0];
+                $method = $target[1];
+                $result = call_user_func_array(array(&$object, $method), $arguments);
+            } else {
+                if (strstr($target, '::')) { // classname::staticMethod
+                    list($class, $method) = explode('::', $target);
+                    $result = call_user_func_array(array($class, $method), $arguments);
+                } else if (strstr($target, '->')) { // object->method
+                    // use a stupid name ($objet_123456789 because) of problems where the object
+                    // name is the same as this var name
+                    list($object_123456789, $method) = explode('->', $target);
+                    global $$object_123456789;
+                    $result = call_user_func_array(array($$object_123456789, $method), $arguments);
+                } else { // function
+                    $result = call_user_func_array($target, $arguments);
+                }
+            }
+            $output = ob_get_contents();
+            ob_end_clean();
+            if ($this->_dontCacheWhenTheResultIsFalse) {
+                if ((is_bool($result)) && (!($result))) {
+                    echo($output);
+                    return $result;
+                }
+            }
+            if ($this->_dontCacheWhenTheResultIsNull) {
+                if (is_null($result)) {
+                    echo($output);
+                    return $result;
+                }
+            }
+            if ($this->_dontCacheWhenTheOutputContainsNOCACHE) {
+                if (strpos($output, 'NOCACHE') > -1) {
+                    return $result;
+                }
+            }
+            $array['output'] = $output;
+            $array['result'] = $result;
+            $this->save(serialize($array), $id, $this->_defaultGroup);
+        }
+        echo($output);
+        return $result;
+    }
+
+    /**
+    * Drop a cache file
+    *
+    * Arguments of this method are read with func_get_args. So it doesn't appear
+    * in the function definition. Synopsis :
+    * remove('functionName', $arg1, $arg2, ...)
+    * (arg1, arg2... are arguments of 'functionName')
+    *
+    * @return boolean true if no problem
+    * @access public
+    */
+    function drop()
+    {
+        $id = $this->_makeId(func_get_args());
+        return $this->remove($id, $this->_defaultGroup);
+    }
+
+    /**
+    * Make an id for the cache
+    *
+    * @var array result of func_get_args for the call() or the remove() method
+    * @return string id
+    * @access private
+    */
+    function _makeId($arguments)
+    {
+        $id = serialize($arguments); // Generate a cache id
+        if (!$this->_fileNameProtection) {
+            $id = md5($id);
+            // if fileNameProtection is set to false, then the id has to be hashed
+            // because it's a very bad file name in most cases
+        }
+        return $id;
+    }
+
+}

追加: branches/version-2_12-dev/data/module/Cache/Lite/NestedOutput.php
===================================================================
--- branches/version-2_12-dev/data/module/Cache/Lite/NestedOutput.php	                        (rev 0)
+++ branches/version-2_12-dev/data/module/Cache/Lite/NestedOutput.php	2013-02-28 06:30:57 UTC (rev 22587)
@@ -0,0 +1,56 @@
+<?php
+
+/**
+* This class extends Cache_Lite and uses output buffering to get the data to cache.
+* It supports nesting of caches
+*
+* @package Cache_Lite
+* @author Markus Tacker <tacker @ php.net>
+*/
+
+require_once('Cache/Lite/Output.php');
+
+class Cache_Lite_NestedOutput extends Cache_Lite_Output
+{
+	private $nestedIds = array();
+	private $nestedGroups = array();
+
+    /**
+    * Start the cache
+    *
+    * @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 boolean|string false if the cache is not hit else the data
+    * @access public
+    */
+    function start($id, $group = 'default', $doNotTestCacheValidity = false)
+    {
+    	$this->nestedIds[] = $id;
+    	$this->nestedGroups[] = $group;
+    	$data = $this->get($id, $group, $doNotTestCacheValidity);
+        if ($data !== false) {
+            return $data;
+        }
+        ob_start();
+        ob_implicit_flush(false);
+        return false;
+    }
+
+    /**
+    * Stop the cache
+    *
+    * @param boolen
+    * @return string return contents of cache
+    */
+    function end()
+    {
+        $data = ob_get_contents();
+        ob_end_clean();
+        $id = array_pop($this->nestedIds);
+        $group = array_pop($this->nestedGroups);
+        $this->save($data, $id, $group);
+		return $data;
+    }
+
+}

追加: branches/version-2_12-dev/data/module/Cache/Lite/Output.php
===================================================================
--- branches/version-2_12-dev/data/module/Cache/Lite/Output.php	                        (rev 0)
+++ branches/version-2_12-dev/data/module/Cache/Lite/Output.php	2013-02-28 06:30:57 UTC (rev 22587)
@@ -0,0 +1,68 @@
+<?php
+
+/**
+* This class extends Cache_Lite and uses output buffering to get the data to cache.
+*
+* There are some examples in the 'docs/examples' file
+* Technical choices are described in the 'docs/technical' file
+*
+* @package Cache_Lite
+* @author Fabien MARTY <fab @ php.net>
+*/
+
+require_once('Cache/Lite.php');
+
+class Cache_Lite_Output extends Cache_Lite
+{
+
+    // --- Public methods ---
+
+    /**
+    * Constructor
+    *
+    * $options is an assoc. To have a look at availables options,
+    * see the constructor of the Cache_Lite class in 'Cache_Lite.php'
+    *
+    * @param array $options options
+    * @access public
+    */
+    function Cache_Lite_Output($options)
+    {
+        $this->Cache_Lite($options);
+    }
+
+    /**
+    * Start the cache
+    *
+    * @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 boolean true if the cache is hit (false else)
+    * @access public
+    */
+    function start($id, $group = 'default', $doNotTestCacheValidity = false)
+    {
+        $data = $this->get($id, $group, $doNotTestCacheValidity);
+        if ($data !== false) {
+            echo($data);
+            return true;
+        }
+        ob_start();
+        ob_implicit_flush(false);
+        return false;
+    }
+
+    /**
+    * Stop the cache
+    *
+    * @access public
+    */
+    function end()
+    {
+        $data = ob_get_contents();
+        ob_end_clean();
+        $this->save($data, $this->_id, $this->_group);
+        echo($data);
+    }
+
+}

追加: branches/version-2_12-dev/data/module/Cache/Lite.php
===================================================================
--- branches/version-2_12-dev/data/module/Cache/Lite.php	                        (rev 0)
+++ branches/version-2_12-dev/data/module/Cache/Lite.php	2013-02-28 06:30:57 UTC (rev 22587)
@@ -0,0 +1,848 @@
+<?php
+
+/**
+* Fast, light and safe Cache Class
+*
+* Cache_Lite is a fast, light and safe cache system. It's optimized
+* for file containers. It is fast and safe (because it uses file
+* locking and/or anti-corruption tests).
+*
+* There are some examples in the 'docs/examples' file
+* Technical choices are described in the 'docs/technical' file
+*
+* Memory Caching is from an original idea of
+* Mike BENOIT <ipso @ snappymail.ca>
+*
+* Nota : A chinese documentation (thanks to RainX <china_1982 @ 163.com>) is
+* available at :
+* http://rainx.phpmore.com/manual/cache_lite.html
+*
+* @package Cache_Lite
+* @category Caching
+* @author Fabien MARTY <fab @ php.net>
+* @author Markus Tacker <tacker @ php.net>
+*/
+
+define('CACHE_LITE_ERROR_RETURN', 1);
+define('CACHE_LITE_ERROR_DIE', 8);
+
+class Cache_Lite
+{
+
+    // --- Private properties ---
+
+    /**
+    * Directory where to put the cache files
+    * (make sure to add a trailing slash)
+    *
+    * @var string $_cacheDir
+    */
+    var $_cacheDir = '/tmp/';
+
+    /**
+    * Enable / disable caching
+    *
+    * (can be very usefull for the debug of cached scripts)
+    *
+    * @var boolean $_caching
+    */
+    var $_caching = true;
+
+    /**
+    * Cache lifetime (in seconds)
+    *
+    * If null, the cache is valid forever.
+    *
+    * @var int $_lifeTime
+    */
+    var $_lifeTime = 3600;
+
+    /**
+    * Enable / disable fileLocking
+    *
+    * (can avoid cache corruption under bad circumstances)
+    *
+    * @var boolean $_fileLocking
+    */
+    var $_fileLocking = true;
+
+    /**
+    * Timestamp of the last valid cache
+    *
+    * @var int $_refreshTime
+    */
+    var $_refreshTime;
+
+    /**
+    * File name (with path)
+    *
+    * @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)
+    *
+    * Enable write control will lightly slow the cache writing but not the cache reading
+    * Write control can detect some corrupt cache files but maybe it's not a perfect control
+    *
+    * @var boolean $_writeControl
+    */
+    var $_writeControl = true;
+
+    /**
+    * Enable / disable read control
+    *
+    * If enabled, a control key is embeded in cache file and this key is compared with the one
+    * calculated after the reading.
+    *
+    * @var boolean $_writeControl
+    */
+    var $_readControl = true;
+
+    /**
+    * Type of read control (only if read control is enabled)
+    *
+    * Available values are :
+    * 'md5' for a md5 hash control (best but slowest)
+    * 'crc32' for a crc32 hash control (lightly less safe but faster, better choice)
+    * 'strlen' for a length only test (fastest)
+    *
+    * @var boolean $_readControlType
+    */
+    var $_readControlType = 'crc32';
+
+    /**
+    * Pear error mode (when raiseError is called)
+    *
+    * (see PEAR doc)
+    *
+    * @see setToDebug()
+    * @var int $_pearErrorMode
+    */
+    var $_pearErrorMode = CACHE_LITE_ERROR_RETURN;
+    
+    /**
+    * Current cache id
+    *
+    * @var string $_id
+    */
+    var $_id;
+
+    /**
+    * Current cache group
+    *
+    * @var string $_group
+    */
+    var $_group;
+
+    /**
+    * Enable / Disable "Memory Caching"
+    *
+    * NB : There is no lifetime for memory caching ! 
+    *
+    * @var boolean $_memoryCaching
+    */
+    var $_memoryCaching = false;
+
+    /**
+    * Enable / Disable "Only Memory Caching"
+    * (be carefull, memory caching is "beta quality")
+    *
+    * @var boolean $_onlyMemoryCaching
+    */
+    var $_onlyMemoryCaching = false;
+
+    /**
+    * Memory caching array
+    *
+    * @var array $_memoryCachingArray
+    */
+    var $_memoryCachingArray = array();
+
+    /**
+    * Memory caching counter
+    *
+    * @var int $memoryCachingCounter
+    */
+    var $_memoryCachingCounter = 0;
+
+    /**
+    * Memory caching limit
+    *
+    * @var int $memoryCachingLimit
+    */
+    var $_memoryCachingLimit = 1000;
+    
+    /**
+    * File Name protection
+    *
+    * if set to true, you can use any cache id or group name
+    * if set to false, it can be faster but cache ids and group names
+    * will be used directly in cache file names so be carefull with
+    * special characters...
+    *
+    * @var boolean $fileNameProtection
+    */
+    var $_fileNameProtection = true;
+    
+    /**
+    * Enable / disable automatic serialization
+    *
+    * it can be used to save directly datas which aren't strings
+    * (but it's slower)    
+    *
+    * @var boolean $_serialize
+    */
+    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
+    *
+    * Set the hashed directory structure level. 0 means "no hashed directory 
+    * structure", 1 means "one level of directory", 2 means "two levels"... 
+    * This option can speed up Cache_Lite only when you have many thousands of 
+    * cache file. Only specific benchs can help you to choose the perfect value 
+    * for you. Maybe, 1 or 2 is a good start.
+    *
+    * @var int $_hashedDirectoryLevel
+    */
+    var $_hashedDirectoryLevel = 0;
+    
+    /**
+    * Umask for hashed directory structure
+    *
+    * @var int $_hashedDirectoryUmask
+    */
+    var $_hashedDirectoryUmask = 0700;
+    
+    /**
+     * API break for error handling in CACHE_LITE_ERROR_RETURN mode
+     * 
+     * In CACHE_LITE_ERROR_RETURN mode, error handling was not good because
+     * for example save() method always returned a boolean (a PEAR_Error object
+     * would be better in CACHE_LITE_ERROR_RETURN mode). To correct this without
+     * breaking the API, this option (false by default) can change this handling.
+     * 
+     * @var boolean
+     */
+    var $_errorHandlingAPIBreak = false;
+    
+    // --- Public methods ---
+
+    /**
+    * Constructor
+    *
+    * $options is an assoc. Available options are :
+    * $options = array(
+    *     'cacheDir' => directory where to put the cache files (string),
+    *     'caching' => enable / disable caching (boolean),
+    *     'lifeTime' => cache lifetime in seconds (int),
+    *     'fileLocking' => enable / disable fileLocking (boolean),
+    *     'writeControl' => enable / disable write control (boolean),
+    *     'readControl' => enable / disable read control (boolean),
+    *     'readControlType' => type of read control 'crc32', 'md5', 'strlen' (string),
+    *     'pearErrorMode' => pear error mode (when raiseError is called) (cf PEAR doc) (int),
+    *     'memoryCaching' => enable / disable memory caching (boolean),
+    *     'onlyMemoryCaching' => enable / disable only memory caching (boolean),
+    *     '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),
+    *     'hashedDirectoryUmask' => umask for hashed directory structure (int),
+    *     'errorHandlingAPIBreak' => API break for better error handling ? (boolean)
+    * );
+    * 
+    * If sys_get_temp_dir() is available and the 
+    * 'cacheDir' option is not provided in the 
+    * constructor options array its output is used 
+    * to determine the suitable temporary directory.
+    * 
+    * @see http://de.php.net/sys_get_temp_dir
+    * @see http://pear.php.net/bugs/bug.php?id=18328
+    *
+    * @param array $options options
+    * @access public
+    */
+    function Cache_Lite($options = array(NULL))
+    {
+        foreach($options as $key => $value) {
+            $this->setOption($key, $value);
+        }
+        if (!isset($options['cacheDir']) && function_exists('sys_get_temp_dir')) {
+        	$this->setOption('cacheDir', sys_get_temp_dir() . DIRECTORY_SEPARATOR);
+        }
+    }
+    
+    /**
+    * Generic way to set a Cache_Lite option
+    *
+    * see Cache_Lite constructor for available options
+    *
+    * @var string $name name of the option
+    * @var mixed $value value of the option
+    * @access public
+    */
+    function setOption($name, $value) 
+    {
+        $availableOptions = array('errorHandlingAPIBreak', 'hashedDirectoryUmask', 'hashedDirectoryLevel', 'automaticCleaningFactor', 'automaticSerialization', 'fileNameProtection', 'memoryCaching', 'onlyMemoryCaching', 'memoryCachingLimit', 'cacheDir', 'caching', 'lifeTime', 'fileLocking', 'writeControl', 'readControl', 'readControlType', 'pearErrorMode');
+        if (in_array($name, $availableOptions)) {
+            $property = '_'.$name;
+            $this->$property = $value;
+        }
+    }
+    
+    /**
+    * Test if a cache is available and (if yes) return it
+    *
+    * @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 (else : false)
+    * @access public
+    */
+    function get($id, $group = 'default', $doNotTestCacheValidity = false)
+    {
+        $this->_id = $id;
+        $this->_group = $group;
+        $data = false;
+        if ($this->_caching) {
+            $this->_setRefreshTime();
+            $this->_setFileName($id, $group);
+            clearstatcache();
+            if ($this->_memoryCaching) {
+                if (isset($this->_memoryCachingArray[$this->_file])) {
+                    if ($this->_automaticSerialization) {
+                        return unserialize($this->_memoryCachingArray[$this->_file]);
+                    }
+                    return $this->_memoryCachingArray[$this->_file];
+                }
+                if ($this->_onlyMemoryCaching) {
+                    return false;
+                }                
+            }
+            if (($doNotTestCacheValidity) || (is_null($this->_refreshTime))) {
+                if (file_exists($this->_file)) {
+                    $data = $this->_read();
+                }
+            } else {
+                if ((file_exists($this->_file)) && (@filemtime($this->_file) > $this->_refreshTime)) {
+                    $data = $this->_read();
+                }
+            }
+            if (($data) and ($this->_memoryCaching)) {
+                $this->_memoryCacheAdd($data);
+            }
+            if (($this->_automaticSerialization) and (is_string($data))) {
+                $data = unserialize($data);
+            }
+            return $data;
+        }
+        return false;
+    }
+    
+    /**
+    * Save some data in a cache file
+    *
+    * @param string $data data to put in cache (can be another type than strings if automaticSerialization is on)
+    * @param string $id cache id
+    * @param string $group name of the cache group
+    * @return boolean true if no problem (else : false or a PEAR_Error object)
+    * @access public
+    */
+    function save($data, $id = NULL, $group = 'default')
+    {
+        if ($this->_caching) {
+            if ($this->_automaticSerialization) {
+                $data = serialize($data);
+            }
+            if (isset($id)) {
+                $this->_setFileName($id, $group);
+            }
+            if ($this->_memoryCaching) {
+                $this->_memoryCacheAdd($data);
+                if ($this->_onlyMemoryCaching) {
+                    return true;
+                }
+            }
+            if ($this->_automaticCleaningFactor>0 && ($this->_automaticCleaningFactor==1 || mt_rand(1, $this->_automaticCleaningFactor)==1)) {
+				$this->clean(false, 'old');			
+			}
+            if ($this->_writeControl) {
+                $res = $this->_writeAndControl($data);
+                if (is_bool($res)) {
+                    if ($res) {
+                        return true;  
+                    }
+                    // if $res if false, we need to invalidate the cache
+                    @touch($this->_file, time() - 2*abs($this->_lifeTime));
+                    return false;
+                }            
+            } else {
+                $res = $this->_write($data);
+            }
+            if (is_object($res)) {
+                // $res is a PEAR_Error object 
+                if (!($this->_errorHandlingAPIBreak)) {   
+                    return false; // we return false (old API)
+                }
+            }
+            return $res;
+        }
+        return false;
+    }
+
+    /**
+    * Remove a cache file
+    *
+    * @param string $id cache id
+    * @param string $group name of the cache group
+    * @param boolean $checkbeforeunlink check if file exists before removing it
+    * @return boolean true if no problem
+    * @access public
+    */
+    function remove($id, $group = 'default', $checkbeforeunlink = false)
+    {
+        $this->_setFileName($id, $group);
+        if ($this->_memoryCaching) {
+            if (isset($this->_memoryCachingArray[$this->_file])) {
+                unset($this->_memoryCachingArray[$this->_file]);
+                $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1;
+            }
+            if ($this->_onlyMemoryCaching) {
+                return true;
+            }
+        }
+        if ( $checkbeforeunlink ) {
+            if (!file_exists($this->_file)) return true;
+        }
+        return $this->_unlink($this->_file);
+    }
+
+    /**
+    * Clean the cache
+    *
+    * if no group is specified all cache files will be destroyed
+    * 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', 
+    *                                        'callback_myFunction'
+    * @return boolean true if no problem
+    * @access public
+    */
+    function clean($group = false, $mode = 'ingroup')
+    {
+        return $this->_cleanDir($this->_cacheDir, $group, $mode);
+    }
+       
+    /**
+    * Set to debug mode
+    *
+    * When an error is found, the script will stop and the message will be displayed
+    * (in debug mode only). 
+    *
+    * @access public
+    */
+    function setToDebug()
+    {
+        $this->setOption('pearErrorMode', CACHE_LITE_ERROR_DIE);
+    }
+
+    /**
+    * Set a new life time
+    *
+    * @param int $newLifeTime new life time (in seconds)
+    * @access public
+    */
+    function setLifeTime($newLifeTime)
+    {
+        $this->_lifeTime = $newLifeTime;
+        $this->_setRefreshTime();
+    }
+
+    /**
+    * Save the state of the caching memory array into a cache file cache
+    *
+    * @param string $id cache id
+    * @param string $group name of the cache group
+    * @access public
+    */
+    function saveMemoryCachingState($id, $group = 'default')
+    {
+        if ($this->_caching) {
+            $array = array(
+                'counter' => $this->_memoryCachingCounter,
+                'array' => $this->_memoryCachingArray
+            );
+            $data = serialize($array);
+            $this->save($data, $id, $group);
+        }
+    }
+
+    /**
+    * Load the state of the caching memory array from a given cache file cache
+    *
+    * @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
+    * @access public
+    */
+    function getMemoryCachingState($id, $group = 'default', $doNotTestCacheValidity = false)
+    {
+        if ($this->_caching) {
+            if ($data = $this->get($id, $group, $doNotTestCacheValidity)) {
+                $array = unserialize($data);
+                $this->_memoryCachingCounter = $array['counter'];
+                $this->_memoryCachingArray = $array['array'];
+            }
+        }
+    }
+    
+    /**
+    * Return the cache last modification time
+    *
+    * BE CAREFUL : THIS METHOD IS FOR HACKING ONLY !
+    *
+    * @return int last modification time
+    */
+    function lastModified() 
+    {
+        return @filemtime($this->_file);
+    }
+    
+    /**
+    * Trigger a PEAR error
+    *
+    * To improve performances, the PEAR.php file is included dynamically.
+    * The file is so included only when an error is triggered. So, in most
+    * cases, the file isn't included and perfs are much better.
+    *
+    * @param string $msg error message
+    * @param int $code error code
+    * @access public
+    */
+    function raiseError($msg, $code)
+    {
+        include_once('PEAR.php');
+        return PEAR::raiseError($msg, $code, $this->_pearErrorMode);
+    }
+    
+    /**
+     * Extend the life of a valid cache file
+     * 
+     * see http://pear.php.net/bugs/bug.php?id=6681
+     * 
+     * @access public
+     */
+    function extendLife()
+    {
+        @touch($this->_file);
+    }
+    
+    // --- Private methods ---
+    
+    /**
+    * Compute & set the refresh time
+    *
+    * @access private
+    */
+    function _setRefreshTime() 
+    {
+        if (is_null($this->_lifeTime)) {
+            $this->_refreshTime = null;
+        } else {
+            $this->_refreshTime = time() - $this->_lifeTime;
+        }
+    }
+    
+    /**
+    * 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)) {
+            return $this->raiseError('Cache_Lite : Unable to remove cache !', -3);
+        }
+        return true;        
+    }
+
+    /**
+    * Recursive function for cleaning cache file in the given directory
+    *
+    * @param string $dir directory complete path (with a trailing slash)
+    * @param string $group name of the cache group
+    * @param string $mode flush cache mode : 'old', 'ingroup', 'notingroup',
+                                             'callback_myFunction'
+    * @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) {
+	    foreach($this->_memoryCachingArray as $key => $v) {
+                if (strpos($key, $motif) !== false) {
+                    unset($this->_memoryCachingArray[$key]);
+                    $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1;
+                }
+            }
+            if ($this->_onlyMemoryCaching) {
+                return true;
+            }
+        }
+        if (!($dh = opendir($dir))) {
+            return $this->raiseError('Cache_Lite : Unable to open cache directory !', -4);
+        }
+        $result = true;
+        while (($file = readdir($dh)) !== false) {
+            if (($file != '.') && ($file != '..')) {
+                if (substr($file, 0, 6)=='cache_') {
+                    $file2 = $dir . $file;
+                    if (is_file($file2)) {
+                        switch (substr($mode, 0, 9)) {
+                            case 'old':
+                                // files older than lifeTime get deleted from cache
+                                if (!is_null($this->_lifeTime)) {
+                                    if ((time() - @filemtime($file2)) > $this->_lifeTime) {
+                                        $result = ($result and ($this->_unlink($file2)));
+                                    }
+                                }
+                                break;
+                            case 'notingrou':
+                                if (strpos($file2, $motif) === false) {
+                                    $result = ($result and ($this->_unlink($file2)));
+                                }
+                                break;
+                            case 'callback_':
+                                $func = substr($mode, 9, strlen($mode) - 9);
+                                if ($func($file2, $group)) {
+                                    $result = ($result and ($this->_unlink($file2)));
+                                }
+                                break;
+                            case 'ingroup':
+                            default:
+                                if (strpos($file2, $motif) !== false) {
+                                    $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 $data data to cache
+    * @access private
+    */
+    function _memoryCacheAdd($data)
+    {
+        $this->_memoryCachingArray[$this->_file] = $data;
+        if ($this->_memoryCachingCounter >= $this->_memoryCachingLimit) {
+            list($key, ) = each($this->_memoryCachingArray);
+            unset($this->_memoryCachingArray[$key]);
+        } else {
+            $this->_memoryCachingCounter = $this->_memoryCachingCounter + 1;
+        }
+    }
+
+    /**
+    * Make a file name (with path)
+    *
+    * @param string $id cache id
+    * @param string $group name of the group
+    * @access private
+    */
+    function _setFileName($id, $group)
+    {
+        
+        if ($this->_fileNameProtection) {
+            $suffix = 'cache_'.md5($group).'_'.md5($id);
+        } else {
+            $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;
+    }
+    
+    /**
+    * Read the cache file and return the content
+    *
+    * @return string content of the cache file (else : false or a PEAR_Error object)
+    * @access private
+    */
+    function _read()
+    {
+        $fp = @fopen($this->_file, "rb");
+        if ($fp) {
+	    if ($this->_fileLocking) @flock($fp, LOCK_SH);
+            clearstatcache();
+            $length = @filesize($this->_file);
+            $mqr = get_magic_quotes_runtime();
+            if ($mqr) {
+                set_magic_quotes_runtime(0);
+            }
+            if ($this->_readControl) {
+                $hashControl = @fread($fp, 32);
+                $length = $length - 32;
+            }
+
+            if ($length) {
+                $data = '';
+                // See https://bugs.php.net/bug.php?id=30936
+                // The 8192 magic number is the chunk size used internally by PHP.
+                while(!feof($fp)) $data .= fread($fp, 8192);
+            } else {
+                $data = '';
+            }
+            if ($mqr) {
+                set_magic_quotes_runtime($mqr);
+            }
+            if ($this->_fileLocking) @flock($fp, LOCK_UN);
+            @fclose($fp);
+            if ($this->_readControl) {
+                $hashData = $this->_hash($data, $this->_readControlType);
+                if ($hashData != $hashControl) {
+                    if (!(is_null($this->_lifeTime))) {
+                        @touch($this->_file, time() - 2*abs($this->_lifeTime)); 
+                    } else {
+                        @unlink($this->_file);
+                    }
+                    return false;
+                }
+            }
+            return $data;
+        }
+        return $this->raiseError('Cache_Lite : Unable to read cache !', -2); 
+    }
+    
+    /**
+    * Write the given data in the cache file
+    *
+    * @param string $data data to put in cache
+    * @return boolean true if ok (a PEAR_Error object else)
+    * @access private
+    */
+    function _write($data)
+    {
+        if ($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) . '/';
+                if (!(@is_dir($root))) {
+                    @mkdir($root, $this->_hashedDirectoryUmask);
+                }
+            }
+        }
+        $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);
+            }
+            $mqr = get_magic_quotes_runtime();
+            if ($mqr) {
+                set_magic_quotes_runtime(0);
+            }
+            @fwrite($fp, $data);
+            if ($mqr) {
+                set_magic_quotes_runtime($mqr);
+            }
+            if ($this->_fileLocking) @flock($fp, LOCK_UN);
+            @fclose($fp);
+            return true;
+        }      
+        return $this->raiseError('Cache_Lite : Unable to write cache file : '.$this->_file, -1);
+    }
+       
+    /**
+    * Write the given data in the cache file and control it just after to avoir corrupted cache entries
+    *
+    * @param string $data data to put in cache
+    * @return boolean true if the test is ok (else : false or a PEAR_Error object)
+    * @access private
+    */
+    function _writeAndControl($data)
+    {
+        $result = $this->_write($data);
+        if (is_object($result)) {
+            return $result; #We return the PEAR_Error object
+        }
+        $dataRead = $this->_read();
+        if (is_object($dataRead)) {
+            return $dataRead; #We return the PEAR_Error object
+        }
+        if ((is_bool($dataRead)) && (!$dataRead)) {
+            return false; 
+        }
+        return ($dataRead==$data);
+    }
+    
+    /**
+    * Make a control key with the string containing datas
+    *
+    * @param string $data data
+    * @param string $controlType type of control 'md5', 'crc32' or 'strlen'
+    * @return string control key
+    * @access private
+    */
+    function _hash($data, $controlType)
+    {
+        switch ($controlType) {
+        case 'md5':
+            return md5($data);
+        case 'crc32':
+            return sprintf('% 32d', crc32($data));
+        case 'strlen':
+            return sprintf('% 32d', strlen($data));
+        default:
+            return $this->raiseError('Unknown controlType ! (available values are only \'md5\', \'crc32\', \'strlen\')', -5);
+        }
+    }
+    
+} 




Svn-src-all メーリングリストの案内