source: trunk/lib/Cache.inc.php @ 611

Last change on this file since 611 was 611, checked in by anonymous, 7 years ago

Add additional clearings to logout service. Add logging to clear methods.

File size: 10.7 KB
Line 
1<?php
2/**
3 * The Strangecode Codebase - a general application development framework for PHP
4 * For details visit the project site: <http://trac.strangecode.com/codebase/>
5 * Copyright 2001-2012 Strangecode, LLC
6 *
7 * This file is part of The Strangecode Codebase.
8 *
9 * The Strangecode Codebase is free software: you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your option)
12 * any later version.
13 *
14 * The Strangecode Codebase is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * The Strangecode Codebase. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23/**
24 * Cache.inc.php
25 *
26 * Provides an API for storing a limited amount of data
27 * intended to have a short lifetime in a user's session.
28 *
29 * Disable cache per-request by adding '_disable_cache=1' to a GET or POST parameter.
30 *
31 * @author  Quinn Comendant <quinn@strangecode.com>
32 * @version 2.1
33 * @since   2001
34 */
35
36class Cache
37{
38
39    // A place to keep object instances for the singleton pattern.
40    protected static $instances = array();
41
42    // Namespace of this instance of Prefs.
43    protected $_ns;
44
45    // Configuration parameters for this object.
46    protected $_params = array(
47
48        // Type of cache. Currently only 'session' is supported.
49        'type' => 'session',
50
51        // If false nothing will be cached or retrieved. Useful for testing realtime data requests.
52        'enabled' => true,
53
54        // The maximum size in bytes of any one variable.
55        'item_size_limit' => 4194304, // 4 MB
56
57        // The maximum size in bytes before the cache will begin flushing out old items.
58        'stack_size_limit' => 4194304, // 4 MB
59
60        // The minimum items to keep in the cache regardless of item or cache size.
61        'min_items' => 5,
62    );
63
64    /*
65    * Constructor. This is publicly accessible for compatibility with older implementations,
66    * but the preferred method of instantiation is by use of the singleton pattern:
67    *   $cache =& Cache::getInstance('namespace');
68    *   $cache->setParam(array('enabled' => true));
69    *
70    * @access   public
71    * @param    string  $namespace  This object will store data under this realm.
72    * @author   Quinn Comendant <quinn@strangecode.com>
73    * @version  1.0
74    * @since    05 Jun 2006 23:14:21
75    */
76    public function __construct($namespace='')
77    {
78        $app =& App::getInstance();
79
80        $this->_ns = $namespace;
81
82        if (true !== $app->getParam('enable_session')) {
83            // Force disable the cache because there is no session to save to.
84            $app->logMsg('Cache disabled, enable_session != true.', LOG_DEBUG, __FILE__, __LINE__);
85            $this->setParam(array('enabled' => false));
86        } else if (!isset($_SESSION['_cache'][$this->_ns])) {
87            // Otherwise, clear to initialize the session variable.
88            $this->clear();
89        }
90    }
91
92    /**
93     * This method enforces the singleton pattern for this class.
94     *
95     * @return  object  Reference to the global Cache object.
96     * @access  public
97     * @static
98     */
99    public static function &getInstance($namespace='')
100    {
101        if (!array_key_exists($namespace, self::$instances)) {
102            self::$instances[$namespace] = new self($namespace);
103        }
104        return self::$instances[$namespace];
105    }
106
107    /**
108     * Set (or overwrite existing) parameters by passing an array of new parameters.
109     *
110     * @access public
111     * @param  array    $params     Array of parameters (key => val pairs).
112     */
113    public function setParam($params)
114    {
115        $app =& App::getInstance();
116
117        if (isset($params) && is_array($params)) {
118            // Merge new parameters with old overriding only those passed.
119            $this->_params = array_merge($this->_params, $params);
120        } else {
121            $app->logMsg(sprintf('Parameters are not an array: %s', $params), LOG_ERR, __FILE__, __LINE__);
122        }
123    }
124
125    /**
126     * Return the value of a parameter, if it exists.
127     *
128     * @access public
129     * @param string $param        Which parameter to return.
130     * @return mixed               Configured parameter value.
131     */
132    public function getParam($param)
133    {
134        $app =& App::getInstance();
135
136        if (array_key_exists($param, $this->_params)) {
137            return $this->_params[$param];
138        } else {
139            $app->logMsg(sprintf('Parameter is not set: %s', $param), LOG_DEBUG, __FILE__, __LINE__);
140            return null;
141        }
142    }
143
144    /**
145     * Stores a new variable in the session cache. The $key should not be numeric
146     * because the array_shift function will reset the key to the next largest
147     * int key. Weird behavior I can't understand. For example $cache["123"] will become $cache[0]
148     *
149     * @param str   $key                An identifier for the cached object.
150     * @param mixed $var                The data to store in the session cache.
151     * @param bool  $allow_oversized    If we have something really big that we still want to cache, setting this to true allows this.
152     * @return bool                     True on success, false otherwise.
153     */
154    public function set($key, $var, $allow_oversized=false)
155    {
156        $app =& App::getInstance();
157
158        if (true !== $this->getParam('enabled') || getFormData('_disable_cache')) {
159            $app->logMsg(sprintf('Cache disabled, not saving data.', null), LOG_DEBUG, __FILE__, __LINE__);
160            return false;
161        }
162
163        if (is_numeric($key)) {
164            $app->logMsg(sprintf('Cache::set key value should not be numeric (%s given)', $key), LOG_WARNING, __FILE__, __LINE__);
165        }
166
167        $var = serialize($var);
168        $var_len = mb_strlen($var);
169
170        if ($var_len >= $this->getParam('item_size_limit')) {
171            $app->logMsg(sprintf('Serialized variable (%s bytes) more than item_size_limit (%s bytes).', $var_len, $this->getParam('item_size_limit')), LOG_NOTICE, __FILE__, __LINE__);
172            return false;
173        }
174
175        if ($allow_oversized && $var_len >= $this->getParam('stack_size_limit')) {
176            $app->logMsg(sprintf('Serialized variable (%s bytes) more than stack_size_limit (%s bytes).', $var_len, $this->getParam('stack_size_limit')), LOG_NOTICE, __FILE__, __LINE__);
177            return false;
178        }
179
180        // Remove any value already stored under this key.
181        unset($_SESSION['_cache'][$this->_ns][$key]);
182
183        // Continue to prune the cache if its size is greater than stack_size_limit, but keep at least min_items.
184        while (mb_strlen(serialize($_SESSION['_cache'][$this->_ns])) + $var_len >= $this->getParam('stack_size_limit') && sizeof($_SESSION['_cache'][$this->_ns]) >= $this->getParam('min_items')) {
185            array_shift($_SESSION['_cache'][$this->_ns]);
186        }
187
188        // Save this value under the specified key.
189        $_SESSION['_cache'][$this->_ns][$key] =& $var;
190
191        $app->logMsg(sprintf('Set cache item “%s”', $key), LOG_DEBUG, __FILE__, __LINE__);
192
193        if ($var_len >= 1024000) {
194            $app->logMsg(sprintf('Successfully cached oversized variable (%s bytes).', $var_len), LOG_DEBUG, __FILE__, __LINE__);
195        }
196
197        return true;
198    }
199
200    /**
201     * Retrieves an object from the session cache and returns it unserialized.
202     * It also moves it to the top of the stack, which makes it such that the
203     * cache flushing mechanism of putCache deletes the oldest referenced items
204     * first.
205     *
206     * @param string $key  The key for the datum to retrieve.
207     * @return mixed          The requested datum, or false on failure.
208     */
209    public function get($key)
210    {
211        $app =& App::getInstance();
212
213        if (true !== $this->getParam('enabled') || getFormData('_disable_cache')) {
214            $app->logMsg(sprintf('Cache disabled, not getting data.', null), LOG_DEBUG, __FILE__, __LINE__);
215            return false;
216        }
217
218        if (isset($_SESSION['_cache'][$this->_ns]) && array_key_exists($key, $_SESSION['_cache'][$this->_ns])) {
219            $app->logMsg(sprintf('Retrieving %s from cache.', $key), LOG_DEBUG, __FILE__, __LINE__);
220            // Move the accessed cached datum to the top of the stack. Maybe somebody knows a better way to do this?
221            $tmp =& $_SESSION['_cache'][$this->_ns][$key];
222            unset($_SESSION['_cache'][$this->_ns][$key]);
223            $_SESSION['_cache'][$this->_ns][$key] =& $tmp;
224            // Return the unserialized datum.
225            return unserialize($_SESSION['_cache'][$this->_ns][$key]);
226        } else {
227            $app->logMsg(sprintf('Missing %s from cache.', $key), LOG_DEBUG, __FILE__, __LINE__);
228            return false;
229        }
230    }
231
232    /**
233     * Tells you if the object is cached.
234     *
235     * @param string $key  The key of the object to check.
236     * @return bool         True if a value exists for the given key.
237     */
238    public function exists($key)
239    {
240        $app =& App::getInstance();
241
242        if (true !== $this->getParam('enabled') || getFormData('_disable_cache')) {
243            $app->logMsg(sprintf('Cache disabled on exist assertion.', null), LOG_DEBUG, __FILE__, __LINE__);
244            return false;
245        }
246
247        return (isset($_SESSION['_cache'][$this->_ns]) && array_key_exists($key, $_SESSION['_cache'][$this->_ns]));
248    }
249
250    /**
251     * Removes a cached object.
252     *
253     * @param string $key  The key of the object to check.
254     * @return bool         True if the value existed before being unset.
255     */
256    public function delete($key)
257    {
258        $app =& App::getInstance();
259
260        if (true !== $this->getParam('enabled') || getFormData('_disable_cache')) {
261            $app->logMsg(sprintf('Cache disabled, skipping delete of %s', $key), LOG_DEBUG, __FILE__, __LINE__);
262            return false;
263        }
264
265        if (isset($_SESSION['_cache'][$this->_ns]) && array_key_exists($key, $_SESSION['_cache'][$this->_ns])) {
266            $app->logMsg(sprintf('Deleting cache item “%s”', $key), LOG_DEBUG, __FILE__, __LINE__);
267            unset($_SESSION['_cache'][$this->_ns][$key]);
268            return true;
269        } else {
270            return false;
271        }
272    }
273
274    /*
275    * Delete all existing items from the cache.
276    *
277    * @access   public
278    * @author   Quinn Comendant <quinn@strangecode.com>
279    * @version  1.0
280    * @since    05 Jun 2006 23:51:34
281    */
282    public function clear()
283    {
284        $app =& App::getInstance();
285
286        $_SESSION['_cache'][$this->_ns] = array();
287        $app->logMsg(sprintf('Cleared %s cache', $this->_ns), LOG_DEBUG, __FILE__, __LINE__);
288    }
289
290// END Cache
291}
292
Note: See TracBrowser for help on using the repository browser.