Commit 061d1a0b authored by Eric Rasmussen's avatar Eric Rasmussen
Browse files

Add UNL_Cache_Lite to lib

parent a9b1d75c
No preview for this file type
<?xml version="1.0" encoding="UTF-8"?>
<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.9.0" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
<name>UNL_Cache_Lite</name>
<channel>pear.unl.edu</channel>
<summary>Basic caching library</summary>
<description>This is a port of the Cache_Lite package from PEAR, with support for PHP 5 and
exceptions.
This package is a little cache system optimized for file containers. It is fast
and safe (because it uses file locking and/or anti-corruption tests).</description>
<lead>
<name>Brett Bieber</name>
<user>saltybeagle</user>
<email>brett.bieber@gmail.com</email>
<active>yes</active>
</lead>
<date>2010-10-08</date>
<time>11:49:16</time>
<version>
<release>0.1.0</release>
<api>0.1.0</api>
</version>
<stability>
<release>beta</release>
<api>beta</api>
</stability>
<license uri="http://www.gnu.org/licenses/lgpl-3.0.txt">LGPL</license>
<notes>
Port of cache lite, remove PEAR dependency, use exceptions instead of PEAR_Error.
</notes>
<contents>
<dir name="/">
<file baseinstalldir="/" md5sum="ea88bd9816a106e30ac2939a28679fc7" name="UNL/Cache/Lite/Output.php" role="php"/>
<file baseinstalldir="/" md5sum="537413f3d9e949be31a1dd9f42c1dd5c" name="UNL/Cache/Lite/NestedOutput.php" role="php"/>
<file baseinstalldir="/" md5sum="95bccdc3a6ec0d54ab4adf3ce03d4b55" name="UNL/Cache/Lite/Function.php" role="php"/>
<file baseinstalldir="/" md5sum="33a3fc682d63cef8f5cf126637001662" name="UNL/Cache/Lite/File.php" role="php"/>
<file baseinstalldir="/" md5sum="7f02fa5030fe224535a8849aa0d98741" name="UNL/Cache/Lite.php" role="php"/>
<file baseinstalldir="/" md5sum="4c638b951a7c8a4dcb94f10abf1d818b" name="TODO" role="data"/>
<file baseinstalldir="/" md5sum="085e7fb76fb3fa8ba9e9ed0ce95a43f9" name="LICENSE" role="data"/>
<file baseinstalldir="/" md5sum="934071f21c17611811e01396ca604c79" name="docs/technical" role="doc"/>
<file baseinstalldir="/" md5sum="516beafae66ea4b9fd7d29ab9c90845d" name="docs/examples" role="doc"/>
</dir>
</contents>
<dependencies>
<required>
<php>
<min>5.1.2</min>
</php>
<pearinstaller>
<min>1.5.4</min>
</pearinstaller>
</required>
</dependencies>
<phprelease>
<changelog>
<release>
<version>
<release>0.1.0</release>
<api>0.1.0</api>
</version>
<stability>
<release>beta</release>
<api>beta</api>
</stability>
<date>2009-10-20</date>
<license uri="http://www.gnu.org/licenses/lgpl-3.0.txt">LGPL</license>
<notes>
Port of cache lite, remove PEAR dependency, use exceptions instead of PEAR_Error.
</notes>
</release>
</changelog>
</phprelease>
</package>
This diff is collapsed.
Patrick O'Lone suggests the following idea which sounds interesting to
add as an optional mode of Cache_Lite class.
(still not tested in the Cache_Lite context)
-------------------------------------------------------------------------
If you use the flags:
ignore_user_abort(true);
$fd = dio_open($szFilename, O_CREATE | O_EXCL | O_TRUNC | O_WRONLY,
0644);
if (is_resource($fd)) {
dio_fcntl($fd, F_SETLKW, array('type' => F_WRLCK));
dio_write($fd, $szBuffer);
dio_fcntl($fd, F_SETLK, array('type' => F_UNLCK));
dio_close($fd);
}
ignore_user_abort(false);
Only the first process will attempt to create a file. Additional
processes will see that a file already exists (at the system level), and
will fail. Another thing to note is that the file descriptor must be
opened using dio_open(), and certain features, like fgets() won't work
with it. If your just doing a raw write, dio_write() should be just
fine. The dio_read() function should be used like:
$fd = dio_open($szFilename, O_RDONLY|O_NONBLOCK, 0644);
if (is_resource($fd)) {
dio_fcntl($fd, F_SETLKW, array('type' => F_RDLCK));
$szBuffer = dio_read($fd, filesize($szFilename));
dio_fcntl($fd, F_SETLK, array('type' => F_UNLCK));
dio_close($fd);
}
You still use locking to ensure that a write process can finish before
another attempts to read the file. We also set non-blocking mode in read
mode so that multiple readers can access the same resource at the same
time. NOTE: Direct I/O support must be compiled into PHP for these
features to work (--enable-dio).
-------------------------------------------------------------------------
A few examples of Cache_Lite using :
------------------------------------
>>> Basic one :
<?php
// Include the package
require_once('Cache/Lite.php');
// Set a id for this cache
$id = '123';
// Set a few options
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 3600
);
// Create a Cache_Lite object
$Cache_Lite = new Cache_Lite($options);
// Test if thereis a valide cache for this id
if ($data = $Cache_Lite->get($id)) {
// Cache hit !
// Content is in $data
// (...)
} else { // No valid cache found (you have to make the page)
// Cache miss !
// Put in $data datas to put in cache
// (...)
$Cache_Lite->save($data);
}
?>
>>> Usage with blocks
(You can use Cache_Lite for caching blocks and not the whole page)
<?php
require_once('Cache/Lite.php');
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 3600
);
// Create a Cache_Lite object
$Cache_Lite = new Cache_Lite($options);
if ($data = $Cache_Lite->get('block1')) {
echo($data);
} else {
$data = 'Data of the block 1';
$Cache_Lite->save($data);
}
echo('<br><br>Non cached line !<br><br>');
if ($data = $Cache_Lite->get('block2')) {
echo($data);
} else {
$data = 'Data of the block 2';
$Cache_Lite->save($data);
}
?>
A few examples of Cache_Lite_Output using :
-------------------------------------------
>>> Basic one :
<?php
require_once('Cache/Lite/Output.php');
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 10
);
$cache = new Cache_Lite_Output($options);
if (!($cache->start('123'))) {
// Cache missed...
for($i=0;$i<1000;$i++) { // Making of the page...
echo('0123456789');
}
$cache->end();
}
?>
>>> Usage with blocks :
(You can use Cache_Lite_Output for caching blocks and not the whole page)
<?php
require_once('Cache/Lite/Output.php');
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 10
);
$cache = new Cache_Lite_Output($options);
if (!($cache->start('block1'))) {
// Cache missed...
echo('Data of the block 1 !<br>');
$cache->end();
}
echo('<br><br>Non cached line !<br><br>');
if (!($cache->start('block2'))) {
// Cache missed...
echo('Data of the block 2 !<br>');
$cache->end();
}
A few examples of Cache_Lite_Function using :
---------------------------------------------
>>> With function :
<?php
require_once('Cache/Lite/Function.php');
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 10
);
$cache = new Cache_Lite_Function($options);
$cache->call('function_to_bench', 12, 45);
function function_to_bench($arg1, $arg2)
{
echo "This is the output of the function function_to_bench($arg1, $arg2) !<br>";
return "This is the result of the function function_to_bench($arg1, $arg2) !<br>";
}
?>
>>> With method :
<?php
require_once('Cache/Lite/Function.php');
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 10
);
$cache = new Cache_Lite_Function($options);
$obj = new bench();
$obj->test = 666;
$cache->call('obj->method_to_bench', 12, 45);
class bench
{
var $test;
function method_to_bench($arg1, $arg2)
{
echo "\$obj->test = $this->test and this is the output of the method \$obj->method_to_bench($arg1, $arg2) !<br>";
return "\$obj->test = $this->test and this is the result of the method \$obj->method_to_bench($arg1, $arg2) !<br>";
}
}
?>
>>> With static method :
<?php
require_once('Cache/Lite/Function.php');
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 10
);
$cache = new Cache_Lite_Function($options);
$cache->call('bench::static_method_to_bench', 12, 45);
class bench
{
var $test;
function static_method_to_bench($arg1, $arg2) {
echo "This is the output of the function static_method_to_bench($arg1, $arg2) !<br>";
return "This is the result of the function static_method_to_bench($arg1, $arg2) !<br>";
}
}
?>
>>> IMPORTANT :
If you try to use Cache_Lite_Function with $this object ($cache->call('this->method',...)
for example), have a look first at :
http://pear.php.net/bugs/bug.php?id=660
A few examples of Cache_Lite_File using :
-----------------------------------------
<?php
$options = array(
'cacheDir' => '/tmp/',
'masterFile' => '/home/web/config.xml'
);
$cache = new Cache_Lite_File($options);
// Set a id for this cache
$id = '123';
if ($data = $cache->get($id)) {
// Cache hit !
// Content is in $data
// (...)
} else { // No valid cache found (you have to make the page)
// Cache miss !
// Put in $data datas to put in cache
// (...)
$cache->save($data);
}
?>
Technical choices for Cache_Lite...
-----------------------------------
To begin, the main goals of Cache_Lite :
- performances
- safe use (even on very high traffic or with NFS (file locking doesn't work
with NFS))
- flexibility (can be used by the end user or as a part of a larger script)
For speed reasons, it has been decided to focus on the file container (the
faster one). So, cache is only stored in files. The class is optimized for that.
If you want to use a different cache container, have a look to PEAR/Cache.
For speed reasons too, the class 'Cache_Lite' has do be independant (so no
'require_once' at all in 'Cache_Lite.php'). But, a conditional include_once
is allowed. For example, when an error is detected, the class include dynamicaly
the PEAR base class 'PEAR.php' to be able to use PEAR::raiseError(). But, in
most cases, PEAR.php isn't included.
For the second goal (safe use), there is three (optional) mecanisms :
- File Locking : seems to work fine (but not with distributed file system
like NFS...)
- WriteControl : the cache is read and compared just after being stored
(efficient but not perfect)
- ReadControl : a control key (crc32(), md5() ou strlen()) is embeded is the
cache file and compared just after reading (the most efficient
but the slowest)
This diff is collapsed.
<?php
/**
* This class extends UNL_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 UNL_Cache_Lite
* @version $Id: File.php 276823 2009-03-07 12:55:39Z tacker $
* @author Fabien MARTY <fab@php.net>
*/
class UNL_Cache_Lite_File extends UNL_Cache_Lite
{
// --- Private properties ---
/**
* Complete path of the file used for controlling the cache lifetime
*
* @var string $_masterFile
*/
protected $_masterFile = '';
/**
* Masterfile mtime
*
* @var int $_masterFile_mtime
*/
protected $_masterFile_mtime = 0;
// --- Public methods ----
/**
* Constructor
*
* $options is an assoc. To have a look at availables options,
* see the constructor of the UNL_Cache_Lite class in 'UNL_Cache_Lite.php'
*
* Comparing to UNL_Cache_Lite constructor, there is another option :
* $options = array(
* (...) see UNL_Cache_Lite constructor
* 'masterFile' => complete path of the file used for controlling the cache lifetime(string)
* );
*
* @param array $options options
* @access public
*/
function __construct($options = array(NULL))
{
$options['lifetime'] = 0;
$this->setOptions($options);
if (isset($options['masterFile'])) {
$this->_masterFile = $options['masterFile'];
} else {
return $this->raiseError('UNL_Cache_Lite_File : masterFile option must be set !');
}
if (!($this->_masterFile_mtime = @filemtime($this->_masterFile))) {
return $this->raiseError('UNL_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;
}
}
?>
<?php
/**
* This class extends UNL_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
* UNL_Cache_Lite
*
* There are some examples in the 'docs/examples' file
* Technical choices are described in the 'docs/technical' file
*
* @package UNL_Cache_Lite
* @version $Id: Function.php 225008 2006-12-14 12:59:43Z cweiske $
* @author Sebastian BERGMANN <sb@sebastian-bergmann.de>
* @author Fabien MARTY <fab@php.net>
*/
class UNL_Cache_Lite_Function extends UNL_Cache_Lite
{
// --- Private properties ---
/**
* Default cache group for function caching
*
* @var string $_defaultGroup
*/
protected $_defaultGroup = 'UNL_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
*/
protected $_dontCacheWhenTheOutputContainsNOCACHE = false;
/**
* Don't cache the method call when its result is false
*
* @var boolean $_dontCacheWhenTheResultIsFalse
*/
protected $_dontCacheWhenTheResultIsFalse = false;
/**
* Don't cache the method call when its result is null
*
* @var boolean $_dontCacheWhenTheResultIsNull
*/
protected $_dontCacheWhenTheResultIsNull = false;
/**
* Debug the UNL_Cache_Lite_Function caching process
*
* @var boolean $_debugCacheLiteFunction
*/
protected $_debugCacheLiteFunction = false;
// --- Public methods ----
/**
* Constructor
*
* $options is an assoc. To have a look at availables options,
* see the constructor of the UNL_Cache_Lite class in 'UNL_Cache_Lite.php'
*
* Comparing to UNL_Cache_Lite constructor, there is another option :
* $options = array(
* (...) see UNL_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 __construct($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->setOptions($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 {