Simpletest Coverage - includes/cache.inc

1 <?php
2 // $Id: cache.inc,v 1.38 2009/07/01 12:47:30 dries Exp $
3
4 /**
5 * Get the cache object for a cache bin.
6 *
7 * By default, this returns an instance of the DrupalDatabaseCache class.
8 * Classes implementing DrupalCacheInterface can register themselves both as a
9 * default implementation and for specific bins.
10 *
11 * @see DrupalCacheInterface
12 *
13 * @param $bin
14 * The cache bin for which the cache object should be returned.
15 */
16 function _cache_get_object($bin) {
17 // We do not use drupal_static() here because we do not want to change the
18 // storage of a cache bin mid-request.
19 static $cache_objects, $default_class;
20 if (!isset($cache_objects[$bin])) {
21 $class = variable_get($bin, FALSE);
22 if (empty($class)) {
23 $class = variable_get('cache_default_class', 'DrupalDatabaseCache');
24 }
25 $cache_objects[$bin] = new $class($bin);
26 }
27 return $cache_objects[$bin];
28 }
29
30 /**
31 * Return data from the persistent cache. Data may be stored as either plain
32 * text or as serialized data. cache_get will automatically return
33 * unserialized objects and arrays.
34 *
35 * @param $cid
36 * The cache ID of the data to retrieve.
37 * @param $bin
38 * The cache bin to store the data in. Valid core values are 'cache_block',
39 * 'cache_field', 'cache_filter', 'cache_form', 'cache_menu', 'cache_page',
40 * 'cache_path', 'cache_registry', 'cache_update' or 'cache' for the default
41 * cache.
42 * @return The cache or FALSE on failure.
43 */
44 function cache_get($cid, $bin = 'cache') {
45 return _cache_get_object($bin)->get($cid);
46 }
47
48 /**
49 * Return data from the persistent cache when given an array of cache IDs.
50 *
51 * @param $cids
52 * An array of cache IDs for the data to retrieve. This is passed by
53 * reference, and will have the IDs successfully returned from cache removed.
54 * @param $bin
55 * The cache bin where the data is stored.
56 * @return
57 * An array of the items successfully returned from cache indexed by cid.
58 */
59 function cache_get_multiple(array &$cids, $bin = 'cache') {
60 return _cache_get_object($bin)->getMultiple($cids);
61 }
62
63 /**
64 * Store data in the persistent cache.
65 *
66 * The persistent cache is split up into several cache bins. In the default
67 * cache implementation, each cache bin corresponds to a database table by the
68 * same name. Other implementations might want to store several bins in data
69 * structures that get flushed together. While it is not a problem for most
70 * cache bins if the entries in them are flushed before their expire time, some
71 * might break functionality or are extremely expensive to recalculate. These
72 * will be marked with a (*). The other bins expired automatically by core.
73 * Contributed modules can add additional bins and get them expired
74 * automatically by implementing hook_flush_caches().
75 *
76 * - cache: Generic cache storage bin (used for variables, theme registry,
77 * locale date, list of simpletest tests etc).
78 *
79 * - cache_block: Stores the content of various blocks.
80 *
81 * - cache field: Stores the field data belonging to a given object.
82 *
83 * - cache_filter: Stores filtered pieces of content.
84 *
85 * - cache_form(*): Stores multistep forms. Flushing this bin means that some
86 * forms displayed to users lose their state and the data already submitted
87 * to them.
88 *
89 * - cache_menu: Stores the structure of visible navigation menus per page.
90 *
91 * - cache_page: Stores generated pages for anonymous users. It is flushed
92 * very often, whenever a page changes, at least for every ode and comment
93 * submission. This is the only bin affected by the page cache setting on
94 * the administrator panel.
95 *
96 * - cache path: Stores the system paths that have an alias.
97 *
98 * - cache update(*): Stores available releases. The update server (for
99 * example, drupal.org) needs to produce the relevant XML for every project
100 * installed on the current site. As this is different for (almost) every
101 * site, it's very expensive to recalculate for the update server.
102 *
103 * The reasons for having several bins are as follows:
104 *
105 * - smaller bins mean smaller database tables and allow for faster selects and
106 * inserts
107 * - we try to put fast changing cache items and rather static ones into different
108 * bins. The effect is that only the fast changing bins will need a lot of
109 * writes to disk. The more static bins will also be better cacheable with
110 * MySQL's query cache.
111 *
112 * @param $cid
113 * The cache ID of the data to store.
114 * @param $data
115 * The data to store in the cache. Complex data types will be automatically
116 * serialized before insertion.
117 * Strings will be stored as plain text and not serialized.
118 * @param $bin
119 * The cache bin to store the data in. Valid core values are 'cache_block',
120 * 'cache_field', 'cache_filter', 'cache_form', 'cache_menu', 'cache_page',
121 * 'cache_path', 'cache_registry', 'cache_update' or 'cache' for the default
122 * cache.
123 * @param $expire
124 * One of the following values:
125 * - CACHE_PERMANENT: Indicates that the item should never be removed unless
126 * explicitly told to using cache_clear_all() with a cache ID.
127 * - CACHE_TEMPORARY: Indicates that the item should be removed at the next
128 * general cache wipe.
129 * - A Unix timestamp: Indicates that the item should be kept at least until
130 * the given time, after which it behaves like CACHE_TEMPORARY.
131 * @param $headers
132 * A string containing HTTP header information for cached pages.
133 */
134 function cache_set($cid, $data, $bin = 'cache', $expire = CACHE_PERMANENT, array $headers = NULL) {
135 return _cache_get_object($bin)->set($cid, $data, $expire, $headers);
136 }
137
138 /**
139 * Expire data from the cache.
140 *
141 * If called without arguments, expirable entries will be cleared from the
142 * cache_page and cache_block bins.
143 *
144 * @param $cid
145 * If set, the cache ID to delete. Otherwise, all cache entries that can
146 * expire are deleted.
147 *
148 * @param $bin
149 * If set, the bin $bin to delete from. Mandatory
150 * argument if $cid is set.
151 *
152 * @param $wildcard
153 * If $wildcard is TRUE, cache IDs starting with $cid are deleted in
154 * addition to the exact cache ID specified by $cid. If $wildcard is
155 * TRUE and $cid is '*' then the entire table $table is emptied.
156 */
157 function cache_clear_all($cid = NULL, $bin = NULL, $wildcard = FALSE) {
158 if (!isset($cid) && !isset($bin)) {
159 // Clear the block cache first, so stale data will
160 // not end up in the page cache.
161 if (module_exists('block')) {
162 cache_clear_all(NULL, 'cache_block');
163 }
164 cache_clear_all(NULL, 'cache_page');
165 return;
166 }
167 return _cache_get_object($bin)->clear($cid, $wildcard);
168 }
169
170 /**
171 * Interface for cache implementations.
172 *
173 * All cache implementations have to implement this interface. DrupalDatabaseCache
174 * provides the default implementation, which can be consulted as an example.
175 *
176 * To make Drupal use your implementation for a certain cache bin, you have to
177 * set a variable with the name of the cache bin as its key and the name of your
178 * class as its value. For example, if your implementation of DrupalCacheInterface
179 * was called MyCustomCache, the following line would make Drupal use it for the
180 * 'cache_page' bin:
181 * @code
182 * variable_set('cache_page', 'MyCustomCache');
183 * @endcode
184 *
185 * Additionally, you can register your cache implementation to be used by default
186 * for all cache bins by setting the variable 'cache_default_class' to the name
187 * of your implementation of the DrupalCacheInterface, e.g.
188 * @code
189 * variable_set('cache_default_class', 'MyCustomCache');
190 * @endcode
191 *
192 * @see _cache_get_object()
193 * @see DrupalDatabaseCache
194 */
195 interface DrupalCacheInterface {
196 /**
197 * Constructor.
198 *
199 * @param $bin
200 * The cache bin for which the object is created.
201 */
202 function __construct($bin);
203
204 /**
205 * Return data from the persistent cache. Data may be stored as either plain
206 * text or as serialized data. cache_get will automatically return
207 * unserialized objects and arrays.
208 *
209 * @param $cid
210 * The cache ID of the data to retrieve.
211 * @return The cache or FALSE on failure.
212 */
213 function get($cid);
214
215 /**
216 * Return data from the persistent cache when given an array of cache IDs.
217 *
218 * @param $cids
219 * An array of cache IDs for the data to retrieve. This is passed by
220 * reference, and will have the IDs successfully returned from cache
221 * removed.
222 * @return
223 * An array of the items successfully returned from cache indexed by cid.
224 */
225 function getMultiple(&$cids);
226
227 /**
228 * Store data in the persistent cache.
229 *
230 * @param $cid
231 * The cache ID of the data to store.
232 * @param $data
233 * The data to store in the cache. Complex data types will be automatically
234 * serialized before insertion.
235 * Strings will be stored as plain text and not serialized.
236 * @param $expire
237 * One of the following values:
238 * - CACHE_PERMANENT: Indicates that the item should never be removed unless
239 * explicitly told to using cache_clear_all() with a cache ID.
240 * - CACHE_TEMPORARY: Indicates that the item should be removed at the next
241 * general cache wipe.
242 * - A Unix timestamp: Indicates that the item should be kept at least until
243 * the given time, after which it behaves like CACHE_TEMPORARY.
244 * @param $headers
245 * A string containing HTTP header information for cached pages.
246 */
247 function set($cid, $data, $expire = CACHE_PERMANENT, array $headers = NULL);
248
249
250 /**
251 * Expire data from the cache. If called without arguments, expirable
252 * entries will be cleared from the cache_page and cache_block bins.
253 *
254 * @param $cid
255 * If set, the cache ID to delete. Otherwise, all cache entries that can
256 * expire are deleted.
257 * @param $wildcard
258 * If set to TRUE, the $cid is treated as a substring
259 * to match rather than a complete ID. The match is a right hand
260 * match. If '*' is given as $cid, the bin $bin will be emptied.
261 */
262 function clear($cid = NULL, $wildcard = FALSE);
263 }
264
265 /**
266 * Default cache implementation.
267 *
268 * This is Drupal's default cache implementation. It uses the database to store
269 * cached data. Each cache bin corresponds to a database table by the same name.
270 */
271 class DrupalDatabaseCache implements DrupalCacheInterface {
272 protected $bin;
273
274 function __construct($bin) {
275 $this->bin = $bin;
276 }
277
278 function get($cid) {
279 // Garbage collection necessary when enforcing a minimum cache lifetime.
280 $this->garbageCollection($this->bin);
281 $cache = db_query("SELECT data, created, headers, expire, serialized FROM {" . $this->bin . "} WHERE cid = :cid", array(':cid' => $cid))->fetchObject();
282
283 return $this->prepareItem($cache);
284 }
285
286 function getMultiple(&$cids) {
287 // Garbage collection necessary when enforcing a minimum cache lifetime.
288 $this->garbageCollection($this->bin);
289 $query = db_select($this->bin);
290 $query->fields($this->bin, array('cid', 'data', 'created', 'headers', 'expire', 'serialized'));
291 $query->condition($this->bin . '.cid', $cids, 'IN');
292 $result = $query->execute();
293 $cache = array();
294 foreach ($result as $item) {
295 $item = $this->prepareItem($item);
296 if ($item) {
297 $cache[$item->cid] = $item;
298 }
299 }
300 $cids = array_diff($cids, array_keys($cache));
301 return $cache;
302 }
303
304 /**
305 * Garbage collection for get() and getMultiple().
306 *
307 * @param $bin
308 * The bin being requested.
309 */
310 protected function garbageCollection() {
311 global $user;
312
313 // Garbage collection necessary when enforcing a minimum cache lifetime.
314 $cache_flush = variable_get('cache_flush_' . $this->bin, 0);
315 if ($cache_flush && ($cache_flush + variable_get('cache_lifetime', 0) <= REQUEST_TIME)) {
316 // Reset the variable immediately to prevent a meltdown in heavy load situations.
317 variable_set('cache_flush_' . $this->bin, 0);
318 // Time to flush old cache data
319 db_delete($this->bin)
320 ->condition('expire', CACHE_PERMANENT, '<>')
321 ->condition('expire', $cache_flush, '<=')
322 ->execute();
323 }
324 }
325
326 /**
327 * Prepare a cached item.
328 *
329 * Checks that items are either permanent or did not expire, and unserializes
330 * data as appropriate.
331 *
332 * @param $cache
333 * An item loaded from cache_get() or cache_get_multiple().
334 * @return
335 * The item with data unserialized as appropriate or FALSE if there is no
336 * valid item to load.
337 */
338 protected function prepareItem($cache) {
339 global $user;
340
341 if (!isset($cache->data)) {
342 return FALSE;
343 }
344 // If the data is permanent or we are not enforcing a minimum cache lifetime
345 // always return the cached data.
346 if ($cache->expire == CACHE_PERMANENT || !variable_get('cache_lifetime', 0)) {
347 if ($cache->serialized) {
348 $cache->data = unserialize($cache->data);
349 }
350 }
351 // If enforcing a minimum cache lifetime, validate that the data is
352 // currently valid for this user before we return it by making sure the cache
353 // entry was created before the timestamp in the current session's cache
354 // timer. The cache variable is loaded into the $user object by _drupal_session_read()
355 // in session.inc. If the data is permanent or we're not enforcing a minimum
356 // cache lifetime always return the cached data.
357 if ($cache->expire != CACHE_PERMANENT && variable_get('cache_lifetime', 0) && $user->cache > $cache->created) {
358 // This cache data is too old and thus not valid for us, ignore it.
359 return FALSE;
360 }
361
362 if (isset($cache->headers)) {
363 $cache->headers = unserialize($cache->headers);
364 }
365
366 return $cache;
367 }
368
369 function set($cid, $data, $expire = CACHE_PERMANENT, array $headers = NULL) {
370 $fields = array(
371 'serialized' => 0,
372 'created' => REQUEST_TIME,
373 'expire' => $expire,
374 'headers' => isset($headers) ? serialize($headers) : NULL,
375 );
376 if (!is_string($data)) {
377 $fields['data'] = serialize($data);
378 $fields['serialized'] = 1;
379 }
380 else {
381 $fields['data'] = $data;
382 $fields['serialized'] = 0;
383 }
384
385 db_merge($this->bin)
386 ->key(array('cid' => $cid))
387 ->fields($fields)
388 ->execute();
389 }
390
391 function clear($cid = NULL, $wildcard = FALSE) {
392 global $user;
393
394 if (empty($cid)) {
395 if (variable_get('cache_lifetime', 0)) {
396 // We store the time in the current user's $user->cache variable which
397 // will be saved into the sessions bin by _drupal_session_write(). We then
398 // simulate that the cache was flushed for this user by not returning
399 // cached data that was cached before the timestamp.
400 $user->cache = REQUEST_TIME;
401
402 $cache_flush = variable_get('cache_flush_' . $this->bin, 0);
403 if ($cache_flush == 0) {
404 // This is the first request to clear the cache, start a timer.
405 variable_set('cache_flush_' . $this->bin, REQUEST_TIME);
406 }
407 elseif (REQUEST_TIME > ($cache_flush + variable_get('cache_lifetime', 0))) {
408 // Clear the cache for everyone, cache_lifetime seconds have
409 // passed since the first request to clear the cache.
410 db_delete($this->bin)
411 ->condition('expire', CACHE_PERMANENT, '<>')
412 ->condition('expire', REQUEST_TIME, '<')
413 ->execute();
414 variable_set('cache_flush_' . $this->bin, 0);
415 }
416 }
417 else {
418 // No minimum cache lifetime, flush all temporary cache entries now.
419 db_delete($this->bin)
420 ->condition('expire', CACHE_PERMANENT, '<>')
421 ->condition('expire', REQUEST_TIME, '<')
422 ->execute();
423 }
424 }
425 else {
426 if ($wildcard) {
427 if ($cid == '*') {
428 db_truncate($this->bin)->execute();
429 }
430 else {
431 db_delete($this->bin)
432 ->condition('cid', $cid . '%', 'LIKE')
433 ->execute();
434 }
435 }
436 elseif (is_array($cid)) {
437 // Delete in chunks when a large array is passed.
438 do {
439 db_delete($this->bin)
440 ->condition('cid', array_splice($cid, 0, 1000), 'IN')
441 ->execute();
442 }
443 while (count($cid));
444 }
445 else {
446 db_delete($this->bin)
447 ->condition('cid', $cid)
448 ->execute();
449 }
450 }
451 }
452 }
453

Legend

Missed
lines code that were not excersized during program execution.
Covered
lines code were excersized during program execution.
Comment/non executable
Comment or non-executable line of code.
Dead
lines of code that according to xdebug could not be executed. This is counted as coverage code because in almost all cases it is code that runnable.