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