/[drupal]/contributions/modules/devel/devel.module
ViewVC logotype

Contents of /contributions/modules/devel/devel.module

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


Revision 1.364 - (show annotations) (download) (as text)
Wed Oct 21 14:56:12 2009 UTC (5 weeks, 4 days ago) by salvis
Branch: MAIN
Changes since 1.363: +2 -2 lines
File MIME type: text/x-php
#498028: Fix kprint_r() not displaying $name.
1 <?php
2 // $Id: devel.module,v 1.363 2009/10/14 14:44:26 weitzman Exp $
3
4 // This module holds functions useful for Drupal development.
5 // Please contribute!
6
7 // Suggested profiling and stacktrace library from http://www.xdebug.org/index.php
8
9 define('DEVEL_QUERY_SORT_BY_SOURCE', 0);
10 define('DEVEL_QUERY_SORT_BY_DURATION', 1);
11
12 define('DEVEL_ERROR_HANDLER_NONE', 0);
13 define('DEVEL_ERROR_HANDLER_STANDARD', 1);
14 define('DEVEL_ERROR_HANDLER_BACKTRACE', 2);
15
16 define('DEVEL_MIN_TEXTAREA', 50);
17
18 /**
19 * Implementation of hook_help().
20 */
21 function devel_help($section) {
22 switch ($section) {
23 case 'devel/reference':
24 return '<p>'. t('This is a list of defined user functions that generated this current request lifecycle. Click on a function name to view its documention.') .'</p>';
25 case 'devel/session':
26 return '<p>'. t('Here are the contents of your <code>$_SESSION</code> variable.') .'</p>';
27 case 'devel/variable':
28 $api = variable_get('devel_api_url', 'api.drupal.org');
29 return '<p>'. t('This is a list of the variables and their values currently stored in variables table and the <code>$conf</code> array of your settings.php file. These variables are usually accessed with <a href="@variable-get-doc">variable_get()</a> and <a href="@variable-set-doc">variable_set()</a>. Variables that are too long can slow down your pages.', array('@variable-get-doc' => "http://$api/api/HEAD/function/variable_get", '@variable-set-doc' => "http://$api/api/HEAD/function/variable_set")) .'</p>';
30 }
31 }
32
33 /**
34 * Implementationation of hook_menu().
35 */
36 function devel_menu() {
37 $items = array();
38 // Note: we can't dynamically append destination to querystring. Do so at theme layer. Fix in D7?
39 $items['devel/cache/clear'] = array(
40 'title' => 'Empty cache',
41 'page callback' => 'devel_cache_clear',
42 'description' => 'Clear the CSS cache and all database cache tables which store page, node, theme and variable caches.',
43 'access arguments' => array('access devel information'),
44 'menu_name' => 'devel',
45 );
46
47 $items['devel/reference'] = array(
48 'title' => 'Function reference',
49 'description' => 'View a list of currently defined user functions with documentation links.',
50 'page callback' => 'devel_function_reference',
51 'access arguments' => array('access devel information'),
52 'menu_name' => 'devel',
53 );
54 $items['devel/reinstall'] = array(
55 'title' => 'Reinstall modules',
56 'page callback' => 'drupal_get_form',
57 'page arguments' => array('devel_reinstall'),
58 'description' => 'Run hook_uninstall() and then hook_install() for a given module.',
59 'access arguments' => array('access devel information'),
60 'menu_name' => 'devel',
61 );
62 $items['devel/devel_themer'] = array(
63 'title callback' => 'devel_menu_title_theme_developer',
64 'title arguments' => array(NULL),
65 'title' => 'foo',
66 'description' => 'Quickly enable or disable theme developer module. Useful for removing HTML cruft added by that module.',
67 'page callback' => 'devel_devel_themer_toggle',
68 'access arguments' => array('access devel information'),
69 'menu_name' => 'devel',
70 );
71 $items['devel/source'] = array(
72 'title' => 'Display the PHP code of any file in your Drupal installation',
73 'page callback' => 'devel_display_source',
74 'access arguments' => array('display source code'),
75 'type' => MENU_CALLBACK,
76 'menu_name' => 'devel',
77 );
78 $items['devel/menu/reset'] = array(
79 'title' => 'Rebuild menus',
80 'description' => 'Rebuild menu based on hook_menu() and revert any custom changes. All menu items return to their default settings.',
81 'page callback' => 'drupal_get_form',
82 'page arguments' => array('devel_menu_rebuild'),
83 'access arguments' => array('access devel information'),
84 'menu_name' => 'devel',
85 );
86 $items['devel/variable'] = array(
87 'title' => 'Variable editor',
88 'description' => 'Edit and delete site variables.',
89 'page callback' => 'devel_variable_page',
90 'access arguments' => array('access devel information'),
91 'menu_name' => 'devel',
92 );
93 // we don't want the abbreviated version provided by status report
94 $items['devel/phpinfo'] = array(
95 'title' => 'PHPinfo()',
96 'description' => 'View your server\'s PHP configuration',
97 'page callback' => 'devel_phpinfo',
98 'access arguments' => array('access devel information'),
99 'menu_name' => 'devel',
100 );
101 $items['devel/php'] = array(
102 'title' => 'Execute PHP Code',
103 'description' => 'Execute some PHP code',
104 'page callback' => 'drupal_get_form',
105 'page arguments' => array('devel_execute_form'),
106 'access arguments' => array('execute php code'),
107 'menu_name' => 'devel',
108 );
109 $items['devel/theme/registry'] = array(
110 'title' => 'Theme registry',
111 'description' => 'View a list of available theme functions across the whole site.',
112 'page callback' => 'devel_theme_registry',
113 'access arguments' => array('access devel information'),
114 'menu_name' => 'devel',
115 );
116 $items['devel/field/info'] = array(
117 'title' => 'Field info',
118 'description' => 'View fields information across the whole site.',
119 'page callback' => 'devel_field_info_page',
120 'access arguments' => array('access devel information'),
121 'menu_name' => 'devel',
122 );
123 $items['devel/elements'] = array(
124 'title' => 'Hook_elements()',
125 'description' => 'View the active form/render elements for this site.',
126 'page callback' => 'devel_elements_page',
127 'access arguments' => array('access devel information'),
128 'menu_name' => 'devel',
129 );
130 $items['devel/variable/edit/%'] = array(
131 'title' => 'Variable editor',
132 'page callback' => 'drupal_get_form',
133 'page arguments' => array('devel_variable_edit', 3),
134 'access arguments' => array('access devel information'),
135 'type' => MENU_CALLBACK,
136 'menu_name' => 'devel',
137 );
138 $items['devel/session'] = array(
139 'title' => 'Session viewer',
140 'description' => 'List the contents of $_SESSION.',
141 'page callback' => 'devel_session',
142 'access arguments' => array('access devel information'),
143 'menu_name' => 'devel',
144 );
145 $items['devel/switch'] = array(
146 'title' => 'Switch user',
147 'page callback' => 'devel_switch_user',
148 'access arguments' => array('switch users'),
149 'type' => MENU_CALLBACK,
150 'menu_name' => 'devel',
151 );
152 $items['devel/run-cron'] = array(
153 'title' => 'Run cron',
154 'page callback' => 'system_run_cron',
155 'access arguments' => array('administer site configuration'),
156 'file' => 'system.admin.inc',
157 'file path' => drupal_get_path('module', 'system'),
158 'menu_name' => 'devel',
159 );
160 $items['admin/config/development/devel'] = array(
161 'title' => 'Devel settings',
162 'description' => 'Helper functions, pages, and blocks to assist Drupal developers. The devel blocks can be managed via the <a href="' . url('admin/structure/block') . '">block administration</a> page.',
163 'page callback' => 'drupal_get_form',
164 'page arguments' => array('devel_admin_settings'),
165 'access arguments' => array('administer site configuration'),
166 'menu_name' => 'devel',
167 );
168 $items['node/%node/devel/load'] = array(
169 'title' => 'Dev load',
170 'page callback' => 'devel_load_object',
171 'page arguments' => array(1, 'node'),
172 'access callback' => 'user_access',
173 'access arguments' => array('access devel information'),
174 'type' => MENU_LOCAL_TASK,
175 'weight' => 100,
176 );
177 $items['node/%node/devel/render'] = array(
178 'title' => 'Dev render',
179 'page callback' => 'devel_render_object',
180 'page arguments' => array('node', 1),
181 'access callback' => 'user_access',
182 'access arguments' => array('access devel information'),
183 'type' => MENU_LOCAL_TASK,
184 'weight' => 100,
185 );
186 $items['comment/%comment/devel/load'] = array(
187 'title' => 'Dev load',
188 'page callback' => 'devel_load_object',
189 'page arguments' => array(1, 'comment'),
190 'access callback' => 'user_access',
191 'access arguments' => array('access devel information'),
192 'type' => MENU_LOCAL_TASK,
193 'weight' => 100,
194 );
195 $items['comment/%comment/devel/render'] = array(
196 'title' => 'Dev render',
197 'page callback' => 'devel_render_object',
198 'page arguments' => array('comment', 1),
199 'access callback' => 'user_access',
200 'access arguments' => array('access devel information'),
201 'type' => MENU_LOCAL_TASK,
202 'weight' => 100,
203 );
204 $items['user/%user/devel/load'] = array(
205 'title' => 'Dev load',
206 'page callback' => 'devel_load_object',
207 'page arguments' => array(1, 'user'),
208 'access callback' => 'user_access',
209 'access arguments' => array('access devel information'),
210 'type' => MENU_LOCAL_TASK,
211 'weight' => 100,
212 );
213 $items['user/%user/devel/render'] = array(
214 'title' => 'Dev render',
215 'page callback' => 'devel_render_object',
216 'page arguments' => array('user', 1),
217 'access callback' => 'user_access',
218 'access arguments' => array('access devel information'),
219 'type' => MENU_LOCAL_TASK,
220 'weight' => 100,
221 );
222 $items['taxonomy/term/%taxonomy_term/devel/load'] = array(
223 'title' => 'Dev load',
224 'page callback' => 'devel_load_object',
225 'page arguments' => array(2, 'term'),
226 'access callback' => 'user_access',
227 'access arguments' => array('access devel information'),
228 'type' => MENU_LOCAL_TASK,
229 'weight' => 100,
230 );
231 $items['taxonomy/term/%taxonomy_term/devel/render'] = array(
232 'title' => 'Dev render',
233 'page callback' => 'devel_render_term',
234 'page arguments' => array(2),
235 'access callback' => 'user_access',
236 'access arguments' => array('access devel information'),
237 'type' => MENU_LOCAL_TASK,
238 'weight' => 100,
239 );
240
241 return $items;
242 }
243
244 function devel_menu_need_destination() {
245 return array('devel/cache/clear', 'devel/devel_themer', 'devel/reinstall', 'devel/menu/reset', 'admin/og/og', 'devel/variable', 'admin/reports/status/run-cron');
246 }
247
248 /**
249 * An implementation of hook_menu_link_alter(). Flag this link as needing alter at display time.
250 * This is more robust that setting alter in hook_menu(). See devel_translated_menu_link_alter().
251 *
252 **/
253 function devel_menu_link_alter(&$item) {
254 if (in_array($item['link_path'], devel_menu_need_destination())) {
255 $item['options']['alter'] = TRUE;
256 }
257 }
258
259 /**
260 * An implementation of hook_translated_menu_item_alter(). Append dynamic
261 * querystring 'destination' to several of our own menu items.
262 *
263 **/
264 function devel_translated_menu_link_alter(&$item) {
265 if (in_array($item['href'], devel_menu_need_destination())) {
266 $item['localized_options']['query'] = drupal_get_destination();
267 }
268 }
269
270 function devel_menu_title_theme_developer() {
271 if (module_exists('devel_themer')) {
272 return t('Disable Theme developer');
273 }
274 else {
275 return t('Enable Theme developer');
276 }
277 }
278
279 function devel_devel_themer_toggle() {
280 if (module_exists('devel_themer')) {
281 module_disable(array('devel_themer'));
282 }
283 else {
284 // Sanity check in case the devel_themer schema is not installed.
285 include_once('./includes/install.inc');
286 if (drupal_get_schema_versions('devel_themer') == FALSE) {
287 drupal_install_modules(array('devel_themer'));
288 }
289 else {
290 module_enable(array('devel_themer'));
291 }
292 }
293 drupal_theme_rebuild();
294 menu_rebuild();
295 drupal_goto();
296 }
297
298 /**
299 * Implementation of hook_theme()
300 */
301 function devel_theme() { // &$cache, $type, $theme, $path
302 return array(
303 'devel_querylog' => array(
304 'arguments' => array('header' => array(), 'rows' => array()),
305 ),
306 'devel_querylog_row' => array(
307 'arguments' => array('row' => array()),
308 ),
309 );
310 }
311
312 /**
313 * Page callback to display syntax hilighted source code
314 *
315 * note: the path for this function is received via $_GET['path']
316 * example http://www.example.com/devel/source?file=modules/node/node.module
317 *
318 * @param $standalone
319 * Set to FALSE to place the code inside a Drupal page. Otherwise code displays on its own.
320 */
321 function devel_display_source($standalone = TRUE) {
322 $path = $_GET['file'];
323 // take out the nasties
324 $path = str_replace('../', '', $path);
325 $output = devel_highlight_file($path, $standalone);
326 if ($output) {
327 if ($standalone) {
328 print $output;
329 exit();
330 }
331 return $output;
332 }
333 else {
334 drupal_set_message(t('Invalid file path'), 'error');
335 drupal_not_found();
336 }
337 }
338
339 /**
340 * Return PHP highlighted file
341 *
342 * @param $path
343 * path to the file
344 * *warning* there is NO VALIDATION in this function
345 * Beware of paths such as '../../../../../etc/apache/httpd.conf'
346 *
347 * @param $standalone
348 * should the returned HTML be wrapped in a full <html> page or will it be output by Drupal?
349 */
350 function devel_highlight_file($path = NULL, $standalone = FALSE) {
351 if (file_exists($path)) {
352 $source = highlight_file($path, TRUE);
353 // add anchor links before all functions
354 // with doxygen
355 // $source = preg_replace('!(\/\*\*.*?\*\/.*?)<br.*?function.*?#0000BB">(.*?)<\/span>!', '<a id="$2"></a> $0', $source);
356 //$source = preg_replace('!(\/\*\*.*?\*\/).*?function.*?#0000BB">(.*?)<\/span>!', '<a id="$2"></a> $0', $source);
357 if ($standalone) {
358 $source = <<<EOT
359 <head><title>$path</title></head>
360 <body>$source</body>
361 EOT;
362 }
363 return $source;
364 }
365 else {
366 return FALSE;
367 }
368 }
369
370 /**
371 * Implementation of hook_init().
372 */
373 function devel_init() {
374 if (!devel_silent()) {
375 if (user_access('access devel information')) {
376 devel_set_handler(variable_get('devel_error_handler', DEVEL_ERROR_HANDLER_STANDARD));
377 // We want to include the class early so that anyone may call krumo() as needed. See http://krumo.sourceforge.net/
378 has_krumo();
379
380 // See http://www.firephp.org/.
381 // Support Libraries API - http://drupal.org/project/libraries
382 if (module_exists('libraries')) {
383 $path = libraries_get_path('FirePHPCore') . '/lib/FirePHPCore/fb.php';
384 }
385 else {
386 $path = './'. drupal_get_path('module', 'devel') .'/FirePHPCore/lib/FirePHPCore/fb.php';
387 }
388 if (file_exists($path)) {
389 include_once $path;
390 }
391 // Add CSS for query log if should be displayed.
392 if (variable_get('devel_query_display', 0)) {
393 drupal_add_css(drupal_get_path('module', 'devel') .'/devel.css');
394 }
395 }
396 }
397 if (variable_get('devel_rebuild_theme_registry', FALSE)) {
398 drupal_theme_rebuild();
399 if (flood_is_allowed('devel_rebuild_registry_warning', 1)) {
400 flood_register_event('devel_rebuild_registry_warning');
401 if (!devel_silent() && user_access('access devel information')) {
402 drupal_set_message(t('The theme registry is being rebuilt on every request. Remember to <a href="!url">turn off</a> this feature on production websites.', array("!url" => url('admin/config/development/devel'))));
403 }
404 }
405 }
406 }
407
408 // return boolean. no need for cache here.
409 function has_krumo() {
410 // see README.txt or just download from http://krumo.sourceforge.net/
411 @include_once './'. drupal_get_path('module', 'devel') .'/krumo/class.krumo.php';
412 if (function_exists('krumo') && php_sapi_name() != 'cli') {
413 return TRUE;
414 }
415 else {
416 return FALSE;
417 }
418 }
419
420 /**
421 * Decide whether or not to print a debug variable using krumo().
422 *
423 * @param $input
424 * @return boolean
425 */
426 function merits_krumo($input) {
427 return (is_object($input) || is_array($input)) && has_krumo() && variable_get('devel_krumo_skin', '') != 'disabled';
428 }
429
430 /**
431 * Calls the http://www.firephp.org/ fb() function if it is found.
432 *
433 * @return void
434 */
435 function dfb() {
436 if (function_exists('fb') && user_access('access devel information')) {
437 $args = func_get_args();
438 call_user_func_array('fb', $args);
439 }
440 }
441
442
443 function devel_set_handler($handler) {
444 switch ($handler) {
445 case DEVEL_ERROR_HANDLER_STANDARD:
446 // do nothing
447 break;
448 case DEVEL_ERROR_HANDLER_BACKTRACE:
449 if (has_krumo()) {
450 set_error_handler('backtrace_error_handler');
451 }
452 break;
453 case DEVEL_ERROR_HANDLER_NONE:
454 restore_error_handler();
455 break;
456 }
457 }
458
459 function devel_silent() {
460 // isset($_GET['q']) is needed on IIS when calling the front page. q is not set.
461 // Don't interfere with private files/images.
462 return
463 devel_verify_cli() ||
464 isset($GLOBALS['devel_shutdown']) ||
465 strstr($_SERVER['PHP_SELF'], 'update.php') ||
466 (isset($_GET['q']) && (
467 in_array($_GET['q'], array('upload/js', 'admin/content/node-settings/rebuild')) ||
468 substr($_GET['q'], 0, strlen('system/files')) == 'system/files' ||
469 substr($_GET['q'], 0, strlen('batch')) == 'batch')
470 );
471 }
472
473 /**
474 * Implementation of hook_boot(). Runs even for cached pages.
475 */
476 function devel_boot() {
477 if (!devel_silent()) {
478 devel_start();
479 }
480 }
481
482 // Kickoff our tricks. Put here all code which must run for cached pages too. Called from both devel_boot() and devel_init().
483 function devel_start() {
484 if (variable_get('dev_mem', 0)) {
485 global $memory_init;
486 $memory_init = memory_get_usage();
487 }
488
489 if (devel_query_enabled()) {
490 //TODO: How best to include this?
491 @include_once DRUPAL_ROOT . '/includes/database/log.inc';
492 Database::startLog('devel');;
493 }
494
495 if (devel_code_coverage_enabled()) {
496 xdebug_start_code_coverage();
497 }
498
499 // we need user_access() in the shutdown function. make sure it gets loaded
500 drupal_load('module', 'user');
501 register_shutdown_function('devel_shutdown');
502 }
503
504 function backtrace_error_handler($errno, $message, $filename, $line) {
505 // Don't respond to the error if it was suppressed with a '@'
506 if (error_reporting() == 0) return;
507
508 if ($errno & (E_ALL ^ E_NOTICE)) {
509 // We can't use the PHP E_* constants here as not all versions of PHP have all
510 // the constants defined, so for consistency, we just use the numeric equivelant.
511 $types = array(
512 1 => 'error',
513 2 => 'warning',
514 4 => 'parse error',
515 8 => 'notice',
516 16 => 'core error',
517 32 => 'core warning',
518 64 => 'compile error',
519 128 => 'compile warning',
520 256 => 'user error',
521 512 => 'user warning',
522 1024 => 'user notice',
523 2048 => 'strict warning',
524 4096 => 'recoverable error',
525 8192 => 'deprecated',
526 16384 => 'user deprecated',
527 );
528 $entry = $types[$errno] .': '. $message .' in '. $filename .' on line '. $line .'.';
529
530 if (variable_get('error_level', 1) == 1) {
531 $backtrace = debug_backtrace();
532 foreach ($backtrace as $call) {
533 $nicetrace[$call['function']] = $call;
534 }
535 krumo($nicetrace);
536 }
537
538 watchdog('php', '%message in %file on line %line.', array('%error' => $types[$errno], '%message' => $message, '%file' => $filename, '%line' => $line), WATCHDOG_ERROR);
539 }
540 }
541
542 /**
543 * Implement hook_permission().
544 */
545 function devel_permission() {
546 return array(
547 'access devel information' => array(
548 'description' => t('View developer output like variable printouts, query log, etc.'),
549 'title' => t('Access developer information'),
550 ),
551 'execute php code' => array(
552 'title' => t('Execute PHP code'),
553 'description' => t('Run arbitrary PHP from a block. Danger!'),
554 ),
555 'switch users' => array(
556 'title' => t('Switch users'),
557 'description' => t('Become any user on the site with just a click. Danger!'),
558 ),
559 'display source code' => array(
560 'title' => t('Display source code'),
561 'description' => t('View the site\'s php source code. Danger!'),
562 ),
563 );
564 }
565
566 function devel_block_info() {
567 $blocks['execute_php'] = array(
568 'info' => t('Execute PHP'),
569 'cache' => DRUPAL_NO_CACHE,
570 );
571 $blocks['switch_user'] = array(
572 'info' => t('Switch user'),
573 'cache' => DRUPAL_NO_CACHE,
574 );
575 return $blocks;
576 }
577
578 /**
579 * Implementation of hook_block_configure().
580 */
581 function devel_block_configure($delta) {
582 if ($delta == 'switch_user') {
583 $form['devel_switch_user_list_size'] = array(
584 '#type' => 'textfield',
585 '#title' => t('Number of users to display in the list'),
586 '#default_value' => variable_get('devel_switch_user_list_size', 10),
587 '#size' => '3',
588 '#maxlength' => '4',
589 );
590 return $form;
591 }
592 }
593
594 function devel_block_save($delta, $edit = array()) {
595 if ($delta == 'switch_user') {
596 variable_set('devel_switch_user_list_size', $edit['devel_switch_user_list_size']);
597 }
598 }
599
600 function devel_block_view($delta) {
601 $block = array();
602 switch ($delta) {
603 case 'switch_user':
604 $block = devel_block_switch_user();
605 break;
606
607 case 'execute_php':
608 if (user_access('execute php code')) {
609 $block['subject'] = t('Execute PHP');
610 $block['content'] = drupal_get_form('devel_execute_form');
611 }
612 break;
613 }
614 return $block;
615 }
616
617 function devel_block_switch_user() {
618 $links = devel_switch_user_list();
619 if (!empty($links)) {
620 $block['subject'] = t('Switch user');
621 $build['devel_links'] = array('#theme' => 'links', '#links' => $links);
622 $build['devel_form'] = drupal_get_form('devel_switch_user_form');
623 $block['content'] = $build;
624 return $block;
625 }
626 }
627
628 function devel_switch_user_list() {
629 $links = array();
630 if (user_access('switch users')) {
631 $list_size = variable_get('devel_switch_user_list_size', 10);
632 $dest = drupal_get_destination();
633 // Try to find at least $list_size users that can switch.
634 $roles = user_roles(1, 'switch users');
635 if (isset($roles[2])) {
636 // If authenticated users have this permission, just grab
637 // the last $list_size users, since there won't be records in
638 // {user_roles} and every user on the system can switch.
639 $users = db_query_range("SELECT DISTINCT u.uid, u.name, u.access FROM {users} u WHERE u.uid > 0 ORDER BY u.access DESC", 0, $list_size);
640 }
641 else {
642 $where = array('u.uid = 1');
643 if (count($roles)) {
644 $where[] = 'r.rid IN ('. implode(',', array_keys($roles)) .')';
645 }
646 $where_sql = implode(' OR ', $where);
647 $users = db_query_range("SELECT DISTINCT u.uid, u.name, u.access FROM {users} u LEFT JOIN {users_roles} r ON u.uid = r.uid WHERE $where_sql ORDER BY u.access DESC", 0, $list_size);
648 }
649 foreach ($users as $user) {
650 $links[$user->uid] = array(
651 'title' => theme('placeholder', $user->name),
652 'href' => 'devel/switch/'. $user->name,
653 'query' => $dest,
654 'attributes' => array('title' => t('This user can switch back.')),
655 'html' => TRUE,
656 );
657 }
658 $num_links = count($links);
659 if ($num_links < $list_size) {
660 // If we don't have enough, add distinct uids until we hit $list_size.
661 $users = db_query_range('SELECT uid, name, access FROM {users} WHERE uid > 0 AND uid NOT IN ('. implode(',', array_keys($links)) .') ORDER BY access DESC', 0, $list_size - $num_links);
662 foreach ($users as $user) {
663 if (count($links) >= $list_size) {
664 break;
665 }
666 $links[$user->uid] = array(
667 'title' => $user->name ? $user->name : 'anon',
668 'href' => 'devel/switch/'. $user->name,
669 'query' => $dest,
670 'attributes' => array('title' => t('Caution: this user will be unable switch back.')),
671 );
672 }
673 }
674 }
675 return $links;
676 }
677
678 function devel_phpinfo() {
679 print phpinfo();
680 exit;
681 }
682
683 function devel_switch_user_form() {
684 $form['username'] = array(
685 '#type' => 'textfield',
686 '#description' => t('Enter username'),
687 '#autocomplete_path' => 'user/autocomplete',
688 '#maxlength' => USERNAME_MAX_LENGTH,
689 '#size' => 16,
690 );
691 $form['submit'] = array(
692 '#type' => 'submit',
693 '#value' => t('Switch'),
694 );
695 return $form;
696
697 }
698
699 function devel_doc_function_form() {
700 $version = devel_get_core_version(VERSION);
701 $form['function'] = array(
702 '#type' => 'textfield',
703 '#description' => t('Enter function name for api lookup'),
704 '#size' => 16,
705 '#maxlength' => 255,
706 );
707 $form['version'] = array('#type' => 'value', '#value' => $version);
708 $form['submit_button'] = array(
709 '#type' => 'submit',
710 '#value' => t('Submit'),
711 );
712 return $form;
713 }
714
715 function devel_doc_function_form_submit($form, &$form_state) {
716 $version = $form_state['values']['version'];
717 $function = $form_state['values']['function'];
718 $api = variable_get('devel_api_url', 'api.drupal.org');
719 $form_state['redirect'] = "http://$api/api/$version/function/$function";
720 }
721
722 function devel_switch_user_form_validate($form, &$form_state) {
723 if (!$account = user_load_by_name($form_state['values']['username'])) {
724 form_set_error('username', t('Username not found'));
725 }
726 }
727
728 function devel_switch_user_form_submit($form, &$form_state) {
729 $form_state['redirect'] = 'devel/switch/'. $form_state['values']['username'];
730 }
731
732
733 /**
734 * TODO: I switched params as per http://drupal.org/node/144132#form-alter but needs work still
735 * Implementation of hook_form_alter().
736 */
737 function devel_form_alter(&$form, $form_state, $form_id, $key_in = NULL) {
738 if (user_access('access devel information') && variable_get('devel_form_weights', 0)) {
739 $children = element_children($form);
740 if (empty($children)) {
741 if (isset($form['#type']) && !in_array($form['#type'], array('value', 'hidden'))) {
742 if (!isset($form['#title'])) {
743 $form['#title'] = '';
744 }
745 $form['#title'] .= " (key=$key_in, weight=". (isset($form['#weight']) ? $form['#weight'] : 0) .')';
746 }
747 }
748 else {
749 foreach (element_children($form) as $key) {
750 // We need to add the weight to fieldsets.
751 if (element_children($form[$key])) { // Which are a container of others.
752 if (!isset($form[$key]['#title'])) {
753 $form[$key]['#title'] = '';
754 }
755 $form[$key]['#title'] .= " (key=$key, weight=". (isset($form[$key]['#weight']) ? $form[$key]['#weight'] : 0) .')';
756 }
757 devel_form_alter($form[$key], $form_state, $form_id, $key);
758 }
759 }
760 }
761 }
762
763 // An implementation of hook_exit().
764 function devel_exit($destination = NULL) {
765 global $user;
766
767 if (isset($destination) && !devel_silent()) {
768 // The page we are leaving is a drupal_goto(). Present a redirection page
769 // so that the developer can see the intermediate query log.
770 // We don't want to load user module here, so keep function_exists() call.
771 if (isset($user) && function_exists('user_access') && user_access('access devel information') && variable_get('devel_redirect_page', 0)) {
772 $output = t_safe('<p>The user is being redirected to <a href="@destination">@destination</a>.</p>', array('@destination' => $destination));
773 drupal_set_page_content($output);
774 $page = element_info('page');
775 print drupal_render_page($page);
776
777 // Don't allow the automatic redirect to happen.
778 drupal_page_footer();
779 exit();
780 }
781 else {
782 $GLOBALS['devel_redirecting'] = TRUE;
783 }
784 }
785 }
786
787 /**
788 * See devel_start() which registers this function as a shutdown function.
789 */
790 function devel_shutdown() {
791 // Register the real shutdown function so it runs later than other shutdown functions.
792 register_shutdown_function('devel_shutdown_real');
793 }
794
795 // Borrowed from drush.
796 function devel_verify_cli() {
797 if (php_sapi_name() == 'cgi') {
798 return (is_numeric($_SERVER['argc']) && $_SERVER['argc'] > 0);
799 }
800
801 return (php_sapi_name() == 'cli');
802 }
803
804 function devel_page_alter($page) {
805 if (variable_get('devel_page_alter', FALSE) && user_access('access devel information')) {
806 dpm($page, 'page');
807 }
808 }
809
810 /**
811 * See devel_shutdown() which registers this function as a shutdown function. Displays developer information in the footer.
812 */
813 function devel_shutdown_real() {
814 global $memory_init, $user;
815 $output = $txt = '';
816
817 // Set $GLOBALS['devel_shutdown'] = FALSE in order to supress the
818 // devel footer for a page. Not necessary if your page outputs any
819 // of the Content-type http headers tested below (e.g. text/xml,
820 // text/javascript, etc). This is is advised where applicable.
821 if (!isset($GLOBALS['devel_shutdown']) && !isset($GLOBALS['devel_redirecting'])) {
822 // Try not to break non html pages.
823 if (function_exists('drupal_get_header')) {
824 if ($headers = drupal_get_header()) {
825 $formats = array('xml', 'javascript', 'json', 'plain', 'image', 'application', 'csv', 'x-comma-separated-values');
826 foreach ($formats as $format) {
827 if (strstr($headers['content-type'], $format)) {
828 return;
829 }
830 }
831 }
832 }
833
834 if (isset($user) && user_access('access devel information')) {
835 if (devel_query_enabled()) {
836 $output .= devel_shutdown_query();
837 }
838 if (variable_get('dev_mem', FALSE)) {
839 $memory_shutdown = memory_get_usage();
840 $args = array('@memory_boot' => round($memory_init / 1024 / 1024, 2), '@memory_shutdown' => round($memory_shutdown / 1024 / 1024, 2), '@memory_peak' => round(memory_get_peak_usage(TRUE) / 1024 / 1024, 2));
841 $msg = '<div class="dev-memory-usage"><h3>Memory usage:</h3> Memory used at: devel_boot()=<strong>@memory_boot</strong> MB, devel_shutdown()=<strong>@memory_shutdown</strong> MB, PHP peak usage=<strong>@memory_peak</strong> MB.</div>';
842 // theme() may not be available. not t() either.
843 $output .= t_safe($msg, $args);
844 }
845
846 // Code coverage reporting.
847 if (devel_code_coverage_enabled()) {
848 $mode = variable_get('devel_code_coverage', FALSE);
849 $coverage = xdebug_get_code_coverage();
850 if ($mode == 1) {
851 $output .= dpr(array_keys($coverage), TRUE);
852 }
853 else {
854 $output .= dpr($coverage, TRUE);
855 }
856 }
857 }
858
859 if ($output) {
860 // TODO: gzip this text if we are sending a gzip page. see drupal_page_header().
861 // For some reason, this is not actually printing for cached pages even though it gets executed
862 // and $output looks good.
863 print $output;
864 }
865 }
866 }
867
868 function devel_shutdown_query() {
869 $output = '';
870 $queries = Database::getLog('devel', 'default');
871 list($counts, $query_summary) = devel_query_summary($queries);
872 // Query log off, timer on.
873 if (!variable_get('devel_query_display', 0) && variable_get('dev_timer', 0)) {
874 $output .= '<div class="dev-timer">'. devel_timer() .' '. $query_summary. '</div>';
875 }
876
877 // Query log on.
878 $sum = 0;
879 if (variable_get('devel_query_display', FALSE)) {
880 $output .= '<div class="dev-query">';
881 $output .= $query_summary;
882 // calling theme() during shutdown is very bad if registry gets rebuilt like when making a change on admin/build/modules
883 // so we check for presence of theme registry before calling theme()
884 if (function_exists('theme_get_registry') && theme_get_registry()) {
885 $txt = t_safe(' Queries taking longer than @threshold ms and queries executed more than once, are <span class="marker">highlighted</span>.', array('@threshold' => variable_get('devel_execution', 5)));
886 if (variable_get('dev_timer', 0)) {
887 $txt .= devel_timer();
888 }
889 $output .= $txt;
890 $output .= '</div>';
891 $output .= devel_query_table($queries, $counts);
892 }
893 else {
894 $output .= $txt . '</div>' . dprint_r($queries, TRUE);
895 }
896 }
897
898 return $output;
899 }
900
901 function devel_query_enabled() {
902 return method_exists('Database', 'getLog') && variable_get('dev_query', FALSE);
903 }
904
905 function devel_code_coverage_enabled() {
906 return function_exists('xdebug_get_code_coverage') && variable_get('devel_code_coverage', FALSE);
907 }
908
909 function devel_query_summary($queries) {
910 if (variable_get('dev_query', FALSE) && is_array($queries)) {
911 $sum = 0;
912 foreach ($queries as $query) {
913 $text[] = $query['query'];
914 $sum += $query['time'];
915 }
916 $counts = array_count_values($text);
917 return array($counts, t_safe('Executed @queries queries in @time milliseconds.', array('@queries' => count($queries), '@time' => round($sum * 1000, 2))));
918 }
919 }
920
921 function t_safe($string, $args) {
922 // get_t caused problems here with theme registry after changing on admin/build/modules. the theme_get_registry call is needed.
923 if (function_exists('t') && function_exists('theme_get_registry')) {
924 theme_get_registry();
925 return t($string, $args);
926 }
927 else {
928 strtr($string, $args);
929 }
930 }
931
932 /**
933 * Returns a list of all currently defined user functions in the current
934 * request lifecycle, with links their documentation.
935 */
936 function devel_function_reference() {
937 $functions = get_defined_functions();
938 $version = devel_get_core_version(VERSION);
939 $ufunctions = $functions['user'];
940 sort($ufunctions);
941 $api = variable_get('devel_api_url', 'api.drupal.org');
942 foreach ($ufunctions as $function) {
943 $links[] = l($function, "http://$api/api/$version/function/$function");
944 }
945 return theme('item_list', $links);
946 }
947
948 function devel_get_core_version($version) {
949 $version_parts = explode('.', $version);
950 // Map from 4.7.10 -> 4.7
951 if ($version_parts[0] < 5) {
952 return $version_parts[0] .'.'. $version_parts[1];
953 }
954 // Map from 5.5 -> 5 or 6.0-beta2 -> 6
955 else {
956 return $version_parts[0];
957 }
958 }
959
960 // See http://drupal.org/node/126098
961 function devel_is_compatible_optimizer() {
962 ob_start();
963 phpinfo();
964 $info = ob_get_contents();
965 ob_end_clean();
966
967 // Match the Zend Optimizer version in the phpinfo information
968 $found = preg_match('/Zend&nbsp;Optimizer&nbsp;v([0-9])\.([0-9])\.([0-9])/', $info, $matches);
969
970 if ($matches) {
971 $major = $matches[1];
972 $minor = $matches[2];
973 $build = $matches[3];
974
975 if ($major >= 3) {
976 if ($minor >= 3) {
977 return TRUE;
978 }
979 elseif ($minor == 2 && $build >= 8) {
980 return TRUE;
981 }
982 else {
983 return FALSE;
984 }
985 }
986 else {
987 return FALSE;
988 }
989 }
990 else {
991 return TRUE;
992 }
993 }
994
995 function devel_admin_settings() {
996 $form['queries'] = array('#type' => 'fieldset', '#title' => t('Query log'));
997
998 $description = t("Collect query info. If disabled, no query log functionality will work.");
999 if (!devel_is_compatible_optimizer()) {
1000 $description = t('You must disable or upgrade the php Zend Optimizer extension in order to enable this feature. The minimum required version is 3.2.8. Earlier versions of Zend Optimizer are <a href="!url">horribly buggy and segfault your Apache</a> ... ', array('!url' => url('http://drupal.org/node/126098'))) . $description;
1001 }
1002 $form['queries']['dev_query'] = array('#type' => 'checkbox',
1003 '#title' => t('Collect query info'),
1004 '#default_value' => variable_get('dev_query', 0),
1005 '#disabled' => !devel_is_compatible_optimizer() ? TRUE : FALSE,
1006 '#description' => $description,
1007 );
1008
1009 $form['queries']['devel_query_display'] = array('#type' => 'checkbox',
1010 '#title' => t('Display query log'),
1011 '#default_value' => variable_get('devel_query_display', 0),
1012 '#description' => t('Display a log of the database queries needed to generate the current page, and the execution time for each. Also, queries which are repeated during a single page view are summed in the # column, and printed in red since they are candidates for caching.'));
1013 $form['queries']['devel_query_sort'] = array('#type' => 'radios',
1014 '#title' => t('Sort query log'),
1015 '#default_value' => variable_get('devel_query_sort', DEVEL_QUERY_SORT_BY_SOURCE),
1016 '#options' => array(t('by source'), t('by duration')),
1017 '#description' => t('The query table can be sorted in the order that the queries were executed or by descending duration.'),
1018 );
1019 $form['queries']['devel_execution'] = array('#type' => 'textfield',
1020 '#title' => t('Slow query highlighting'),
1021 '#default_value' => variable_get('devel_execution', 5),
1022 '#size' => 4,
1023 '#maxlength' => 4,
1024 '#description' => t('Enter an integer in milliseconds. Any query which takes longer than this many milliseconds will be highlighted in the query log. This indicates a possibly inefficient query, or a candidate for caching.'),
1025 );
1026 $form['devel_api_url'] = array('#type' => 'textfield',
1027 '#title' => t('API Site'),
1028 '#default_value' => variable_get('devel_api_url', 'api.drupal.org'),
1029 '#description' => t('The base URL for your developer documentation links. You might change this if you run <a href="!url">api.module</a> locally.', array('!url' => url('http://drupal.org/project/api'))));
1030 $form['dev_timer'] = array('#type' => 'checkbox',
1031 '#title' => t('Display page timer'),
1032 '#default_value' => variable_get('dev_timer', 0),
1033 '#description' => t('Display page execution time in the query log box.'),
1034 );
1035 $form['dev_mem'] = array('#type' => 'checkbox',
1036 '#title' => t('Display memory usage'),
1037 '#default_value' => variable_get('dev_mem', 0),
1038 '#description' => t('Display how much memory is used to generate the current page. This will show memory usage when devel_init() is called and when devel_exit() is called.'),
1039 );
1040 $form['devel_code_coverage'] = array(
1041 '#type' => 'radios',
1042 '#title' => t('Code Coverage'),
1043 '#disabled' => !function_exists('xdebug_start_code_coverage'),
1044 '#default_value' => variable_get('devel_code_coverage', 0),
1045 '#options' => array(t('Disabled'), t('Files only'), 'Files and lines'),
1046 '#description' => t('Show the files (and optionally the lines in those files) which were loaded by PHP to fulfill this request. In general, fewer files is better since parsing files costs significant performance. Requires <a href="http://xdebug.org">xdebug</a>.'),
1047 );
1048 $form['devel_redirect_page'] = array('#type' => 'checkbox',
1049 '#title' => t('Display redirection page'),
1050 '#default_value' => variable_get('devel_redirect_page', 0),
1051 '#description' => t('When a module executes drupal_goto(), the query log and other developer information is lost. Enabling this setting presents an intermediate page to developers so that the log can be examined before continuing to the destination page.'),
1052 );
1053 $form['devel_form_weights'] = array('#type' => 'checkbox',
1054 '#title' => t('Display form element keys and weights'),
1055 '#default_value' => variable_get('devel_form_weights', 0),
1056 '#description' => t('Form element names are needed for performing themeing or altering a form. Their weights determine the position of the element. Enabling this setting will show these keys and weights beside each form item.'),
1057 );
1058 $form['devel_page_alter'] = array('#type' => 'checkbox',
1059 '#title' => t('Display $page array'),
1060 '#default_value' => variable_get('devel_page_alter', FALSE),
1061 '#description' => t('Display $page array from <a href="http://api.drupal.org/api/function/hook_page_alter/7">hook_page_alter()</a> in the messages area of each page.'),
1062 );
1063 $form['devel_error_handler'] = array('#type' => 'radios',
1064 '#title' => t('Error handler'),
1065 '#default_value' => variable_get('devel_error_handler', DEVEL_ERROR_HANDLER_STANDARD),
1066 '#options' => array(DEVEL_ERROR_HANDLER_NONE => t('None'), DEVEL_ERROR_HANDLER_STANDARD => t('Standard drupal')),
1067 '#description' => t('Choose an error handler for your site. <em>Backtrace</em> prints nice debug information when an error is noticed, and you <a href="@choose">choose to show errors on screen</a>. <strong>Backtrace requires the <a href="@krumo">krumo library</a></strong>. <em>None</em> is a good option when stepping through the site in your debugger.', array('@krumo' => url('http://krumo.sourceforge.net'), '@choose' => url('admin/settings/error-reporting'))),
1068 );
1069 if (has_krumo()) {
1070 $form['devel_error_handler']['#options'][DEVEL_ERROR_HANDLER_BACKTRACE] = t('Backtrace');
1071 }
1072
1073 $options = drupal_map_assoc(array('default', 'blue', 'green', 'orange', 'schablon.com', 'disabled'));
1074 $form['devel_krumo_skin'] = array(
1075 '#type' => 'radios',
1076 '#title' => t('Krumo display'),
1077 '#description' => t('Select a skin for your debug messages or select <em>disabled</em> to display object and array output in standard PHP format.'),
1078 '#options' => $options,
1079 '#default_value' => variable_get('devel_krumo_skin', 'default'),
1080 );
1081
1082 $form['devel_rebuild_theme_registry'] = array(
1083 '#type' => 'checkbox',
1084 '#title' => t('Rebuild the theme registry on every page load'),
1085 '#description' => t('While creating new templates and theme_ overrides the theme registry needs to be rebuilt.'),
1086 '#default_value' => variable_get('devel_rebuild_theme_registry', FALSE),
1087 );
1088
1089 return system_settings_form($form);
1090 }
1091
1092 /**
1093 * Menu callback; clears all caches, then redirects to the previous page.
1094 */
1095 function devel_cache_clear() {
1096 // If you used to implement our own cache clear hook, implement hook_flush_caches instead. See drupal_flush_all_caches()
1097 drupal_flush_all_caches();
1098
1099 drupal_set_message('Cache cleared.');
1100
1101 drupal_goto();
1102 }
1103
1104 /**
1105 * Generates the execute block form.
1106 */
1107 function devel_execute_form() {
1108 $form['code'] = array(
1109 '#type' => 'textarea',
1110 '#title' => t('PHP code to execute'),
1111 '#description' => t('Enter some code. Do not use <code>&lt;?php ?&gt;</code> tags.')
1112 );
1113 $form['op'] = array('#type' => 'submit', '#value' => t('Execute'));