]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/pear/Cache/Container.php
Allow bold, italics or underlined for numbers
[SourceForge/phpwiki.git] / lib / pear / Cache / Container.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 // |          Christian Stocker <chregu@phant.ch>                         |
18 // +----------------------------------------------------------------------+
19 //
20 // $Id: Container.php 293863 2010-01-23 03:46:52Z clockwerx $
21
22 require_once 'Cache/Error.php';
23
24 /**
25 * Common base class of all cache storage container.
26 *
27 * To speed up things we do a preload you should know about, otherwise it might
28 * play you a trick. The Cache controller classes (Cache/Cache, Cache/Output, ...)
29 * usually do something like is (isCached($id) && !isExpired($id)) return $container->load($id).
30 * if you implement isCached(), isExpired() and load() straight ahead, each of this
31 * functions will result in a storage medium (db, file,...) access. This generates too much load.
32 * Now, a simple speculative preload should saves time in most cases. Whenever
33 * one of the mentioned methods is invoked we preload the cached dataset into class variables.
34 * That means that we have only one storage medium access for the sequence
35 *  (isCached($id) && !isExpired($id)) return $container->load($id).
36 * The bad thing is that the preloaded data might be outdated meanwhile, which is
37 * unlikely but for you power users, be warned. If you do not want the preload
38 * you should switch it off by setting the class variable $preload to false. Anyway, this is
39 * not recommended!
40 *
41 * @author   Ulf Wendel <ulf.wendel@phpdoc.de>
42 * @version  $Id: Container.php 293863 2010-01-23 03:46:52Z clockwerx $
43 * @package  Cache
44 * @access   public
45 * @abstract
46 */
47 class Cache_Container
48 {
49
50     /**
51     * Flag indicating wheter to preload datasets.
52     *
53     * See the class description for more details.
54     *
55     * @var  boolean
56     */
57     var $preload = true;
58
59     /**
60     * ID of a preloaded dataset
61     *
62     * @var  string
63     */
64     var $id = '';
65
66     /**
67     * Cache group of a preloaded dataset
68     *
69     * @var  string
70     */
71     var $group = '';
72
73     /**
74     * Expiration timestamp of a preloaded dataset.
75     *
76     * @var  integer 0 means never, endless
77     */
78     var $expires = 0;
79
80     /**
81     * Value of a preloaded dataset.
82     *
83     * @var  string
84     */
85     var $cachedata = '';
86
87     /**
88     * Preloaded userdata field.
89     *
90     * @var  string
91     */
92     var $userdata = '';
93
94     /**
95     * Flag indicating that the dataset requested for preloading is unknown.
96     *
97     * @var  boolean
98     */
99     var $unknown = true;
100
101     /**
102     * Encoding mode for cache data: base64 or addslashes() (slash).
103     *
104     * @var  string  base64 or slash
105     */
106     var $encoding_mode = 'base64';
107
108     /**
109     * Highwater mark - maximum space required by all cache entries.
110     *
111     * Whenever the garbage collection runs it checks the amount of space
112     * required by all cache entries. If it's more than n (highwater) bytes
113     * the garbage collection deletes as many entries as necessary to reach the
114     * lowwater mark.
115     *
116     * @var  int
117     * @see  lowwater
118     */
119     var $highwater = 2048000;
120
121
122     /**
123     * Lowwater mark
124     *
125     * @var  int
126     * @see  highwater
127     */
128     var $lowwater = 1536000;
129
130
131     /**
132     * Options that can be set in every derived class using it's constructor.
133     *
134     * @var  array
135     */
136     var $allowed_options = array('encoding_mode', 'highwater', 'lowwater');
137
138
139     /**
140     * Loads a dataset from the cache.
141     *
142     * @param    string  dataset ID
143     * @param    string  cache group
144     * @return   mixed   dataset value or null on failure
145     * @access   public
146     */
147     function load($id, $group)
148     {
149         if ($this->preload) {
150             if ($this->id != $id || $this->group != $group) {
151                 $this->preload($id, $group);
152             }
153             return $this->cachedata;
154         }
155
156         $ret = $this->fetch($id, $group);
157         if (PEAR::isError($ret)) {
158             return $ret;
159         }
160
161         list( , $data, ) = $ret;
162         return $data;
163     } // end func load
164
165     /**
166     * Returns the userdata field of a cached data set.
167     *
168     * @param    string  dataset ID
169     * @param    string  cache group
170     * @return   string  userdata
171     * @access   public
172     */
173     function getUserdata($id, $group)
174     {
175         if ($this->preload) {
176             if ($this->id != $id || $this->group != $group) {
177                 $this->preload($id, $group);
178             }
179             return $this->userdata;
180         }
181
182         $ret = $this->fetch($id, $group);
183         if (PEAR::isError($ret)) {
184             return $ret;
185         }
186
187         list( , , $userdata) = $ret;
188         return $userdata;
189     } // end func getUserdata
190
191     /**
192     * Checks if a dataset is expired.
193     *
194     * @param    string  dataset ID
195     * @param    string  cache group
196     * @param    integer maximum age timestamp
197     * @return   boolean
198     * @access   public
199     */
200     function isExpired($id, $group, $max_age)
201     {
202         if ($this->preload) {
203             if ($this->id != $id || $this->group != $group) {
204                 $this->preload($id, $group);
205             }
206             if ($this->unknown) {
207                 return false;
208             }
209         } else {
210             // check if at all it is cached
211             if (!$this->isCached($id, $group)) {
212                 return false;
213             }
214             // I'm lazy...
215             $ret = $this->fetch($id, $group);
216             if (PEAR::isError($ret)) {
217                 return $ret;
218             }
219
220             list($this->expires, , ) = $ret;
221         }
222
223         // endless
224         if (0 == $this->expires) {
225             return false;
226         }
227         // you feel fine, Ulf?
228         if ($expired  = ($this->expires <= time() || ($max_age && ($this->expires <= $max_age))) ) {
229
230            $this->remove($id, $group);
231            $this->flushPreload();
232         }
233         return $expired;
234     } // end func isExpired
235
236     /**
237     * Checks if a dataset is cached.
238     *
239     * @param    string  dataset ID
240     * @param    string  cache group
241     * @return   boolean
242     */
243     function isCached($id, $group)
244     {
245         if ($this->preload) {
246             if ($this->id != $id || $this->group != $group) {
247                 $this->preload($id, $group);
248             }
249             return !($this->unknown);
250         }
251         return $this->idExists($id, $group);
252     } // end func isCached
253
254     //
255     // abstract methods
256     //
257
258     /**
259     * Fetches a dataset from the storage medium.
260     *
261     * @param    string  dataset ID
262     * @param    string  cache group
263     * @return   array   format: [expire date, cached data, user data]
264     * @throws   Cache_Error
265     * @abstract
266     */
267     function fetch($id, $group)
268     {
269         return array(null, null, null);
270     } // end func fetch
271
272     /**
273     * Stores a dataset.
274     *
275     * @param    string  dataset ID
276     * @param    mixed   data to store
277     * @param    mixed   userdefined expire date
278     * @param    string  cache group
279     * @param    string  additional userdefined data
280     * @return   boolean
281     * @throws   Cache_Error
282     * @access   public
283     * @abstract
284     */
285     function save($id, $data, $expire, $group, $userdata)
286     {
287         // QUESTION: Should we update the preload buffer instead?
288         // Don't think so as the sequence save()/load() is unlikely.
289         $this->flushPreload($id, $group);
290         return null;
291     } // end func save
292
293     /**
294     * Removes a dataset.
295     *
296     * @param    string  dataset ID
297     * @param    string  cache group
298     * @return   boolean
299     * @access   public
300     * @abstract
301     */
302     function remove($id, $group)
303     {
304         $this->flushPreload($id, $group);
305         return null;
306     } // end func remove
307
308     /**
309     * Flushes the cache - removes all caches datasets from the cache.
310     *
311     * @param    string      If a cache group is given only the group will be flushed
312     * @return   integer     Number of removed datasets, -1 on failure
313     * @access   public
314     * @abstract
315     */
316     function flush($group)
317     {
318         $this->flushPreload();
319         return null;
320     } // end func flush
321
322     /**
323     * Checks if a dataset exists.
324     *
325     * @param    string  dataset ID
326     * @param    string  cache group
327     * @return   boolean
328     * @access   public
329     * @abstract
330     */
331     function idExists($id, $group)
332     {
333         return null;
334     } // end func idExists
335
336     /**
337     * Starts the garbage collection.
338     *
339     * @param int $gc_maxlifetime The maximum lifetime (seconds) for a cache
340     *                            entry. Implemented by containers,
341     *
342     * @access   public
343     * @abstract
344     */
345     function garbageCollection($gc_maxlifetime)
346     {
347         $this->flushPreload();
348     } // end func garbageCollection
349
350     /**
351     * Does a speculative preload of a dataset
352     *
353     * @param    string  dataset ID
354     * @param    string  cache group
355     * @return   boolean
356     */
357     function preload($id, $group)
358     {
359         // whatever happens, remember the preloaded ID
360         $this->id = $id;
361         $this->group = $group;
362
363         $ret = $this->fetch($id, $group);
364         if (PEAR::isError($ret)) {
365             return $ret;
366         }
367
368         list($this->expires, $this->cachedata, $this->userdata) = $ret;
369         if ($this->expires === null) {
370             // Uuups, unknown ID
371             $this->flushPreload();
372             return false;
373         }
374
375         $this->unknown = false;
376
377         return true;
378     } // end func preload
379
380     /**
381     * Flushes the internal preload buffer.
382     *
383     * save(), remove() and flush() must call this method
384     * to preevent differences between the preloaded values and
385     * the real cache contents.
386     *
387     * @param    string  dataset ID, if left out the preloaded values will be flushed.
388     *                   If given the preloaded values will only be flushed if they are
389     *                   equal to the given id and group
390     * @param    string  cache group
391     * @see  preload()
392     */
393     function flushPreload($id = '', $group = 'default')
394     {
395         if (!$id || ($this->id == $id && $this->group == $group)) {
396             // clear the internal preload values
397             $this->id = '';
398             $this->group = '';
399             $this->cachedata = '';
400             $this->userdata = '';
401             $this->expires = -1;
402             $this->unknown = true;
403         }
404     } // end func flushPreload
405
406     /**
407     * Imports the requested datafields as object variables if allowed
408     *
409     * @param    array   List of fields to be imported as object variables
410     * @param    array   List of allowed datafields
411     */
412     function setOptions($requested, $allowed)
413     {
414         foreach ($allowed as $k => $field) {
415             if (isset($requested[$field])) {
416                 $this->$field = $requested[$field];
417             }
418         }
419     } // end func setOptions
420
421     /**
422     * Encodes the data for the storage container.
423     *
424     * @var  mixed data to encode
425     */
426     function encode($data)
427     {
428         if ($this->encoding_mode == 'base64') {
429             return base64_encode(serialize($data));
430         } else {
431             return serialize($data);
432         }
433     } // end func encode
434
435
436     /**
437     * Decodes the data from the storage container.
438     *
439     * @var  mixed
440     */
441     function decode($data)
442     {
443         if ($this->encoding_mode == 'base64') {
444             return unserialize(base64_decode($data));
445         } else {
446             return unserialize($data);
447         }
448     } // end func decode
449
450
451     /**
452     * Translates human readable/relative times in unixtime
453     *
454     * @param  mixed   can be in the following formats:
455     *               human readable          : yyyymmddhhmm[ss]] eg: 20010308095100
456     *               relative in seconds (1) : +xx              eg: +10
457     *               relative in seconds (2) : x <  946681200   eg: 10
458     *               absolute unixtime       : x < 2147483648   eg: 2147483648
459     *               see comments in code for details
460     * @return integer unix timestamp
461     */
462     function getExpiresAbsolute($expires)
463     {
464         if (!$expires) {
465             return 0;
466         }
467         //for api-compatibility, one has not to provide a "+",
468         // if integer is < 946681200 (= Jan 01 2000 00:00:00)
469         if ($expires[0] == '+' || $expires < 946681200) {
470             return(time() + $expires);
471         } elseif ($expires < 100000000000) {
472             //if integer is < 100000000000 (= in 3140 years),
473             // it must be an absolut unixtime
474             // (since the "human readable" definition asks for a higher number)
475             return $expires;
476         } else {
477             // else it's "human readable";
478             $year = substr($expires, 0, 4);
479             $month = substr($expires, 4, 2);
480             $day = substr($expires, 6, 2);
481             $hour = substr($expires, 8, 2);
482             $minute = substr($expires, 10, 2);
483             $second = substr($expires, 12, 2);
484             return mktime($hour, $minute, $second, $month, $day, $year);
485         }
486
487     } // end func getExpireAbsolute
488
489 } // end class Container
490 ?>