/[drupal]/contributions/modules/cache_browser/cache_browser.pages.inc
ViewVC logotype

Contents of /contributions/modules/cache_browser/cache_browser.pages.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.1 - (show annotations) (download) (as text)
Mon Sep 15 02:55:44 2008 UTC (14 months, 1 week ago) by markuspetrux
Branch: MAIN
CVS Tags: DRUPAL-6--1-0, HEAD
Branch point for: DRUPAL-6--1
File MIME type: text/x-php
- Initial commit for the cache_browser project.
1 <?php
2 // $Id$
3
4 /**
5 * @file
6 * Implements the cache browser page and related actions.
7 */
8
9 /**
10 * Menu callback; Cache browser page.
11 *
12 * @param string $cache_table
13 * @param string $action
14 */
15 function cache_browser_page($cache_table = NULL, $action = NULL) {
16 drupal_add_css(drupal_get_path('module', 'cache_browser') .'/cache_browser.css', 'module', 'all', FALSE);
17
18 $report_rows = array();
19 $cache_size = 0;
20
21 if (!isset($cache_table)) {
22 // List cache tables.
23 $report_header = array(
24 array('data' => t('Table'), 'class' => 'cache-browser-th'),
25 array('data' => t('Description'), 'class' => 'cache-browser-th'),
26 array('data' => t('Entries'), 'class' => 'cache-browser-th'),
27 array('data' => t('Size (KB)'), 'class' => 'cache-browser-th'),
28 array('data' => t('Last updated'), 'class' => 'cache-browser-th'),
29 array('data' => t('Actions'), 'class' => 'cache-browser-th'),
30 );
31 $cache_tables = cache_browser_get_tables();
32 foreach ($cache_tables as $table => $table_info) {
33 $cache_size += $table_info->size;
34 $report_rows[] = array(
35 array(
36 'data' => l($table, CACHE_BROWSER_PAGE_PATH .'/'. $table .'/browse'),
37 'class' => 'cache-browser-td-text',
38 ),
39 array(
40 'data' => check_plain($table_info->description),
41 'class' => 'cache-browser-td-text',
42 ),
43 array(
44 'data' => cache_browser_format_number($table_info->entries),
45 'class' => 'cache-browser-td-numeric',
46 ),
47 array(
48 'data' => cache_browser_format_number($table_info->size / 1024, 2),
49 'title' => cache_browser_format_number($table_info->size) .' '. t('bytes'),
50 'class' => 'cache-browser-td-numeric',
51 ),
52 array(
53 'data' => (!empty($table_info->max_created) ? format_date($table_info->max_created, 'custom', 'Y-m-d H:i:s') : t('N/A')),
54 'class' => 'cache-browser-td-date',
55 ),
56 array(
57 'data' => ($table_info->entries > 0 ? l(t('Reset'), CACHE_BROWSER_PAGE_PATH .'/'. $table .'/reset', array('query' => drupal_get_destination())) : t('N/A')),
58 'class' => 'cache-browser-td-action',
59 ),
60 );
61 }
62 }
63 else {
64 // Validate user supplied cache table name.
65 $cache_tables = cache_browser_get_tables();
66 if (!isset($cache_tables[$cache_table])) {
67 drupal_not_found();
68 return;
69 }
70
71 // Validate user supplied action and transfer control to proper handler.
72 if (!in_array($action, array('browse', 'reset', 'view', 'delete'))) {
73 drupal_not_found();
74 return;
75 }
76 if ($action == 'reset') {
77 return drupal_get_form('cache_browser_page_action_reset', $cache_table);
78 }
79 if ($action == 'view' || $action == 'delete') {
80 if (!isset($_GET['cid'])) {
81 drupal_not_found();
82 return;
83 }
84 if ($action == 'view') {
85 return cache_browser_page_action_view($cache_table, $_GET['cid']);
86 }
87 return drupal_get_form('cache_browser_page_action_delete', $cache_table, $_GET['cid']);
88 }
89
90 // Browse cache table contents.
91 $report_header = array(
92 array('data' => t('Entry ID'), 'class' => 'cache-browser-th'),
93 array('data' => t('Size (KB)'), 'class' => 'cache-browser-th'),
94 array('data' => t('Created'), 'class' => 'cache-browser-th'),
95 array('data' => t('Expires'), 'class' => 'cache-browser-th'),
96 array('data' => t('Actions'), 'class' => 'cache-browser-th'),
97 );
98 $result = pager_query(
99 'SELECT cid, created, expire, LENGTH(data) AS size FROM {'. $cache_table .'} ORDER BY cid',
100 CACHE_BROWSER_ITEMS_PER_PAGE, 0,
101 'SELECT COUNT(*) FROM {'. $cache_table .'}'
102 );
103 while ($entry = db_fetch_object($result)) {
104 $cache_size += $entry->size;
105 $report_rows[] = array(
106 array(
107 'data' => l(cache_browser_trim_cid($cache_table, $entry->cid), CACHE_BROWSER_PAGE_PATH .'/'. $cache_table .'/view', array('query' => 'cid='. $entry->cid)),
108 'title' => $entry->cid,
109 'class' => 'cache-browser-td-text',
110 ),
111 array(
112 'data' => cache_browser_format_number($entry->size / 1024, 2),
113 'title' => cache_browser_format_number($entry->size) .' '. t('bytes'),
114 'class' => 'cache-browser-td-numeric',
115 ),
116 array(
117 'data' => format_date($entry->created, 'custom', 'Y-m-d H:i:s'),
118 'class' => 'cache-browser-td-date',
119 ),
120 array(
121 'data' => cache_browser_format_expire_date($entry->expire),
122 'class' => 'cache-browser-td-date',
123 ),
124 array(
125 'data' => l(t('Delete'), CACHE_BROWSER_PAGE_PATH .'/'. $cache_table .'/delete', array('query' => 'cid='. $entry->cid .'&'. drupal_get_destination())),
126 'class' => 'cache-browser-td-action',
127 ),
128 );
129 }
130 }
131
132 $report_rows_count = count($report_rows);
133 if ($report_rows_count <= 0) {
134 $report_rows[] = array(array(
135 'data' => (!isset($cache_table) ? t('No cache tables found.') : t('No cache entries found.')),
136 'align' => 'center',
137 'colspan' => count($report_header),
138 ));
139 }
140
141 $page_header_info = array(
142 'size' => $cache_size,
143 'items' => $report_rows_count,
144 );
145 if (isset($cache_table)) {
146 $page_header_info['table'] = $cache_table;
147 }
148
149 $output = theme('cache_browser_page_header', $page_header_info);
150 $output .= theme('table', $report_header, $report_rows);
151 $output .= theme('pager', NULL, CACHE_BROWSER_ITEMS_PER_PAGE);
152 return $output;
153 }
154
155 /**
156 * View contents of the given cache entry.
157 *
158 * @param string $cache_table
159 * @param string $cid
160 */
161 function cache_browser_page_action_view($cache_table, $cid) {
162 $cache = cache_browser_cache_get($cid, $cache_table);
163 if (!isset($cache->data)) {
164 drupal_not_found();
165 return;
166 }
167
168 $page_header_info = array(
169 'table' => $cache_table,
170 'cid' => $cid,
171 );
172 $output = theme('cache_browser_page_header', $page_header_info);
173
174 // Uncompress data?
175 if ($cache_table == 'cache_page' && !empty($cache->data) && variable_get('page_compression', TRUE) && function_exists('gzencode')) {
176 // Assume data is compressed when first bytes are Gzip ID1 + ID2.
177 // See http://www.gzip.org/zlib/rfc-gzip.html
178 if ($cache->data[0] == "\x1f" && $cache->data[1] == "\x8b") {
179 $cache->data = gzinflate(substr(substr($cache->data, 10), 0, -8));
180 }
181 }
182
183 // Display cache entry information.
184 $output .= cache_browser_dump_cache_entry($cache);
185
186 // Dump cached headers.
187 if (!empty($cache->headers)) {
188 $output .= cache_browser_dump_headers($cache->headers);
189 }
190
191 // Dump cached data.
192 $output .= cache_browser_dump_data($cache->data, $cid);
193
194 return $output;
195 }
196
197 /**
198 * Reset cache table confirmation form.
199 *
200 * @param string $cache_table
201 */
202 function cache_browser_page_action_reset(&$form_state, $cache_table) {
203 // Validate user supplied cache table name.
204 $cache_tables = cache_browser_get_tables();
205 if (!isset($cache_tables[$cache_table])) {
206 drupal_not_found();
207 return;
208 }
209 $form = array();
210 $form['cache_table'] = array(
211 '#type' => 'value',
212 '#value' => $cache_table,
213 );
214 return confirm_form(
215 $form,
216 t('Are you sure you want to reset table <strong>!cache_table</strong>?', array('!cache_table' => $cache_table)),
217 $_GET['destination'] ? $_GET['destination'] : CACHE_BROWSER_PAGE_PATH,
218 t('This action cannot be undone.'), t('Confirm'), t('Cancel')
219 );
220 }
221
222 /**
223 * Process the reset cache table confirmation form.
224 */
225 function cache_browser_page_action_reset_submit($form, &$form_state) {
226 cache_clear_all('*', $form_state['values']['cache_table'], TRUE);
227 }
228
229 /**
230 * Delete cache entry confirmation form.
231 *
232 * @param string $cache_table
233 * @param string $cid
234 */
235 function cache_browser_page_action_delete(&$form_state, $cache_table, $cid) {
236 // Validate user supplied cache table name.
237 $cache_tables = cache_browser_get_tables();
238 if (!isset($cache_tables[$cache_table])) {
239 drupal_not_found();
240 return;
241 }
242 $form = array();
243 $form['cache_table'] = array(
244 '#type' => 'value',
245 '#value' => $cache_table,
246 );
247 $form['cid'] = array(
248 '#type' => 'value',
249 '#value' => $cid,
250 );
251 return confirm_form(
252 $form,
253 t('Are you sure you want to delete the entry <strong>!entry</strong> from table <strong>!cache_table</strong>?', array('!entry' => $cid, '!cache_table' => $cache_table)),
254 $_GET['destination'] ? $_GET['destination'] : CACHE_BROWSER_PAGE_PATH .'/'. $cache_table .'/browse',
255 t('This action cannot be undone.'), t('Delete'), t('Cancel')
256 );
257 }
258
259 /**
260 * Process the delete cache entry confirmation form.
261 */
262 function cache_browser_page_action_delete_submit($form, &$form_state) {
263 cache_clear_all($form_state['values']['cid'], $form_state['values']['cache_table']);
264 }
265
266 /**
267 * Obtain list of cache tables.
268 *
269 * @return array
270 * Hash of cache tables where key is cache table name and elements are
271 * objects with the following attributes:
272 *
273 * description string Table description obtained from database schema.
274 * max_created int Timestamp of the most recently created cache entry.
275 * size int Sum of the size in bytes of all cached data.
276 * entries int Number of cache entries in the table.
277 *
278 * The hash is ordered by cache table name.
279 */
280 function cache_browser_get_tables() {
281 global $db_type;
282 static $cache_tables;
283 if (!isset($cache_tables)) {
284 // Obtain cache tables from system schema.
285 $schema_tables = array();
286 if ($schema = drupal_get_schema()) {
287 foreach ($schema as $table => $table_info) {
288 if (strpos($table, 'cache') === 0) {
289 $schema_tables[$table] = $table_info['description'];
290 }
291 }
292 }
293 ksort($schema_tables);
294
295 // Load table statistics from database.
296 $cache_tables = array();
297 foreach ($schema_tables as $table => $description) {
298 $cache_tables[$table] = db_fetch_object(db_query('SELECT MAX(created) AS max_created, SUM(LENGTH(data)) AS size, COUNT(*) AS entries FROM {'. $table .'}'));
299 $cache_tables[$table]->description = $description;
300 }
301 }
302 return $cache_tables;
303 }
304
305 /**
306 * Shorten cid when it is too long.
307 *
308 * @param string $cache_table
309 * @param string $cid
310 * @param int $max_length
311 */
312 function cache_browser_trim_cid($cache_table, $cid, $max_length = NULL) {
313 global $base_url;
314 if (!isset($max_length)) {
315 $max_length = CACHE_BROWSER_MAX_CID_LENGTH;
316 }
317 if (drupal_strlen($cid) > $max_length) {
318 if ($cache_table == 'cache_page' && strpos($cid, $base_url) === 0) {
319 $cid = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ? 'https' : 'http') .'://...'. drupal_substr($cid, drupal_strlen($base_url));
320 if (drupal_strlen($cid) > $max_length) {
321 $cid = drupal_substr($cid, 0, $max_length-3) .'...';
322 }
323 }
324 else {
325 $cid = drupal_substr($cid, 0, $max_length-3) .'...';
326 }
327 }
328 return $cid;
329 }
330
331 /**
332 * Format a cache expiration date.
333 *
334 * @param int $expire
335 * @return string
336 */
337 function cache_browser_format_expire_date($expire) {
338 if ($expire == CACHE_PERMANENT) {
339 return t('Permanent');
340 }
341 if ($expire == CACHE_TEMPORARY) {
342 return t('Temporary');
343 }
344 $output = format_date($expire, 'custom', 'Y-m-d H:i:s');
345 if ($expire <= time()) {
346 $output = '<span class="cache-browser-expired">'. $output .'</span>';
347 }
348 return $output;
349 }
350
351 /**
352 * Format a floating point number with grouped thousands.
353 *
354 * @param float $number
355 * @param int $decimals
356 * @return string
357 */
358 function cache_browser_format_number($number, $decimals = 0) {
359 return number_format($number, $decimals, CACHE_BROWSER_DECIMAL_POINT, CACHE_BROWSER_THOUSANDS_SEP);
360 }
361
362 /**
363 * Obtain data from the persistent cache.
364 *
365 * This function is a safe wrapper around Drupal's cache_get().
366 * We wish to obtain data even if it is expired, and we don't wish to alter
367 * normal cache expiration schema removing stale data as cache_get() does.
368 */
369 function cache_browser_cache_get($cid, $table) {
370 $cache = db_fetch_object(db_query("SELECT data, created, headers, expire, serialized FROM {". $table ."} WHERE cid = '%s'", $cid));
371 if (isset($cache->data)) {
372 $cache->data = db_decode_blob($cache->data);
373 if ($cache->serialized) {
374 $cache->data = unserialize($cache->data);
375 }
376 return $cache;
377 }
378 return 0;
379 }
380
381 /**
382 * Dump cache headers.
383 */
384 function cache_browser_dump_cache_entry($cache) {
385 $info = array();
386 $info[] = '<strong>'. t('Created') .'</strong>: '. format_date($cache->created, 'custom', 'Y-m-d H:i:s');
387 $info[] = '<strong>'. t('Expires') .'</strong>: '. cache_browser_format_expire_date($cache->expire);
388 $info[] = '<strong>'. t('Size') .'</strong>: '. cache_browser_format_number(strlen($cache->serialized ? serialize($cache->data) : $cache->data)) .' '. t('bytes');
389 return '<h3 class="cache-browser-dump-title">'. t('Cache entry') .':</h3><pre class="cache-browser-dump">'. implode("\n", $info) .'</pre>';
390 }
391
392 /**
393 * Dump cache headers.
394 */
395 function cache_browser_dump_headers($headers) {
396 $headers = '<span class="cache-browser-dump-headers">'. check_plain($headers) .'</span>';
397 return '<h3 class="cache-browser-dump-title">'. t('Cached headers') .':</h3><pre class="cache-browser-dump">'. $headers .'</pre>';
398 }
399
400 /**
401 * Dump cache data contents.
402 */
403 function cache_browser_dump_data($data, $name = NULL) {
404 $data = (isset($name) ? $name .' => ' : '') . var_export($data, TRUE);
405
406 // Remove redundant white spaces.
407 $data = preg_replace('#=>\s+(class [a-zA-Z0-9_]*)\s{#', '=> \1 {', $data);
408 $data = preg_replace('#=>\s+array\s\(#', '=> array (', $data);
409 $data = preg_replace('#array \(\s+\)#', 'array ()', $data);
410
411 $data = htmlspecialchars($data);
412
413 // Make sure line breaks are well formed. ie. only \n works when PCRE m modifier is used.
414 $data = str_replace(array("\r\n", "\n\r"), array("\n", "\n"), $data);
415
416 // Replace escaped single quotes with \xFF\xFF.
417 $data = str_replace("\\'", "\xFF\xFF", $data);
418
419 // Replace string hashes with placeholders highlighting saved strings.
420 preg_match_all('#^(\s*)(\'.*?\')\s+=#m', $data, $saved_hashes);
421 $saved_hashes = array_values(array_unique($saved_hashes[2]));
422 foreach ($saved_hashes as $i => $string) {
423 $saved_hashes[$i] = '<span class="cache-browser-dump-hash">'. str_replace("\xFF\xFF", "\\'", $string) .'</span>';
424 $data = str_replace($string, '[_hash_'. $i .'_hash_]', $data);
425 }
426
427 // Replace string literals with placeholders highlighting saved strings.
428 preg_match_all('#\'.*?\'#s', $data, $saved_strings);
429 $saved_strings = array_values(array_unique($saved_strings[0]));
430 foreach ($saved_strings as $i => $string) {
431 $saved_strings[$i] = '<span class="cache-browser-dump-string">'. str_replace("\xFF\xFF", "\\'", $string) .'</span>';
432 $data = str_replace($string, '[_value_'. $i .'_value_]', $data);
433 }
434
435 // Highlight class attributes.
436 $data = preg_replace('#^(\s*var\s+)(\$[a-zA-Z0-9_]*)\s+=#m', '\1<span class="cache-browser-dump-variable-name">\2</span> =', $data);
437
438 // Highlight numeric array indexes.
439 $data = preg_replace('#^(\s*)([0-9]*)\s+=#m', '\1<span class="cache-browser-dump-hash">\2</span> =', $data);
440
441 // Highlight numeric values.
442 $data = preg_replace('# (=|=&gt;) ([e+0-9.]*)([,;])$#m', ' \1 <span class="cache-browser-dump-numeric">\2</span>\3', $data);
443
444 // Highlight constants (NULL, true, false).
445 $data = preg_replace('# (=|=&gt;) (null|true|false)([,;])$#im', ' \1 <span class="cache-browser-dump-constant">\2</span>\3', $data);
446
447 $module_path = drupal_get_path('module', 'cache_browser');
448 $icon_minus = url($module_path .'/images/icon_minus.gif');
449 $icon_plus = url($module_path .'/images/icon_plus.gif');
450
451 preg_match_all('#^\s+.*?[({]$#m', $data, $matches);
452 $pos = -1;
453 foreach ($matches[0] as $i => $match) {
454 $pos = strpos($data, $match, $pos + 1) + drupal_strlen(preg_replace('#^(\s*).*$#', '\1', $match));
455 $len = drupal_strlen(preg_replace('#^\s*(.*)$#', '\1', $match));
456 $data = substr($data, 0, $pos) . substr($data, $pos, $len) .'<img src="'. $icon_plus .'" align="absmiddle" class="cache-browser-dump-hotspot" id="dump-hotspot-'. $i .'" alt="" /><span class="cache-browser-dump-block" id="dump-block-'. $i .'">'. substr($data, $pos + $len);
457 }
458 unset($matches);
459 $data = preg_replace('#(\s+)([)}],)$#m', '\1</span>\2', $data);
460
461 if ($pos > 0) {
462 $header_icons = '<div class="cache-browser-dump-title-right">'
463 .'<a id="dump-hotspot-expand" href="javascript:void(0)"><img src="'. $icon_plus .'" align="absmiddle" alt="" /> '. t('Expand all') .'</a>'
464 .'&nbsp;&nbsp;'
465 .'<a id="dump-hotspot-collapse" href="javascript:void(0)"><img src="'. $icon_minus .'" align="absmiddle" alt="" /> '. t('Collapse all') .'</a>'
466 .'</div>';
467 drupal_add_js(array('cache_browser' => array(
468 'icon_plus' => $icon_plus,
469 'icon_minus' => $icon_minus,
470 )), 'setting');
471 // Add our custom JS script, do not defer, can be cached, do not preprocess.
472 drupal_add_js($module_path .'/cache_browser.js', 'module', 'footer', FALSE, TRUE, FALSE);
473 }
474 else {
475 $header_icons = '';
476 }
477
478 // Restore saved string hashes and values.
479 foreach ($saved_hashes as $i => $string) {
480 $data = str_replace('[_hash_'. $i .'_hash_]', $string, $data);
481 }
482 foreach ($saved_strings as $i => $string) {
483 $data = str_replace('[_value_'. $i .'_value_]', $string, $data);
484 }
485 unset($saved_hashes, $saved_strings);
486
487 return '<div class="clear-block"><h3 class="cache-browser-dump-title-left">'. t('Data dump') .':</h3>'. $header_icons .'</div><pre class="cache-browser-dump">'. $data .'</pre>';
488 }
489
490 /**
491 * Render cache browser header.
492 *
493 * @ingroup themeable
494 */
495 function theme_cache_browser_page_header($page_header_info) {
496 $header_left_pane = '<a href="'. url(CACHE_BROWSER_PAGE_PATH) .'">'. t('Cache') .'</a>';
497 if (isset($page_header_info['table'])) {
498 $header_left_pane .= ' -&gt; <a href="'. url(CACHE_BROWSER_PAGE_PATH .'/'. $page_header_info['table'] .'/browse') .'">'. check_plain($page_header_info['table']) .'</a>';
499 if (isset($page_header_info['cid'])) {
500 $header_left_pane .= ' -&gt; <a href="'. url(CACHE_BROWSER_PAGE_PATH .'/'. $page_header_info['table'] .'/view', array('query' => 'cid='. $page_header_info['cid'], 'attributes' => array('title' => $page_header_info['cid']))) .'">'. check_plain(cache_browser_trim_cid($page_header_info['table'], $page_header_info['cid'])) .'</a>';
501 }
502 }
503
504 $extra = array();
505 if (isset($page_header_info['items'])) {
506 $extra[] = (!isset($page_header_info['table']) ? t('Tables') : t('Items')) .': '. cache_browser_format_number($page_header_info['items']);
507 }
508 if (isset($page_header_info['size'])) {
509 $cache_size = $page_header_info['size'];
510 $cache_size_units = t('bytes');
511 if ($cache_size > 1024) {
512 $cache_size = $cache_size / 1024;
513 $cache_size_units = 'KB';
514 }
515 if ($cache_size > 1024) {
516 $cache_size = $cache_size / 1024;
517 $cache_size_units = 'MB';
518 }
519 $extra[] = '<span title="'. cache_browser_format_number($page_header_info['size']) .' '. t('bytes') .'">'. t('Size') .': '. cache_browser_format_number($cache_size, 2) .' '. $cache_size_units .'</span>';
520 }
521 if (!empty($extra)) {
522 $header_left_pane .= ' ('. implode(', ', $extra) .')';
523 }
524
525 $header_right_pane = t('Current time') .': '. format_date(time(), 'custom', 'Y-m-d H:i:s');
526
527 $output = '<div class="clear-block">';
528 $output .= '<div class="cache-browser-header-left-pane">'. $header_left_pane .'</div>';
529 $output .= '<div class="cache-browser-header-right-pane">'. $header_right_pane .'</div>';
530 $output .= '</div>';
531 return $output;
532 }

  ViewVC Help
Powered by ViewVC 1.1.2