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