From: <var...@us...> - 2012-11-20 09:06:58
|
Revision: 8494 http://phpwiki.svn.sourceforge.net/phpwiki/?rev=8494&view=rev Author: vargenau Date: 2012-11-20 09:06:44 +0000 (Tue, 20 Nov 2012) Log Message: ----------- Update to PEAR Cache-1.5.6 Modified Paths: -------------- trunk/lib/pear/Cache/Container/db.php trunk/lib/pear/Cache/Container/file.php trunk/lib/pear/Cache/Container/trifile.php trunk/lib/pear/Cache/Container.php trunk/lib/pear/Cache/Error.php trunk/lib/pear/Cache/Graphics.php Added Paths: ----------- trunk/lib/pear/Cache/Application.php trunk/lib/pear/Cache/Cache.php trunk/lib/pear/Cache/Container/dbx.php trunk/lib/pear/Cache/Container/mdb.php trunk/lib/pear/Cache/Container/mdb_cache_schema.xml trunk/lib/pear/Cache/Container/msession.php trunk/lib/pear/Cache/Container/phplib.php trunk/lib/pear/Cache/Container/shm.php trunk/lib/pear/Cache/Function.php trunk/lib/pear/Cache/HTTP_Request.php trunk/lib/pear/Cache/Output.php trunk/lib/pear/Cache/OutputCompression.php Added: trunk/lib/pear/Cache/Application.php =================================================================== --- trunk/lib/pear/Cache/Application.php (rev 0) +++ trunk/lib/pear/Cache/Application.php 2012-11-20 09:06:44 UTC (rev 8494) @@ -0,0 +1,179 @@ +<?php +// +// +----------------------------------------------------------------------+ +// | PEAR :: Cache | +// +----------------------------------------------------------------------+ +// | Copyright (c) 1997-2003 The PHP Group | +// +----------------------------------------------------------------------+ +// | This source file is subject to version 2.02 of the PHP license, | +// | that is bundled with this package in the file LICENSE, and is | +// | available at through the world-wide-web at | +// | http://www.php.net/license/2_02.txt. | +// | If you did not receive a copy of the PHP license and are unable to | +// | obtain it through the world-wide-web, please send a note to | +// | li...@ph... so we can mail you a copy immediately. | +// +----------------------------------------------------------------------+ +// | Authors: Richard Heyes <ri...@ph...> | +// +----------------------------------------------------------------------+ +// +// $Id: Application.php 178289 2005-01-26 09:47:28Z dufuz $ + +require_once 'Cache.php'; + +// Application level variables +// +// Purpose +// Variables that are persisent across all user sessions, +// not just a specific user ala normal sessions. +// +// Usage: +// +// Example 1: +// +// $app =& new Cache_Application(); +// $_APP =& $app->getData(); +// +// In this case the $_APP variable is akin to the $_SESSION variable. +// If you add/remove stuff, it will be reflected in the next request +// (of any user). +// +// Example 2: +// +// $foo = 'Some data'; +// $bar = 'Some other data'; +// +// $app =& new Cache_Application(); +// $app->register('foo'); +// $app->register('bar', $bar); +// +// $foo = 'Different data'; +// +// In this case the variables are registered with the register() function. +// This is akin to session_register(). +// +// As with session_register(), the contents of the variable at the *end* of the +// request is registered and not at the point of registration. Therefore in this +// example, for the $foo variable, the string 'Different data' is stored and not +// 'Some data'. The exception to this rule is if you use the second argument to +// register() as in the second call to it above. This will cause the data supplied +// in the second argument to be stored and not the contents at the end of the request. +// +// Note: If you use this method with register_globals turned on, the variables will be +// automatically globalled upon startup, (ie. when you create the object). +// +// Note: If you register a variable that is not set when the script finishes, it will +// registered as null. +// +// +// *** You are strongly recommended to use only one method of the two above. *** +// +// (In fact if you use the register() function with register_globals Off, you have to +// use the $_APP method to get at the data). + +class Cache_Application extends Cache +{ + + var $data; + var $id; + var $group; + var $registered_vars; + + /** + * Constructor + * + * @param string Name of container class + * @param array Array with container class options + */ + function Cache_Application($container = 'file', $container_options = array('cache_dir' => '/tmp/', 'filename_prefix' => 'cache_'), $id = 'application_var', $group = 'application_cache') + { + $this->id = $id; + $this->group = $group; + $this->registered_vars = array(); + + $this->Cache($container, $container_options); + $this->data = $this->isCached($this->id, $this->group) ? unserialize($this->get($this->id, $this->group)) : array(); + + // If register_globals on, global all registered variables + if (ini_get('register_globals') && is_array($this->data)) { + foreach ($this->data as $key => $value) { + global $$key; + $$key = $value; + } + } + } + + /** + * Destructor + * + * Gets values of all registered variables and stores them. Then calls save() to + * write data away. + */ + function _Cache_Application() + { + // Get contents of all registered variables + if (is_array($this->registered_vars) && !empty($this->registered_vars)) { + foreach ($this->registered_vars as $varname) { + global $$varname; + $this->data[$varname] = $$varname; + } + } + + // Save the data + $this->save($this->id, serialize($this->data), 0, $this->group); + } + + /** + * register() + * + * Registers a variable to be stored. + * + * @param string Name of variable to register + * @param mixed Optional data to store + */ + function register($varname, $data = null) + { + if (isset($data)) { + $this->data[$varname] = $data; + } else { + $this->registered_vars[] = $varname; + } + } + + /** + * unregister() + * + * Unregisters a variable from being stored. + * + * @param string Name of variable to unregister + */ + function unregister($varname) + { + if (isset($this->data[$varname])) { + unset($this->data[$varname]); + } + } + + /** + * clear() + * + * Removes all stored data + */ + function clear() + { + $this->data = array(); + } + + /** + * getData() + * + * Use this to get a reference to the data to manipulate + * in calling script. Eg. $_APP =& $obj->getData(); + * + * @return mixed A reference to the data + */ + function &getData() + { + return $this->data; + } +} +?> \ No newline at end of file Added: trunk/lib/pear/Cache/Cache.php =================================================================== --- trunk/lib/pear/Cache/Cache.php (rev 0) +++ trunk/lib/pear/Cache/Cache.php 2012-11-20 09:06:44 UTC (rev 8494) @@ -0,0 +1,368 @@ +<?php +// +----------------------------------------------------------------------+ +// | PEAR :: Cache | +// +----------------------------------------------------------------------+ +// | Copyright (c) 1997-2003 The PHP Group | +// +----------------------------------------------------------------------+ +// | This source file is subject to version 2.0 of the PHP license, | +// | that is bundled with this package in the file LICENSE, and is | +// | available at through the world-wide-web at | +// | http://www.php.net/license/2_02.txt. | +// | If you did not receive a copy of the PHP license and are unable to | +// | obtain it through the world-wide-web, please send a note to | +// | li...@ph... so we can mail you a copy immediately. | +// +----------------------------------------------------------------------+ +// | Authors: Ulf Wendel <ulf...@ph...> | +// | Sebastian Bergmann <sb...@se...> | +// +----------------------------------------------------------------------+ +// +// $Id: Cache.php 267047 2008-10-07 08:58:46Z dufuz $ + +require_once 'PEAR.php'; +require_once 'Cache/Error.php'; + +/** +* Cache is a base class for cache implementations. +* +* The pear cache module is a generic data cache which can be used to +* cache script runs. The idea behind the cache is quite simple. If you have +* the same input parameters for whatever tasks/algorithm you use you'll +* usually get the same output. So why not caching templates, functions calls, +* graphic generation etc. Caching certain actions e.g. XSLT tranformations +* saves you lots of time. +* +* The design of the cache reminds of PHPLibs session implementation. A +* (PHPLib: session) controller uses storage container (PHPLib: ct_*.inc) to save +* certain data (PHPLib: session data). In contrast to the session stuff it's up to +* you to generate an ID for the data to cache. If you're using the output cache +* you might use the script name as a seed for cache::generateID(), if your using the +* function cache you'd use an array with all function parameters. +* +* Usage example of the generic data cache: +* +* require_once('Cache.php'); +* +* $cache = new Cache('file', array('cache_dir' => 'cache/') ); +* $id = $cache->generateID('testentry'); +* +* if ($data = $cache->get($id)) { +* print "Cache hit.<br>Data: $data"; +* +* } else { +* $data = 'data of any kind'; +* $cache->save($id, $data); +* print 'Cache miss.<br>'; +* } +* +* WARNING: No File/DB-Table-Row locking is implemented yet, +* it's possible, that you get corrupted data-entries under +* bad circumstances (especially with the file container) +* +* @author Ulf Wendel <ulf...@ph...> +* @version $Id: Cache.php 267047 2008-10-07 08:58:46Z dufuz $ +* @package Cache +* @access public +*/ +class Cache extends PEAR +{ + + /** + * Enables / disables caching. + * + * TODO: Add explanation what this is good for. + * + * @var boolean + * @access private + */ + var $caching = true; + + /** + * Garbage collection: probability in seconds + * + * If set to a value above 0 a garbage collection will + * flush all cache entries older than the specified number + * of seconds. + * + * @var integer + * @see $gc_probability, $gc_maxlifetime + * @access public + */ + var $gc_time = 1; + + /** + * Garbage collection: probability in percent + * + * TODO: Add an explanation. + * + * @var integer 0 => never + * @see $gc_time, $gc_maxlifetime + * @access public + */ + var $gc_probability = 1; + + /** + * Garbage collection: delete all entries not use for n seconds. + * + * Default is one day, 60 * 60 * 24 = 86400 seconds. + * + * @var integer + * @see $gc_probability, $gc_time + */ + var $gc_maxlifetime = 86400; + + /** + * Storage container object. + * + * @var object Cache_Container + */ + var $container; + + // + // public methods + // + + /** + * + * @param string Name of container class + * @param array Array with container class options + */ + function Cache($container, $container_options = '') + { + $this->PEAR(); + $container = strtolower($container); + $container_class = 'Cache_Container_' . $container; + $container_classfile = 'Cache/Container/' . $container . '.php'; + + include_once $container_classfile; + $this->container = new $container_class($container_options); + } + + //deconstructor + function _Cache() + { + $this->garbageCollection(); + } + + /** + * Returns the current caching state. + * + * @return boolean The current caching state. + * @access public + */ + function getCaching() + { + return $this->caching; + } + + /** + * Enables or disables caching. + * + * @param boolean The new caching state. + * @access public + */ + function setCaching($state) + { + $this->caching = $state; + } + + /** + * Returns the requested dataset it if exists and is not expired + * + * @param string dataset ID + * @param string cache group + * @return mixed cached data or null on failure + * @access public + */ + function get($id, $group = 'default') + { + if (!$this->caching) { + return ''; + } + + if ($this->isCached($id, $group) && !$this->isExpired($id, $group)) { + return $this->load($id, $group); + } + return null; + } // end func get + + /** + * Stores the given data in the cache. + * + * @param string dataset ID used as cache identifier + * @param mixed data to cache + * @param integer lifetime of the cached data in seconds - 0 for endless + * @param string cache group + * @return boolean + * @access public + */ + function save($id, $data, $expires = 0, $group = 'default') + { + if (!$this->caching) { + return true; + } + return $this->extSave($id, $data, '',$expires, $group); + } // end func save + + /** + * Stores a dataset with additional userdefined data. + * + * @param string dataset ID + * @param mixed data to store + * @param string additional userdefined data + * @param mixed userdefined expire date + * @param string cache group + * @return boolean + * @throws Cache_Error + * @access public + * @see getUserdata() + */ + function extSave($id, $cachedata, $userdata, $expires = 0, $group = 'default') + { + if (!$this->caching) { + return true; + } + return $this->container->save($id, $cachedata, $expires, $group, $userdata); + } // end func extSave + + /** + * Loads the given ID from the cache. + * + * @param string dataset ID + * @param string cache group + * @return mixed cached data or null on failure + * @access public + */ + function load($id, $group = 'default') + { + if (!$this->caching) { + return ''; + } + return $this->container->load($id, $group); + } // end func load + + /** + * Returns the userdata field of a cached data set. + * + * @param string dataset ID + * @param string cache group + * @return string userdata + * @access public + * @see extSave() + */ + function getUserdata($id, $group = 'default') + { + if (!$this->caching) { + return ''; + } + return $this->container->getUserdata($id, $group); + } // end func getUserdata + + /** + * Removes the specified dataset from the cache. + * + * @param string dataset ID + * @param string cache group + * @return boolean + * @access public + */ + function remove($id, $group = 'default') + { + if (!$this->caching) { + return true; + } + return $this->container->remove($id, $group); + } // end func remove + + /** + * Flushes the cache - removes all data from it + * + * @param string cache group, if empty all groups will be flashed + * @return integer number of removed datasets + */ + function flush($group = 'default') + { + if (!$this->caching) { + return true; + } + return $this->container->flush($group); + } // end func flush + + /** + * Checks if a dataset exists. + * + * Note: this does not say that the cached data is not expired! + * + * @param string dataset ID + * @param string cache group + * @return boolean + * @access public + */ + function isCached($id, $group = 'default') + { + if (!$this->caching) { + return false; + } + return $this->container->isCached($id, $group); + } // end func isCached + + /** + * Checks if a dataset is expired + * + * @param string dataset ID + * @param string cache group + * @param integer maximum age for the cached data in seconds - 0 for endless + * If the cached data is older but the given lifetime it will + * be removed from the cache. You don't have to provide this + * argument if you call isExpired(). Every dataset knows + * it's expire date and will be removed automatically. Use + * this only if you know what you're doing... + * @return boolean + * @access public + */ + function isExpired($id, $group = 'default', $max_age = 0) + { + if (!$this->caching) { + return true; + } + return $this->container->isExpired($id, $group, $max_age); + } // end func isExpired + + /** + * Generates a "unique" ID for the given value + * + * This is a quick but dirty hack to get a "unique" ID for a any kind of variable. + * ID clashes might occur from time to time although they are extreme unlikely! + * + * @param mixed variable to generate a ID for + * @return string "unique" ID + * @access public + */ + function generateID($variable) + { + // WARNING: ID clashes are possible although unlikely + return md5(serialize($variable)); + } + + /** + * Calls the garbage collector of the storage object with a certain probability + * + * @param boolean Force a garbage collection run? + * @see $gc_probability, $gc_time + */ + function garbageCollection($force = false) + { + static $last_run = 0; + + if (!$this->caching) { + return; + } + + // time and probability based + if (($force) || ($last_run && $last_run < time() + $this->gc_time) || (rand(1, 100) < $this->gc_probability)) { + $this->container->garbageCollection($this->gc_maxlifetime); + $last_run = time(); + } + } // end func garbageCollection + +} // end class cache +?> Modified: trunk/lib/pear/Cache/Container/db.php =================================================================== --- trunk/lib/pear/Cache/Container/db.php 2012-11-20 08:46:58 UTC (rev 8493) +++ trunk/lib/pear/Cache/Container/db.php 2012-11-20 09:06:44 UTC (rev 8494) @@ -16,6 +16,8 @@ // | Sebastian Bergmann <sb...@se...> | // | Chuck Hagenbuch <ch...@ho...> | // +----------------------------------------------------------------------+ +// +// $Id: db.php 178288 2005-01-26 09:42:30Z dufuz $ require_once 'DB.php'; require_once 'Cache/Container.php'; @@ -37,23 +39,24 @@ * For _MySQL_ you need this DB table: * * CREATE TABLE cache ( -* id CHAR(32) NOT NULL DEFAULT '', -* cachegroup VARCHAR(127) NOT NULL DEFAULT '', -* cachedata BLOB NOT NULL DEFAULT '', -* userdata VARCHAR(255) NOT NULL DEFAUL '', -* expires INT(9) NOT NULL DEFAULT 0, +* id CHAR(32) NOT null DEFAULT '', +* cachegroup VARCHAR(127) NOT null DEFAULT '', +* cachedata BLOB NOT null DEFAULT '', +* userdata VARCHAR(255) NOT null DEFAULT '', +* expires INT(9) NOT null DEFAULT 0, * -* changed TIMESTAMP(14) NOT NULL, +* changed TIMESTAMP(14) NOT null, * * INDEX (expires), * PRIMARY KEY (id, cachegroup) * ) * * @author Sebastian Bergmann <sb...@se...> -* @version +* @version $Id: db.php 178288 2005-01-26 09:42:30Z dufuz $ * @package Cache */ -class Cache_Container_db extends Cache_Container { +class Cache_Container_db extends Cache_Container +{ /** * Name of the DB table to store caching data @@ -84,15 +87,14 @@ $this->setOptions($options, array_merge($this->allowed_options, array('dsn', 'cache_table'))); - if (!$this->dsn) + if (!$this->dsn) { return new Cache_Error('No dsn specified!', __FILE__, __LINE__); - + } $this->db = DB::connect($this->dsn, true); - if (DB::isError($this->db)) { + if (PEAR::isError($this->db)) { return new Cache_Error('DB::connect failed: ' . DB::errorMessage($this->db), __FILE__, __LINE__); - } else { - $this->db->setFetchMode(DB_FETCHMODE_ASSOC); } + $this->db->setFetchMode(DB_FETCHMODE_ASSOC); } function fetch($id, $group) @@ -105,15 +107,15 @@ $res = $this->db->query($query); - if (DB::isError($res)) + if (PEAR::isError($res)) { return new Cache_Error('DB::query failed: ' . DB::errorMessage($res), __FILE__, __LINE__); - + } $row = $res->fetchRow(); - if (is_array($row)) + if (is_array($row)) { $data = array($row['expires'], $this->decode($row['cachedata']), $row['userdata']); - else - $data = array(NULL, NULL, NULL); - + } else { + $data = array(null, null, null); + } // last used required by the garbage collection // WARNING: might be MySQL specific $query = sprintf("UPDATE %s SET changed = (NOW() + 0) WHERE id = '%s' AND cachegroup = '%s'", @@ -124,9 +126,9 @@ $res = $this->db->query($query); - if (DB::isError($res)) + if (PEAR::isError($res)) { return new Cache_Error('DB::query failed: ' . DB::errorMessage($res), __FILE__, __LINE__); - + } return $data; } @@ -152,7 +154,7 @@ $res = $this->db->query($query); - if (DB::isError($res)) { + if (PEAR::isError($res)) { return new Cache_Error('DB::query failed: ' . DB::errorMessage($res) , __FILE__, __LINE__); } } @@ -169,8 +171,9 @@ $res = $this->db->query($query); - if (DB::isError($res)) + if (PEAR::isError($res)) { return new Cache_Error('DB::query failed: ' . DB::errorMessage($res), __FILE__, __LINE__); + } } function flush($group = '') @@ -185,7 +188,7 @@ $res = $this->db->query($query); - if (DB::isError($res)) + if (PEAR::isError($res)) return new Cache_Error('DB::query failed: ' . DB::errorMessage($res), __FILE__, __LINE__); } @@ -199,16 +202,15 @@ $res = $this->db->query($query); - if (DB::isError($res)) + if (PEAR::isError($res)) { return new Cache_Error('DB::query failed: ' . DB::errorMessage($res), __FILE__, __LINE__); - + } $row = $res->fetchRow(); if (is_array($row)) { return true; - } else { - return false; } + return false; } function garbageCollection($maxlifetime) @@ -227,19 +229,18 @@ $this->cache_table ); $cachesize = $this->db->GetOne($query); - if (DB::isError($cachesize)) { + if (PEAR::isError($cachesize)) { return new Cache_Error('DB::query failed: ' . DB::errorMessage($cachesize), __FILE__, __LINE__); } //if cache is to big. - if ($cachesize > $this->highwater) - { + if ($cachesize > $this->highwater) { //find the lowwater mark. $query = sprintf('select length(cachedata) as size, changed from %s order by changed DESC', $this->cache_table ); $res = $this->db->query($query); - if (DB::isError($res)) { + if (PEAR::isError($res)) { return new Cache_Error('DB::query failed: ' . DB::errorMessage($res), __FILE__, __LINE__); } @@ -257,9 +258,10 @@ $res = $this->db->query($query); } - if (DB::isError($res)) { + if (PEAR::isError($res)) { return new Cache_Error('DB::query failed: ' . DB::errorMessage($res), __FILE__, __LINE__); } } } +?> Added: trunk/lib/pear/Cache/Container/dbx.php =================================================================== --- trunk/lib/pear/Cache/Container/dbx.php (rev 0) +++ trunk/lib/pear/Cache/Container/dbx.php 2012-11-20 09:06:44 UTC (rev 8494) @@ -0,0 +1,295 @@ +<?php +// +----------------------------------------------------------------------+ +// | PEAR :: Cache | +// +----------------------------------------------------------------------+ +// | Copyright (c) 1997-2003 The PHP Group | +// +----------------------------------------------------------------------+ +// | This source file is subject to version 2.0 of the PHP license, | +// | that is bundled with this package in the file LICENSE, and is | +// | available at through the world-wide-web at | +// | http://www.php.net/license/2_02.txt. | +// | If you did not receive a copy of the PHP license and are unable to | +// | obtain it through the world-wide-web, please send a note to | +// | li...@ph... so we can mail you a copy immediately. | +// +----------------------------------------------------------------------+ +// | Authors: Christian Stocker <ch...@ph...> | +// +----------------------------------------------------------------------+ +// +// $Id: dbx.php 268860 2008-11-12 14:56:26Z clockwerx $ + + +require_once 'Cache/Container.php'; + +/** +* ext/dbx Cache Container. +* +* WARNING: Other systems might or might not support certain datatypes of +* the tables shown. As far as I know there's no large binary +* type in SQL-92 or SQL-99. Postgres seems to lack any +* BLOB or TEXT type, for MS-SQL you could use IMAGE, don't know +* about other databases. Please add sugestions for other databases to +* the inline docs. +* +* The field 'changed' has no meaning for the Cache itself. It's just there +* because it's a good idea to have an automatically updated timestamp +* field for debugging in all of your tables. +* +* For _MySQL_ you need this DB table: +* +* CREATE TABLE cache ( +* id CHAR(32) NOT null DEFAULT '', +* cachegroup VARCHAR(127) NOT null DEFAULT '', +* cachedata BLOB NOT null DEFAULT '', +* userdata VARCHAR(255) NOT null DEFAULT '', +* expires INT(9) NOT null DEFAULT 0, +* +* changed TIMESTAMP(14) NOT null, +* +* INDEX (expires), +* PRIMARY KEY (id, cachegroup) +* ) +* +* @author Christian Stocker <ch...@ph...> +* @version $Id: dbx.php 268860 2008-11-12 14:56:26Z clockwerx $ +* @package Cache +*/ +class Cache_Container_dbx extends Cache_Container +{ + + /** + * Name of the DB table to store caching data + * + * @see Cache_Container_file::$filename_prefix + */ + var $cache_table = ''; + + /** + * DBx module to use + * + * at the moment only mysql or odbc + * + * @var string + */ + var $module = ''; + + /** + * DB host to use + * + * @var string + */ + var $host = ''; + + /** + * DB database to use + * + * @var string + */ + var $db = ''; + + /** + * DB username to use + * + * @var string + */ + var $username = ''; + + /** + * DB password to use + * + * @var string + */ + var $password = ''; + + + /** + * Establish a persistent connection? + * + * @var boolean + */ + var $persistent = true; + + + function Cache_Container_dbx($options) + { + if (!is_array($options) ) { + return new Cache_Error('No options specified!', __FILE__, __LINE__); + } + + $this->setOptions($options, array_merge($this->allowed_options, array('module','host','db','username','password', 'cache_table', 'persistent'))); + + if (!$this->module) + return new Cache_Error('No module specified!', __FILE__, __LINE__); + + $this->db = dbx_connect($this->module, $this->host, $this->db, $this->username, $this->password, $this->persistent); + + if (dbx_error($this->db)) { + return new Cache_Error('DBx connect failed: ' . dbx_error($this->db), __FILE__, __LINE__); + } else { + //not implemented yet in dbx + //$this->db->setFetchMode(DB_FETCHMODE_ASSOC); + } + } + + function fetch($id, $group) + { + $query = sprintf("SELECT cachedata, userdata, expires FROM %s WHERE id = '%s' AND cachegroup = '%s'", + $this->cache_table, + addslashes($id), + addslashes($group) + ); + + $res = dbx_query($this->db, $query); + if (dbx_error($this->db)) { + return new Cache_Error('DBx query failed: ' . dbx_error($this->db), __FILE__, __LINE__); + } + $row = $res->data[0]; + + if (is_array($row)) { + $data = array($row['expires'], $this->decode($row['cachedata']), $row['userdata']); + } else { + $data = array(null, null, null); + } + // last used required by the garbage collection + // WARNING: might be MySQL specific + $query = sprintf("UPDATE %s SET changed = (NOW() + 0) WHERE id = '%s' AND cachegroup = '%s'", + $this->cache_table, + addslashes($id), + addslashes($group) + ); + + $res = dbx_query($this->db, $query); + if (dbx_error($this->db)) { + return new Cache_Error('DBx query failed: ' . dbx_error($this->db), __FILE__, __LINE__); + } + return $data; + } + + /** + * Stores a dataset. + * + * WARNING: we use the SQL command REPLACE INTO this might be + * MySQL specific. As MySQL is very popular the method should + * work fine for 95% of you. + */ + function save($id, $data, $expires, $group, $userdata) + { + $this->flushPreload($id, $group); + + $query = sprintf("REPLACE INTO %s (userdata, cachedata, expires, id, cachegroup) VALUES ('%s', '%s', %d, '%s', '%s')", + $this->cache_table, + addslashes($userdata), + addslashes($this->encode($data)), + $this->getExpiresAbsolute($expires) , + addslashes($id), + addslashes($group) + ); + + $res = dbx_query($this->db, $query); + + if (dbx_error($this->db)) { + return new Cache_Error('DBx query failed: ' . dbx_error($this->db) , __FILE__, __LINE__); + } + } + + function remove($id, $group) + { + $this->flushPreload($id, $group); + + $query = sprintf("DELETE FROM %s WHERE id = '%s' and cachegroup = '%s'", + $this->cache_table, + addslashes($id), + addslashes($group) + ); + + $res = dbx_query($this->db, $query); + + if (dbx_error($this->db)) { + return new Cache_Error('DBx query failed: ' . dbx_error($this->db), __FILE__, __LINE__); + } + } + + function flush($group = '') + { + $this->flushPreload(); + + if ($group) { + $query = sprintf("DELETE FROM %s WHERE cachegroup = '%s'", $this->cache_table, addslashes($group)); + } else { + $query = sprintf("DELETE FROM %s", $this->cache_table); + } + + $res = dbx_query($this->db,$query); + + if (dbx_error($this->db)) { + return new Cache_Error('DBx query failed: ' . dbx_error($this->db), __FILE__, __LINE__); + } + } + + function idExists($id, $group) + { + $query = sprintf("SELECT id FROM %s WHERE ID = '%s' AND cachegroup = '%s'", + $this->cache_table, + addslashes($id), + addslashes($group) + ); + + $res = dbx_query($this->db, $query); + + if (dbx_error($this->db)) { + return new Cache_Error('DBx query failed: ' . dbx_error($this->db), __FILE__, __LINE__); + } + + $row = $res[0]; + + if (is_array($row)) { + return true; + } + return false; + } + + function garbageCollection($maxlifetime) + { + $this->flushPreload(); + + $query = sprintf('DELETE FROM %s WHERE (expires <= %d AND expires > 0) OR changed <= (NOW() - %d)', + $this->cache_table, + time(), + $maxlifetime + ); + + + $res = dbx_query($this->db, $query); + + if (dbx_error($this->db)) { + return new Cache_Error('DBx query failed: ' . dbx_error($this->db), __FILE__, __LINE__); + } + $query = sprintf('select sum(length(cachedata)) as CacheSize from %s', + $this->cache_table + ); + + $res = dbx_query($this->db, $query); + //if cache is to big. + if ($res->data[0][CacheSize] > $this->highwater) { + //find the lowwater mark. + $query = sprintf('select length(cachedata) as size, changed from %s order by changed DESC', + $this->cache_table + ); + + $res = dbx_query($this->db, $query); + $keep_size = 0; + $i = 0; + while ($keep_size < $this->lowwater && $i < $res->rows ) { + $keep_size += $res->data[$i][size]; + $i++; + } + + //delete all entries, which were changed before the "lowwwater mark" + $query = sprintf('delete from %s where changed <= %s', + $this->cache_table, + $res->data[$i][changed] + ); + $res = dbx_query($this->db, $query); + } + } +} +?> Modified: trunk/lib/pear/Cache/Container/file.php =================================================================== --- trunk/lib/pear/Cache/Container/file.php 2012-11-20 08:46:58 UTC (rev 8493) +++ trunk/lib/pear/Cache/Container/file.php 2012-11-20 09:06:44 UTC (rev 8494) @@ -15,6 +15,8 @@ // | Authors: Ulf Wendel <ulf...@ph...> | // | Sebastian Bergmann <sb...@se...> | // +----------------------------------------------------------------------+ +// +// $Id: file.php 293864 2010-01-23 03:49:21Z clockwerx $ require_once 'Cache/Container.php'; @@ -22,9 +24,10 @@ * Stores cache contents in a file. * * @author Ulf Wendel <ulf...@ph...> -* @version +* @version $Id: file.php 293864 2010-01-23 03:49:21Z clockwerx $ */ -class Cache_Container_file extends Cache_Container { +class Cache_Container_file extends Cache_Container +{ /** * File locking @@ -63,25 +66,27 @@ * @var string */ var $filename_prefix = ''; - + + /** * List of cache entries, used within a gc run - * + * * @var array */ var $entries; - + /** * Total number of bytes required by all cache entries, used within a gc run. - * + * * @var int */ var $total_size = 0; + /** * Max Line Length of userdata * - * If set to 0, it will take the default + * If set to 0, it will take the default * ( 1024 in php 4.2, unlimited in php 4.3) * see http://ch.php.net/manual/en/function.fgets.php * for details @@ -95,13 +100,13 @@ * * @param array Config options: ["cache_dir" => ..., "filename_prefix" => ...] */ - function Cache_Container_file($options = '') { - if (is_array($options)) + function Cache_Container_file($options = '') + { + if (is_array($options)) { $this->setOptions($options, array_merge($this->allowed_options, array('cache_dir', 'filename_prefix', 'max_userdata_linelength'))); - + } clearstatcache(); - if ($this->cache_dir) - { + if ($this->cache_dir) { // make relative paths absolute for use in deconstructor. // it looks like the deconstructor has problems with relative paths if (OS_UNIX && '/' != $this->cache_dir{0} ) @@ -116,22 +121,27 @@ } $this->entries = array(); $this->group_dirs = array(); - + } // end func contructor - function fetch($id, $group) { + function fetch($id, $group) + { $file = $this->getFilename($id, $group); - if (!file_exists($file)) - return array(NULL, NULL, NULL); + if (PEAR::isError($file)) { + return $file; + } + if (!file_exists($file)) { + return array(null, null, null); + } // retrive the content - if (!($fh = @fopen($file, 'rb'))) + if (!($fh = @fopen($file, 'rb'))) { return new Cache_Error("Can't access cache file '$file'. Check access rights and path.", __FILE__, __LINE__); - + } // File locking (shared lock) - if ($this->fileLocking) + if ($this->fileLocking) { flock($fh, LOCK_SH); - + } // file format: // 1st line: expiration date // 2nd line: user data @@ -142,12 +152,16 @@ } else { $userdata = trim(fgets($fh, $this->max_userdata_linelength)); } - $cachedata = $this->decode(fread($fh, filesize($file))); + $buffer = ''; + while (!feof($fh)) { + $buffer .= fread($fh, 8192); + } + $cachedata = $this->decode($buffer); // Unlocking - if ($this->fileLocking) + if ($this->fileLocking) { flock($fh, LOCK_UN); - + } fclose($fh); // last usage date used by the gc - maxlifetime @@ -164,17 +178,19 @@ * WARNING: If you supply userdata it must not contain any linebreaks, * otherwise it will break the filestructure. */ - function save($id, $cachedata, $expires, $group, $userdata) { + function save($id, $cachedata, $expires, $group, $userdata) + { $this->flushPreload($id, $group); $file = $this->getFilename($id, $group); - if (!($fh = @fopen($file, 'wb'))) + if (!($fh = @fopen($file, 'wb'))) { return new Cache_Error("Can't access '$file' to store cache data. Check access rights and path.", __FILE__, __LINE__); + } // File locking (exclusive lock) - if ($this->fileLocking) + if ($this->fileLocking) { flock($fh, LOCK_EX); - + } // file format: // 1st line: expiration date // 2nd line: user data @@ -185,24 +201,28 @@ fwrite($fh, $this->encode($cachedata)); // File unlocking - if ($this->fileLocking) + if ($this->fileLocking) { flock($fh, LOCK_UN); - + } fclose($fh); // I'm not sure if we need this - // i don't think we need this (chregu) + // i don't think we need this (chregu) // touch($file); return true; } // end func save - function remove($id, $group) { + function remove($id, $group) + { $this->flushPreload($id, $group); $file = $this->getFilename($id, $group); + if (PEAR::isError($file)) { + return $file; + } + if (file_exists($file)) { - $ok = unlink($file); clearstatcache(); @@ -212,7 +232,8 @@ return false; } // end func remove - function flush($group) { + function flush($group) + { $this->flushPreload(); $dir = ($group) ? $this->cache_dir . $group . '/' : $this->cache_dir; @@ -223,8 +244,8 @@ return $num_removed; } // end func flush - function idExists($id, $group) { - + function idExists($id, $group) + { return file_exists($this->getFilename($id, $group)); } // end func idExists @@ -241,34 +262,35 @@ * @param integer Maximum lifetime in seconds of an no longer used/touched entry * @throws Cache_Error */ - function garbageCollection($maxlifetime) { - + function garbageCollection($maxlifetime) + { $this->flushPreload(); clearstatcache(); $ok = $this->doGarbageCollection($maxlifetime, $this->cache_dir); - // check the space used by the cache entries + // check the space used by the cache entries if ($this->total_size > $this->highwater) { - + krsort($this->entries); reset($this->entries); - + while ($this->total_size > $this->lowwater && list($lastmod, $entry) = each($this->entries)) { - if (@unlink($entry['file'])) + if (@unlink($entry['file'])) { $this->total_size -= $entry['size']; - else - new CacheError("Can't delete {$entry["file"]}. Check the permissions."); + } else { + new CacheError("Can't delete {$entry['file']}. Check the permissions."); + } } - + } - + $this->entries = array(); $this->total_size = 0; - + return $ok; } // end func garbageCollection - + /** * Does the recursive gc procedure, protected. * @@ -277,11 +299,12 @@ * recursive function call! * @throws Cache_Error */ - function doGarbageCollection($maxlifetime, $dir) { + function doGarbageCollection($maxlifetime, $dir) + { + if (!is_writable($dir) || !is_readable($dir) || !($dh = opendir($dir))) { + return new Cache_Error("Can't remove directory '$dir'. Check permissions and path.", __FILE__, __LINE__); + } - if (!($dh = opendir($dir))) - return new Cache_Error("Can't access cache directory '$dir'. Check permissions and path.", __FILE__, __LINE__); - while ($file = readdir($dh)) { if ('.' == $file || '..' == $file) continue; @@ -301,13 +324,14 @@ $expire = fgets($fh, 11); fclose($fh); $lastused = filemtime($file); - + $this->entries[$lastused] = array('file' => $file, 'size' => filesize($file)); $this->total_size += filesize($file); - + // remove if expired - if (( ($expire && $expire <= time()) || ($lastused <= (time() - $maxlifetime)) ) && !unlink($file)) + if (( ($expire && $expire <= time()) || ($lastused <= (time() - $maxlifetime)) ) && !unlink($file)) { new Cache_Error("Can't unlink cache file '$file', skipping. Check permissions and path.", __FILE__, __LINE__); + } } closedir($dh); @@ -325,17 +349,21 @@ * @return string full filename with the path * @access public */ - function getFilename($id, $group) { - - if (isset($this->group_dirs[$group])) + function getFilename($id, $group) + { + if (isset($this->group_dirs[$group])) { return $this->group_dirs[$group] . $this->filename_prefix . $id; + } $dir = $this->cache_dir . $group . '/'; - if (!file_exists($dir)) { - mkdir($dir, 0755); - clearstatcache(); + if (is_writeable($this->cache_dir)) { + if (!file_exists($dir)) { + mkdir($dir, 0755, true); + clearstatcache(); + } + } else { + return new Cache_Error("Can't make directory '$dir'. Check permissions and path.", __FILE__, __LINE__); } - $this->group_dirs[$group] = $dir; return $dir . $this->filename_prefix . $id; @@ -348,9 +376,11 @@ * @return integer number of removed files * @throws Cache_Error */ - function deleteDir($dir) { - if (!($dh = opendir($dir))) + function deleteDir($dir) + { + if (!is_writable($dir) || !is_readable($dir) || !($dh = opendir($dir))) { return new Cache_Error("Can't remove directory '$dir'. Check permissions and path.", __FILE__, __LINE__); + } $num_removed = 0; @@ -379,5 +409,6 @@ return $num_removed; } // end func deleteDir - + } // end class file +?> Added: trunk/lib/pear/Cache/Container/mdb.php =================================================================== --- trunk/lib/pear/Cache/Container/mdb.php (rev 0) +++ trunk/lib/pear/Cache/Container/mdb.php 2012-11-20 09:06:44 UTC (rev 8494) @@ -0,0 +1,380 @@ +<?php +// +----------------------------------------------------------------------+ +// | PEAR :: Cache :: MDB Container | +// +----------------------------------------------------------------------+ +// | Copyright (c) 1997-2003 The PHP Group | +// +----------------------------------------------------------------------+ +// | This source file is subject to version 2.0 of the PHP license, | +// | that is bundled with this package in the file LICENSE, and is | +// | available at through the world-wide-web at | +// | http://www.php.net/license/2_02.txt. | +// | If you did not receive a copy of the PHP license and are unable to | +// | obtain it through the world-wide-web, please send a note to | +// | li...@ph... so we can mail you a copy immediately. | +// +----------------------------------------------------------------------+ +// | Note: This is a MDB-oriented rewrite of Cache/Container/db.php. | +// | Thanks to Lukas Smith for his patience in answering my questions | +// +----------------------------------------------------------------------+ +// | Author: Lorenzo Alberton <l.alberton at quipo.it> | +// +----------------------------------------------------------------------+ +// +// $Id: mdb.php 174777 2004-12-15 09:09:33Z dufuz $ + +require_once 'MDB.php'; +require_once 'Cache/Container.php'; + +/** +* PEAR/MDB Cache Container. +* +* NB: The field 'changed' has no meaning for the Cache itself. It's just there +* because it's a good idea to have an automatically updated timestamp +* field for debugging in all of your tables. +* +* A XML MDB-compliant schema example for the table needed is provided. +* Look at the file "mdb_cache_schema.xml" for that. +* +* ------------------------------------------ +* A basic usage example: +* ------------------------------------------ +* +* $dbinfo = array( +* 'database' => 'dbname', +* 'phptype' => 'mysql', +* 'username' => 'root', +* 'password' => '', +* 'cache_table' => 'cache' +* ); +* +* +* $cache = new Cache('mdb', $dbinfo); +* $id = $cache->generateID('testentry'); +* +* if ($data = $cache->get($id)) { +* echo 'Cache hit.<br />Data: '.$data; +* +* } else { +* $data = 'data of any kind'; +* $cache->save($id, $data); +* echo 'Cache miss.<br />'; +* } +* +* ------------------------------------------ +* +* @author Lorenzo Alberton <l.alberton at quipo.it> +* @version $Id: mdb.php 174777 2004-12-15 09:09:33Z dufuz $ +* @package Cache +*/ +class Cache_Container_mdb extends Cache_Container +{ + + /** + * Name of the MDB table to store caching data + * + * @see Cache_Container_file::$filename_prefix + */ + var $cache_table = ''; + + /** + * PEAR MDB object + * + * @var object PEAR_MDB + */ + var $db; + + /** + * Constructor + * + * @param mixed Array with connection info or dsn string + */ + function Cache_Container_mdb($options) + { + $this->db = &MDB::Connect($options); + if (MDB::isError($this->db)) { + return new Cache_Error('MDB::connect failed: ' + . $this->db->getMessage(), __FILE__, __LINE__); + } else { + $this->db->setFetchMode(MDB_FETCHMODE_ASSOC); + } + $this->setOptions($options, array_merge($this->allowed_options, + array('dsn', 'cache_table'))); + } + + /** + * Fetch in the db the data that matches input parameters + * + * @param string dataset ID + * @param string cache group + * @return mixed dataset value or null/Cache_Error on failure + * @access public + */ + function fetch($id, $group) + { + $query = 'SELECT cachedata FROM ' . $this->cache_table + .' WHERE id=' . $this->db->getTextValue($id) + .' AND cachegroup=' . $this->db->getTextValue($group); + if ($res = $this->db->query($query)) { + if ($this->db->endOfResult($res)) { + //no rows returned + $data = array(null, null, null); + } else { + $clob = $this->db->fetchClob($res,0,'cachedata'); + if (!MDB::isError($clob)) { + $cached_data = ''; + while(!$this->db->endOfLOB($clob)) { + if (MDB::isError($error = + $this->db->readLob($clob,$data,8000)<0)) { + return new Cache_Error('MDB::query failed: ' + . $error->getMessage(), __FILE__, __LINE__); + } + $cached_data .= $data; + } + unset($data); + $this->db->destroyLob($clob); + $this->db->freeResult($res); + + //finished fetching LOB, now fetch other fields... + $query = 'SELECT userdata, expires FROM ' . $this->cache_table + .' WHERE id=' . $this->db->getTextValue($id) + .' AND cachegroup=' . $this->db->getTextValue($group); + if ($res = $this->db->query($query)) { + $row = $this->db->fetchInto($res); + if (is_array($row)) { + $data = array( + $row['expires'], + $this->decode($cached_data), + $row['userdata'] + ); + } else { + $data = array(null, null, null); + } + } else { + $data = array(null, null, null); + } + } else { + return new Cache_Error('MDB::query failed: ' + . $clob->getMessage(), __FILE__, __LINE__); + } + } + $this->db->freeResult($res); + } else { + //return new Cache_Error('MDB::query failed: ' + // . $result->getMessage(), __FILE__, __LINE__); + $data = array(null, null, null); + } + + // last used required by the garbage collection + $query = 'UPDATE ' . $this->cache_table + .' SET changed=' . time() + .' WHERE id=' . $this->db->getTextValue($id) + .' AND cachegroup=' . $this->db->getTextValue($group); + + $res = $this->db->query($query); + if (MDB::isError($res)) { + return new Cache_Error('MDB::query failed: ' + . $this->db->errorMessage($res), __FILE__, __LINE__); + } + return $data; + } + + /** + * Stores a dataset in the database + * + * If dataset_ID already exists, overwrite it with new data, + * else insert data in a new record. + * + * @param string dataset ID + * @param mixed data to be cached + * @param integer expiration time + * @param string cache group + * @param string userdata + * @access public + */ + function save($id, $data, $expires, $group, $userdata) + { + global $db; + $this->flushPreload($id, $group); + + $fields = array( + 'id' => array( + 'Type' => 'text', + 'Value' => $id, + 'Key' => true + ), + 'userdata' => array( + 'Type' => 'integer', + 'Value' => $userdata, + 'null' => ($userdata ? false : true) + ), + 'expires' => array( + 'Type' => 'integer', + 'Value' => $this->getExpiresAbsolute($expires) + ), + 'cachegroup' => array( + 'Type' => 'text', + 'Value' => $group + ) + ); + + $result = $this->db->replace($this->cache_table, $fields); + + if (MDB::isError($result)) { + //Var_Dump::display($result); + return new Cache_Error('MDB::query failed: ' + . $this->db->errorMessage($result), __FILE__, __LINE__); + } + unset($fields); //end first part of query + $query2 = 'UPDATE ' . $this->cache_table + .' SET cachedata=?' + .' WHERE id='. $this->db->getTextValue($id); + + if (($prepared_query = $this->db->prepareQuery($query2))) { + $char_lob = array( + 'Error' => '', + 'Type' => 'data', + 'Data' => $this->encode($data) + ); + if (!MDB::isError($clob = $this->db->createLob($char_lob))) { + $this->db->setParamClob($prepared_query,1,$clob,'cachedata'); + if(MDB::isError($error=$this->db->executeQuery($prepared_query))) { + return new Cache_Error('MDB::query failed: ' + . $error->getMessage() , __FILE__, __LINE__); + } + $this->db->destroyLob($clob); + } else { + // creation of the handler object failed + return new Cache_Error('MDB::query failed: ' + . $clob->getMessage() , __FILE__, __LINE__); + } + $this->db->freePreparedQuery($prepared_query); + } else { + //prepared query failed + return new Cache_Error('MDB::query failed: ' + . $prepared_query->getMessage() , __FILE__, __LINE__); + } + } + + /** + * Removes a dataset from the database + * + * @param string dataset ID + * @param string cache group + */ + function remove($id, $group) + { + $this->flushPreload($id, $group); + + $query = 'DELETE FROM ' . $this->cache_table + .' WHERE id=' . $this->db->getTextValue($id) + .' AND cachegroup=' . $this->db->getTextValue($group); + + $res = $this->db->query($query); + if (MDB::isError($res)) { + return new Cache_Error('MDB::query failed: ' + . $this->db->errorMessage($res), __FILE__, __LINE__); + } + } + + /** + * Remove all cached data for a certain group, or empty + * the cache table if no group is specified. + * + * @param string cache group + */ + function flush($group = '') + { + $this->flushPreload(); + + if ($group) { + $query = 'DELETE FROM ' . $this->cache_table + .' WHERE cachegroup=' . $this->db->getTextValue($group); + } else { + $query = 'DELETE FROM ' . $this->cache_table; + } + + $res = $this->db->query($query); + if (MDB::isError($res)) { + return new Cache_Error('MDB::query failed: ' + . $this->db->errorMessage($res), __FILE__, __LINE__); + } + } + + /** + * Check if a dataset ID/group exists. + * + * @param string dataset ID + * @param string cache group + * @return boolean + */ + function idExists($id, $group) + { + $query = 'SELECT id FROM ' . $this->cache_table + .' WHERE id=' . $this->db->getTextValue($id) + .' AND cachegroup=' . $this->db->getTextValue($group); + echo $query; + $res = $this->db->query($query); + if (MDB::isError($res)) { + return new Cache_Error('MDB::query failed: ' + . $this->db->errorMessage($res), __FILE__, __LINE__); + } + $row = $this->db->fetchInto($res); + + if (is_array($row)) { + return true; + } + return false; + } + + /** + * Garbage collector. + * + * @param int maxlifetime + */ + function garbageCollection($maxlifetime) + { + $this->flushPreload(); + $query = 'DELETE FROM ' . $this->cache_table + .' WHERE (expires <= ' . time() + .' AND expires > 0) OR changed <= '. time() - $maxlifetime; + + $res = $this->db->query($query); + + $query = 'SELECT sum(length(cachedata)) as CacheSize FROM ' + . $this->cache_table; + + $cachesize = $this->db->getOne($query); + if (MDB::isError($cachesize)) { + return new Cache_Error('MDB::query failed: ' + . $this->db->errorMessage($cachesize), __FILE__, __LINE__); + } + //if cache is to big. + if ($cachesize > $this->highwater) { + //find the lowwater mark. + $query = 'SELECT length(cachedata) as size, changed FROM ' + . $this->cache_table .' ORDER BY changed DESC'; + + $res = $this->db->query($query); + if (MDB::isError($res)) { + return new Cache_Error('MDB::query failed: ' + . $this->db->errorMessage($res), __FILE__, __LINE__); + } + $numrows = $this->db->numRows($res); + $keep_size = 0; + while ($keep_size < $this->lowwater && $numrows--) { + $entry = $this->db->fetchInto($res,MDB_FETCHMODE_ASSOC); + $keep_size += $entry['size']; + } + + //delete all entries, which were changed before the "lowwater mark" + $query = 'DELETE FROM ' . $this->cache_table + .' WHERE changed<='.($entry['changed'] ? $entry['changed'] : 0); + + $res = $this->db->query($query); + if (MDB::isError($res)) { + return new Cache_Error('MDB::query failed: ' + . $this->db->errorMessage($res), __FILE__, __LINE__); + } + } + } + +} +?> \ No newline at end of file Added: trunk/lib/pear/Cache/Container/mdb_cache_schema.xml =================================================================== --- trunk/lib/pear/Cache/Container/mdb_cache_schema.xml (rev 0) +++ trunk/lib/pear/Cache/Container/mdb_cache_schema.xml 2012-11-20 09:06:44 UTC (rev 8494) @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="ISO-8859-1" ?> +<database> + + <name>dbname</name> + <create>1</create> + <descrip... [truncated message content] |