fix image text
[pear] / Cache.php
1 <?php
2 // +----------------------------------------------------------------------+
3 // | PEAR :: Cache                                                        |
4 // +----------------------------------------------------------------------+
5 // | Copyright (c) 1997-2003 The PHP Group                                |
6 // +----------------------------------------------------------------------+
7 // | This source file is subject to version 2.0 of the PHP license,       |
8 // | that is bundled with this package in the file LICENSE, and is        |
9 // | available at through the world-wide-web at                           |
10 // | http://www.php.net/license/2_02.txt.                                 |
11 // | If you did not receive a copy of the PHP license and are unable to   |
12 // | obtain it through the world-wide-web, please send a note to          |
13 // | license@php.net so we can mail you a copy immediately.               |
14 // +----------------------------------------------------------------------+
15 // | Authors: Ulf Wendel <ulf.wendel@phpdoc.de>                           |
16 // |          Sebastian Bergmann <sb@sebastian-bergmann.de>               |
17 // +----------------------------------------------------------------------+
18 //
19 // $Id: Cache.php 267047 2008-10-07 08:58:46Z dufuz $
20
21 require_once 'PEAR.php';
22 require_once 'Cache/Error.php';
23
24 /**
25 * Cache is a base class for cache implementations.
26 *
27 * The pear cache module is a generic data cache which can be used to
28 * cache script runs. The idea behind the cache is quite simple. If you have
29 * the same input parameters for whatever tasks/algorithm you use you'll
30 * usually get the same output. So why not caching templates, functions calls,
31 * graphic generation etc. Caching certain actions e.g. XSLT tranformations
32 * saves you lots of time.
33 *
34 * The design of the cache reminds of PHPLibs session implementation. A
35 * (PHPLib: session) controller uses storage container (PHPLib: ct_*.inc) to save
36 * certain data (PHPLib: session data). In contrast to the session stuff it's up to
37 * you to generate an ID for the data to cache. If you're using the output cache
38 * you might use the script name as a seed for cache::generateID(), if your using the
39 * function cache you'd use an array with all function parameters.
40 *
41 * Usage example of the generic data cache:
42 *
43 * require_once('Cache.php');
44 *
45 * $cache = new Cache('file', array('cache_dir' => 'cache/') );
46 * $id = $cache->generateID('testentry');
47 *
48 * if ($data = $cache->get($id)) {
49 *    print "Cache hit.<br>Data: $data";
50 *
51 * } else {
52 *   $data = 'data of any kind';
53 *   $cache->save($id, $data);
54 *   print 'Cache miss.<br>';
55 * }
56 *
57 * WARNING: No File/DB-Table-Row locking is implemented yet,
58 *          it's possible, that you get corrupted data-entries under
59 *          bad circumstances  (especially with the file container)
60 *
61 * @author   Ulf Wendel <ulf.wendel@phpdoc.de>
62 * @version  $Id: Cache.php 267047 2008-10-07 08:58:46Z dufuz $
63 * @package  Cache
64 * @access   public
65 */
66 class Cache extends PEAR
67 {
68
69     /**
70     * Enables / disables caching.
71     *
72     * TODO: Add explanation what this is good for.
73     *
74     * @var      boolean
75     * @access   private
76     */
77     var $caching = true;
78
79     /**
80     * Garbage collection: probability in seconds
81     *
82     * If set to a value above 0 a garbage collection will
83     * flush all cache entries older than the specified number
84     * of seconds.
85     *
86     * @var      integer
87     * @see      $gc_probability, $gc_maxlifetime
88     * @access   public
89     */
90     var $gc_time  = 1;
91
92     /**
93     * Garbage collection: probability in percent
94     *
95     * TODO: Add an explanation.
96     *
97     * @var      integer     0 => never
98     * @see      $gc_time, $gc_maxlifetime
99     * @access   public
100     */
101     var $gc_probability = 1;
102
103     /**
104     * Garbage collection: delete all entries not use for n seconds.
105     *
106     * Default is one day, 60 * 60 * 24 = 86400 seconds.
107     *
108     * @var  integer
109     * @see  $gc_probability, $gc_time
110     */
111     var $gc_maxlifetime = 86400;
112
113     /**
114     * Storage container object.
115     *
116     * @var  object Cache_Container
117     */
118     var $container;
119
120     //
121     // public methods
122     //
123
124     /**
125     *
126     * @param    string  Name of container class
127     * @param    array   Array with container class options
128     */
129     function Cache($container, $container_options = '')
130     {
131         parent::__construct();
132         $container = strtolower($container);
133         $container_class = 'Cache_Container_' . $container;
134         $container_classfile = 'Cache/Container/' . $container . '.php';
135
136         include_once $container_classfile;
137         $this->container = new $container_class($container_options);
138     }
139
140     //deconstructor
141     function _Cache()
142     {
143         $this->garbageCollection();
144     }
145
146     /**
147      * Returns the current caching state.
148      *
149      * @return  boolean     The current caching state.
150      * @access  public
151      */
152     function getCaching()
153     {
154         return $this->caching;
155     }
156
157     /**
158      * Enables or disables caching.
159      *
160      * @param   boolean     The new caching state.
161      * @access  public
162      */
163     function setCaching($state)
164     {
165         $this->caching = $state;
166     }
167
168     /**
169     * Returns the requested dataset it if exists and is not expired
170     *
171     * @param    string  dataset ID
172     * @param    string  cache group
173     * @return   mixed   cached data or null on failure
174     * @access   public
175     */
176     function get($id, $group = 'default')
177     {
178         if (!$this->caching) {
179             return '';
180         }
181
182         if ($this->isCached($id, $group) && !$this->isExpired($id, $group)) {
183             return $this->load($id, $group);
184         }
185         return null;
186     } // end func get
187
188     /**
189     * Stores the given data in the cache.
190     *
191     * @param    string  dataset ID used as cache identifier
192     * @param    mixed   data to cache
193     * @param    integer lifetime of the cached data in seconds - 0 for endless
194     * @param    string  cache group
195     * @return   boolean
196     * @access   public
197     */
198     function save($id, $data, $expires = 0, $group = 'default')
199     {
200         if (!$this->caching) {
201             return true;
202         }
203         return $this->extSave($id, $data, '',$expires, $group);
204     } // end func save
205
206     /**
207     * Stores a dataset with additional userdefined data.
208     *
209     * @param    string  dataset ID
210     * @param    mixed   data to store
211     * @param    string  additional userdefined data
212     * @param    mixed   userdefined expire date
213     * @param    string  cache group
214     * @return   boolean
215     * @throws   Cache_Error
216     * @access   public
217     * @see      getUserdata()
218     */
219     function extSave($id, $cachedata, $userdata, $expires = 0, $group = 'default')
220     {
221         if (!$this->caching) {
222             return true;
223         }
224         return $this->container->save($id, $cachedata, $expires, $group, $userdata);
225     } // end func extSave
226
227     /**
228     * Loads the given ID from the cache.
229     *
230     * @param    string  dataset ID
231     * @param    string  cache group
232     * @return   mixed   cached data or null on failure
233     * @access   public
234     */
235     function load($id, $group = 'default')
236     {
237         if (!$this->caching) {
238             return '';
239         }
240         return $this->container->load($id, $group);
241     } // end func load
242
243     /**
244     * Returns the userdata field of a cached data set.
245     *
246     * @param    string  dataset ID
247     * @param    string  cache group
248     * @return   string  userdata
249     * @access   public
250     * @see      extSave()
251     */
252     function getUserdata($id, $group = 'default')
253     {
254         if (!$this->caching) {
255             return '';
256         }
257         return $this->container->getUserdata($id, $group);
258     } // end func getUserdata
259
260     /**
261     * Removes the specified dataset from the cache.
262     *
263     * @param    string  dataset ID
264     * @param    string  cache group
265     * @return   boolean
266     * @access   public
267     */
268     function remove($id, $group = 'default')
269     {
270         if (!$this->caching) {
271             return true;
272         }
273         return $this->container->remove($id, $group);
274     } // end func remove
275
276     /**
277     * Flushes the cache - removes all data from it
278     *
279     * @param    string  cache group, if empty all groups will be flashed
280     * @return   integer number of removed datasets
281     */
282     function flush($group = 'default')
283     {
284         if (!$this->caching) {
285             return true;
286         }
287         return $this->container->flush($group);
288     } // end func flush
289
290     /**
291     * Checks if a dataset exists.
292     *
293     * Note: this does not say that the cached data is not expired!
294     *
295     * @param    string  dataset ID
296     * @param    string  cache group
297     * @return   boolean
298     * @access   public
299     */
300     function isCached($id, $group = 'default')
301     {
302         if (!$this->caching) {
303             return false;
304         }
305         return $this->container->isCached($id, $group);
306     } // end func isCached
307
308     /**
309     * Checks if a dataset is expired
310     *
311     * @param    string  dataset ID
312     * @param    string  cache group
313     * @param    integer maximum age for the cached data in seconds - 0 for endless
314     *                   If the cached data is older but the given lifetime it will
315     *                   be removed from the cache. You don't have to provide this
316     *                   argument if you call isExpired(). Every dataset knows
317     *                   it's expire date and will be removed automatically. Use
318     *                   this only if you know what you're doing...
319     * @return   boolean
320     * @access   public
321     */
322     function isExpired($id, $group = 'default', $max_age = 0)
323     {
324         if (!$this->caching) {
325             return true;
326         }
327         return $this->container->isExpired($id, $group, $max_age);
328     } // end func isExpired
329
330     /**
331     * Generates a "unique" ID for the given value
332     *
333     * This is a quick but dirty hack to get a "unique" ID for a any kind of variable.
334     * ID clashes might occur from time to time although they are extreme unlikely!
335     *
336     * @param    mixed   variable to generate a ID for
337     * @return   string  "unique" ID
338     * @access   public
339     */
340     function generateID($variable)
341     {
342         // WARNING: ID clashes are possible although unlikely
343         return md5(serialize($variable));
344     }
345
346     /**
347     * Calls the garbage collector of the storage object with a certain probability
348     *
349     * @param    boolean Force a garbage collection run?
350     * @see  $gc_probability, $gc_time
351     */
352     function garbageCollection($force = false)
353     {
354         static $last_run = 0;
355
356         if (!$this->caching) {
357             return;
358         }
359
360         // time and probability based
361         if (($force) || ($last_run && $last_run < time() + $this->gc_time) || (rand(1, 100) < $this->gc_probability)) {
362             $this->container->garbageCollection($this->gc_maxlifetime);
363             $last_run = time();
364         }
365     } // end func garbageCollection
366
367 } // end class cache
368 ?>