4 // This module holds functions useful for Drupal development.
7 // Suggested profiling and stacktrace library from http://www.xdebug.org/index.php
9 define('DEVEL_QUERY_SORT_BY_SOURCE', 0);
10 define('DEVEL_QUERY_SORT_BY_DURATION', 1);
12 define('DEVEL_ERROR_HANDLER_NONE', 0);
13 define('DEVEL_ERROR_HANDLER_STANDARD', 1);
14 define('DEVEL_ERROR_HANDLER_BACKTRACE', 2);
16 define('DEVEL_MIN_TEXTAREA', 50);
19 * Implementation of hook_help().
21 function devel_help($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>';
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>';
34 * Implementationation of hook_menu().
36 function devel_menu() {
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',
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',
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',
62 $items['devel/devel_themer'] = array(
63 'title callback' => 'devel_menu_title_theme_developer',
64 'title arguments' => array(NULL
),
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',
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',
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',
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',
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',
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',
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',
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',
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',
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',
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',
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',
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',
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',
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
,
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
,
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
,
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
,
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
,
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
,
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
,
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
,
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');
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().
253 function devel_menu_link_alter(&$item) {
254 if (in_array($item['link_path'], devel_menu_need_destination())) {
255 $item['options']['alter'] = TRUE
;
260 * An implementation of hook_translated_menu_item_alter(). Append dynamic
261 * querystring 'destination' to several of our own menu items.
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();
270 function devel_menu_title_theme_developer() {
271 if (module_exists('devel_themer')) {
272 return t('Disable Theme developer');
275 return t('Enable Theme developer');
279 function devel_devel_themer_toggle() {
280 if (module_exists('devel_themer')) {
281 module_disable(array('devel_themer'));
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'));
290 module_enable(array('devel_themer'));
293 drupal_theme_rebuild();
299 * Implementation of hook_theme()
301 function devel_theme() { // &$cache, $type, $theme, $path
303 'devel_querylog' => array(
304 'variables' => array('header' => array(), 'rows' => array()),
306 'devel_querylog_row' => array(
307 'variables' => array('row' => array()),
313 * Page callback to display syntax hilighted source code
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
319 * Set to FALSE to place the code inside a Drupal page. Otherwise code displays on its own.
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);
334 drupal_set_message(t('Invalid file path'), 'error');
340 * Return PHP highlighted file
344 * *warning* there is NO VALIDATION in this function
345 * Beware of paths such as '../../../../../etc/apache/httpd.conf'
348 * should the returned HTML be wrapped in a full <html> page or will it be output by Drupal?
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
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);
359 <head
><title
>$path</title
></head
>
371 * Implementation of hook_init().
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/
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';
386 $path = './'.
drupal_get_path('module', 'devel') .
'/FirePHPCore/lib/FirePHPCore/fb.php';
388 if (file_exists($path)) {
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');
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'))));
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') {
421 * Decide whether or not to print a debug variable using krumo().
426 function merits_krumo($input) {
427 return (is_object($input) || is_array($input)) && has_krumo() && variable_get('devel_krumo_skin', '') != 'disabled';
431 * Calls the http://www.firephp.org/ fb() function if it is found.
436 if (function_exists('fb') && user_access('access devel information')) {
437 $args = func_get_args();
438 call_user_func_array('fb', $args);
443 function devel_set_handler($handler) {
445 case DEVEL_ERROR_HANDLER_STANDARD
:
448 case DEVEL_ERROR_HANDLER_BACKTRACE
:
450 set_error_handler('backtrace_error_handler');
453 case DEVEL_ERROR_HANDLER_NONE
:
454 restore_error_handler();
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.
463 devel_verify_cli() ||
464 strpos($_SERVER['HTTP_USER_AGENT'], 'ApacheBench') !== FALSE
||
465 !empty($_REQUEST['XDEBUG_PROFILE']) ||
466 isset($GLOBALS['devel_shutdown']) ||
467 strstr($_SERVER['PHP_SELF'], 'update.php') ||
468 (isset($_GET['q']) && (
469 in_array($_GET['q'], array('upload/js', 'admin/content/node-settings/rebuild')) ||
470 substr($_GET['q'], 0, strlen('system/files')) == 'system/files' ||
471 substr($_GET['q'], 0, strlen('batch')) == 'batch')
476 * Implementation of hook_boot(). Runs even for cached pages.
478 function devel_boot() {
479 if (!devel_silent()) {
484 // Kickoff our tricks. Put here all code which must run for cached pages too. Called from both devel_boot() and devel_init().
485 function devel_start() {
486 if (variable_get('dev_mem', 0)) {
488 $memory_init = memory_get_usage();
491 if (devel_query_enabled()) {
492 //TODO: How best to include this?
493 @include_once DRUPAL_ROOT .
'/includes/database/log.inc';
494 Database
::startLog('devel');;
497 if (devel_code_coverage_enabled()) {
498 xdebug_start_code_coverage();
501 // We need user_access() in the shutdown function. make sure it gets loaded.
502 // Also prime the drupal_get_filename() static with user.module's location to
503 // avoid a stray query.
504 drupal_get_filename('module', 'user', 'modules/user/user.module');
505 drupal_load('module', 'user');
506 register_shutdown_function('devel_shutdown');
509 function backtrace_error_handler($errno, $message, $filename, $line) {
510 // Don't respond to the error if it was suppressed with a '@'
511 if (error_reporting() == 0) return;
513 if ($errno & (E_ALL ^ E_NOTICE
)) {
514 // We can't use the PHP E_* constants here as not all versions of PHP have all
515 // the constants defined, so for consistency, we just use the numeric equivelant.
522 32 => 'core warning',
523 64 => 'compile error',
524 128 => 'compile warning',
526 512 => 'user warning',
527 1024 => 'user notice',
528 2048 => 'strict warning',
529 4096 => 'recoverable error',
530 8192 => 'deprecated',
531 16384 => 'user deprecated',
533 $entry = $types[$errno] .
': '.
$message .
' in '.
$filename .
' on line '.
$line .
'.';
535 if (variable_get('error_level', 1) == 1) {
536 $backtrace = debug_backtrace();
537 foreach ($backtrace as
$call) {
538 $nicetrace[$call['function']] = $call;
543 watchdog('php', '%message in %file on line %line.', array('%error' => $types[$errno], '%message' => $message, '%file' => $filename, '%line' => $line), WATCHDOG_ERROR
);
548 * Implement hook_permission().
550 function devel_permission() {
552 'access devel information' => array(
553 'description' => t('View developer output like variable printouts, query log, etc.'),
554 'title' => t('Access developer information'),
556 'execute php code' => array(
557 'title' => t('Execute PHP code'),
558 'description' => t('Run arbitrary PHP from a block. Danger!'),
560 'switch users' => array(
561 'title' => t('Switch users'),
562 'description' => t('Become any user on the site with just a click. Danger!'),
564 'display source code' => array(
565 'title' => t('Display source code'),
566 'description' => t('View the site\'s php source code. Danger!'),
571 function devel_block_info() {
572 $blocks['execute_php'] = array(
573 'info' => t('Execute PHP'),
574 'cache' => DRUPAL_NO_CACHE
,
576 $blocks['switch_user'] = array(
577 'info' => t('Switch user'),
578 'cache' => DRUPAL_NO_CACHE
,
584 * Implementation of hook_block_configure().
586 function devel_block_configure($delta) {
587 if ($delta == 'switch_user') {
588 $form['devel_switch_user_list_size'] = array(
589 '#type' => 'textfield',
590 '#title' => t('Number of users to display in the list'),
591 '#default_value' => variable_get('devel_switch_user_list_size', 10),
599 function devel_block_save($delta, $edit = array()) {
600 if ($delta == 'switch_user') {
601 variable_set('devel_switch_user_list_size', $edit['devel_switch_user_list_size']);
605 function devel_block_view($delta) {
609 $block = devel_block_switch_user();
613 if (user_access('execute php code')) {
614 $block['subject'] = t('Execute PHP');
615 $block['content'] = drupal_get_form('devel_execute_form');
622 function devel_block_switch_user() {
623 $links = devel_switch_user_list();
624 if (!empty($links)) {
625 $block['subject'] = t('Switch user');
626 $build['devel_links'] = array('#theme' => 'links', '#links' => $links);
627 $build['devel_form'] = drupal_get_form('devel_switch_user_form');
628 $block['content'] = $build;
633 function devel_switch_user_list() {
635 if (user_access('switch users')) {
636 $list_size = variable_get('devel_switch_user_list_size', 10);
637 $dest = drupal_get_destination();
638 // Try to find at least $list_size users that can switch.
639 $roles = user_roles(1, 'switch users');
640 if (isset($roles[2])) {
641 // If authenticated users have this permission, just grab
642 // the last $list_size users, since there won't be records in
643 // {user_roles} and every user on the system can switch.
644 $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);
647 $where = array('u.uid = 1');
649 $where[] = 'r.rid IN ('.
implode(',', array_keys($roles)) .
')';
651 $where_sql = implode(' OR ', $where);
652 $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);
654 foreach ($users as
$user) {
655 $links[$user->uid
] = array(
656 'title' => drupal_placeholder(array('text' => $user->name
)),
657 'href' => 'devel/switch/'.
$user->name
,
659 'attributes' => array('title' => t('This user can switch back.')),
663 $num_links = count($links);
664 if ($num_links < $list_size) {
665 // If we don't have enough, add distinct uids until we hit $list_size.
666 $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);
667 foreach ($users as
$user) {
668 if (count($links) >= $list_size) {
671 $links[$user->uid
] = array(
672 'title' => $user->name ?
$user->name
: 'anon',
673 'href' => 'devel/switch/'.
$user->name
,
675 'attributes' => array('title' => t('Caution: this user will be unable switch back.')),
683 function devel_phpinfo() {
688 function devel_switch_user_form() {
689 $form['username'] = array(
690 '#type' => 'textfield',
691 '#description' => t('Enter username'),
692 '#autocomplete_path' => 'user/autocomplete',
693 '#maxlength' => USERNAME_MAX_LENGTH
,
696 $form['submit'] = array(
698 '#value' => t('Switch'),
704 function devel_doc_function_form() {
705 $version = devel_get_core_version(VERSION
);
706 $form['function'] = array(
707 '#type' => 'textfield',
708 '#description' => t('Enter function name for api lookup'),
712 $form['version'] = array('#type' => 'value', '#value' => $version);
713 $form['submit_button'] = array(
715 '#value' => t('Submit'),
720 function devel_doc_function_form_submit($form, &$form_state) {
721 $version = $form_state['values']['version'];
722 $function = $form_state['values']['function'];
723 $api = variable_get('devel_api_url', 'api.drupal.org');
724 $form_state['redirect'] = "http://$api/api/$version/function/$function";
727 function devel_switch_user_form_validate($form, &$form_state) {
728 if (!$account = user_load_by_name($form_state['values']['username'])) {
729 form_set_error('username', t('Username not found'));
733 function devel_switch_user_form_submit($form, &$form_state) {
734 $form_state['redirect'] = 'devel/switch/'.
$form_state['values']['username'];
739 * TODO: I switched params as per http://drupal.org/node/144132#form-alter but needs work still
740 * Implementation of hook_form_alter().
742 function devel_form_alter(&$form, $form_state, $form_id, $key_in = NULL
) {
743 if (user_access('access devel information') && variable_get('devel_form_weights', 0)) {
744 $children = element_children($form);
745 if (empty($children)) {
746 if (isset($form['#type']) && !in_array($form['#type'], array('value', 'hidden'))) {
747 if (!isset($form['#title'])) {
748 $form['#title'] = '';
750 $form['#title'] .
= " (key=$key_in, weight=".
(isset($form['#weight']) ?
$form['#weight'] : 0) .
')';
754 foreach (element_children($form) as
$key) {
755 // We need to add the weight to fieldsets.
756 if (element_children($form[$key])) { // Which are a container of others.
757 if (!isset($form[$key]['#title'])) {
758 $form[$key]['#title'] = '';
760 $form[$key]['#title'] .
= " (key=$key, weight=".
(isset($form[$key]['#weight']) ?
$form[$key]['#weight'] : 0) .
')';
762 devel_form_alter($form[$key], $form_state, $form_id, $key);
768 // An implementation of hook_exit().
769 function devel_exit($destination = NULL
) {
772 if (isset($destination) && !devel_silent()) {
773 // The page we are leaving is a drupal_goto(). Present a redirection page
774 // so that the developer can see the intermediate query log.
775 // We don't want to load user module here, so keep function_exists() call.
776 if (isset($user) && function_exists('user_access') && user_access('access devel information') && variable_get('devel_redirect_page', 0)) {
777 $output = t_safe('<p>The user is being redirected to <a href="@destination">@destination</a>.</p>', array('@destination' => $destination));
778 drupal_set_page_content($output);
779 $page = element_info('page');
780 print drupal_render_page($page);
782 // Don't allow the automatic redirect to happen.
783 drupal_page_footer();
787 $GLOBALS['devel_redirecting'] = TRUE
;
793 * See devel_start() which registers this function as a shutdown function.
795 function devel_shutdown() {
796 // Register the real shutdown function so it runs later than other shutdown functions.
797 register_shutdown_function('devel_shutdown_real');
800 // Borrowed from drush.
801 function devel_verify_cli() {
802 if (php_sapi_name() == 'cgi') {
803 return (is_numeric($_SERVER['argc']) && $_SERVER['argc'] > 0);
806 return (php_sapi_name() == 'cli');
809 function devel_page_alter($page) {
810 if (variable_get('devel_page_alter', FALSE
) && user_access('access devel information')) {
816 * See devel_shutdown() which registers this function as a shutdown function. Displays developer information in the footer.
818 function devel_shutdown_real() {
819 global $memory_init, $user;
822 // Set $GLOBALS['devel_shutdown'] = FALSE in order to supress the
823 // devel footer for a page. Not necessary if your page outputs any
824 // of the Content-type http headers tested below (e.g. text/xml,
825 // text/javascript, etc). This is is advised where applicable.
826 if (!isset($GLOBALS['devel_shutdown']) && !isset($GLOBALS['devel_redirecting'])) {
827 // Try not to break non html pages.
828 if (function_exists('drupal_get_header')) {
829 if ($headers = drupal_get_header()) {
830 $formats = array('xml', 'javascript', 'json', 'plain', 'image', 'application', 'csv', 'x-comma-separated-values');
831 foreach ($formats as
$format) {
832 if (strstr($headers['content-type'], $format)) {
839 if (isset($user) && user_access('access devel information')) {
840 if (devel_query_enabled()) {
841 $output .
= devel_shutdown_query();
843 if (variable_get('dev_mem', FALSE
)) {
844 $memory_shutdown = memory_get_usage();
845 $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));
846 $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>';
847 // theme() may not be available. not t() either.
848 $output .
= t_safe($msg, $args);
851 // Code coverage reporting.
852 if (devel_code_coverage_enabled()) {
853 $mode = variable_get('devel_code_coverage', FALSE
);
854 $coverage = xdebug_get_code_coverage();
856 $output .
= dpr(array_keys($coverage), TRUE
);
859 $output .
= dpr($coverage, TRUE
);
865 // TODO: gzip this text if we are sending a gzip page. see drupal_page_header().
866 // For some reason, this is not actually printing for cached pages even though it gets executed
867 // and $output looks good.
873 function devel_shutdown_query() {
875 $queries = Database
::getLog('devel', 'default');
876 list($counts, $query_summary) = devel_query_summary($queries);
877 // Query log off, timer on.
878 if (!variable_get('devel_query_display', 0) && variable_get('dev_timer', 0)) {
879 $output .
= '<div class="dev-timer">'.
devel_timer() .
' '.
$query_summary.
'</div>';
884 if (variable_get('devel_query_display', FALSE
)) {
885 $output .
= '<div class="dev-query">';
886 $output .
= $query_summary;
887 // calling theme() during shutdown is very bad if registry gets rebuilt like when making a change on admin/build/modules
888 // so we check for presence of theme registry before calling theme()
889 if (function_exists('theme_get_registry') && theme_get_registry()) {
890 $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)));
891 if (variable_get('dev_timer', 0)) {
892 $txt .
= devel_timer();
896 $output .
= devel_query_table($queries, $counts);
899 $output .
= $txt .
'</div>' .
dprint_r($queries, TRUE
);
906 function devel_query_enabled() {
907 return method_exists('Database', 'getLog') && variable_get('devel_query_display', FALSE
);
910 function devel_code_coverage_enabled() {
911 return function_exists('xdebug_get_code_coverage') && variable_get('devel_code_coverage', FALSE
);
914 function devel_query_summary($queries) {
915 if (variable_get('devel_query_display', FALSE
) && is_array($queries)) {
917 foreach ($queries as
$query) {
918 $text[] = $query['query'];
919 $sum += $query['time'];
921 $counts = array_count_values($text);
922 return array($counts, t_safe('Executed @queries queries in @time milliseconds.', array('@queries' => count($queries), '@time' => round($sum * 1000, 2))));
926 function t_safe($string, $args) {
927 // get_t caused problems here with theme registry after changing on admin/build/modules. the theme_get_registry call is needed.
928 if (function_exists('t') && function_exists('theme_get_registry')) {
929 theme_get_registry();
930 return t($string, $args);
933 strtr($string, $args);
938 * Returns a list of all currently defined user functions in the current
939 * request lifecycle, with links their documentation.
941 function devel_function_reference() {
942 $functions = get_defined_functions();
943 $version = devel_get_core_version(VERSION
);
944 $ufunctions = $functions['user'];
946 $api = variable_get('devel_api_url', 'api.drupal.org');
947 foreach ($ufunctions as
$function) {
948 $links[] = l($function, "http://$api/api/$version/function/$function");
950 return theme('item_list', array('items' => $links));
953 function devel_get_core_version($version) {
954 $version_parts = explode('.', $version);
955 // Map from 4.7.10 -> 4.7
956 if ($version_parts[0] < 5) {
957 return $version_parts[0] .
'.'.
$version_parts[1];
959 // Map from 5.5 -> 5 or 6.0-beta2 -> 6
961 return $version_parts[0];
965 // See http://drupal.org/node/126098
966 function devel_is_compatible_optimizer() {
969 $info = ob_get_contents();
972 // Match the Zend Optimizer version in the phpinfo information
973 $found = preg_match('/Zend Optimizer v([0-9])\.([0-9])\.([0-9])/', $info, $matches);
976 $major = $matches[1];
977 $minor = $matches[2];
978 $build = $matches[3];
984 elseif ($minor == 2 && $build >= 8) {
1000 function devel_admin_settings() {
1001 $form['queries'] = array('#type' => 'fieldset', '#title' => t('Query log'));
1003 $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.');
1004 if (!devel_is_compatible_optimizer()) {
1005 $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;
1007 $form['queries']['devel_query_display'] = array('#type' => 'checkbox',
1008 '#title' => t('Display query log'),
1009 '#default_value' => variable_get('devel_query_display', 0),
1010 '#description' => $description,
1011 '#disabled' => !devel_is_compatible_optimizer(),
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.'),
1019 $form['queries']['devel_execution'] = array('#type' => 'textfield',
1020 '#title' => t('Slow query highlighting'),
1021 '#default_value' => variable_get('devel_execution', 5),
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.'),
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.'),
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.'),
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>.'),
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.'),
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.'),
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.'),
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'))),
1070 $form['devel_error_handler']['#options'][DEVEL_ERROR_HANDLER_BACKTRACE
] = t('Backtrace');
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'),
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
),
1089 return system_settings_form($form);
1093 * Menu callback; clears all caches, then redirects to the previous page.
1095 function devel_cache_clear() {
1096 drupal_flush_all_caches();
1098 drupal_set_message('Cache cleared.');
1104 * Generates the execute block form.
1106 function devel_execute_form() {
1107 $form['code'] = array(
1108 '#type' => 'textarea',
1109 '#title' => t('PHP code to execute'),
1110 '#description' => t('Enter some code. Do not use <code><?php ?></code> tags.')
1112 $form['op'] = array('#type' => 'submit', '#value' => t('Execute'));
1113 $form['#redirect'] = FALSE
;
1114 $form['#skip_duplicate_check'] = TRUE
;
1119 * Process PHP execute form submissions.
1121 function devel_execute_form_submit($form, &$form_state) {
1123 print eval($form_state['values']['code']);
1124 dsm(ob_get_clean());
1128 * Menu callback; clear the database, resetting the menu to factory defaults.
1130 function devel_menu_rebuild() {
1132 drupal_set_message(t('The menu router has been rebuilt.'));
1137 * Display a dropdown of installed modules with the option to reinstall them.
1139 function devel_reinstall($form, &$form_state) {
1141 $modules = module_list();
1143 $options = drupal_map_assoc($modules);
1144 $form['list'] = array(
1145 '#type' => 'checkboxes',
1146 '#options' => $options,
1147 '#description' => t('Uninstall and then install the selected modules. <code>hook_uninstall()</code> and <code>hook_install()</code> will be executed and the schema version number will be set to the most recent update number. You may have to manually clear out any existing tables first if the module doesn\'t implement <code>hook_uninstall()</code>.'),
1149 $form['submit'] = array(
1150 '#value' => t('Reinstall'),
1151 '#type' => 'submit',
1153 if (empty($form_state['post'])) {
1154 drupal_set_message(t('Warning - will delete your module tables and variables.'), 'error');
1160 * Process reinstall menu form submissions.
1162 function devel_reinstall_submit($form, &$form_state) {
1163 require_once
'./includes/install.inc';
1164 $modules = array_filter($form_state['values']['list']);
1165 foreach ($modules as
$module) {
1166 module_load_install($module);
1167 $versions = drupal_get_schema_versions($module);
1168 drupal_set_installed_schema_version($module, SCHEMA_UNINSTALLED
);
1169 module_invoke($module, 'uninstall');
1170 _drupal_install_module($module);
1171 module_invoke($module, 'enable');
1172 drupal_get_schema(NULL
, TRUE
);
1173 drupal_set_message(t('Uninstalled and installed the %name module.', array('%name' => $module)));
1178 function devel_theme_registry() {
1179 drupal_theme_initialize();
1180 $hooks = theme_get_registry();
1182 return kprint_r($hooks, TRUE
);
1186 function devel_field_info_page() {
1187 $info = field_info_fields();
1188 return kprint_r($info, TRUE
);
1192 * Menu callback; display all variables.
1194 function devel_variable_page() {
1195 // We return our own $page so as to avoid blocks.
1196 $output = drupal_get_form('devel_variable_form');
1197 drupal_set_page_content($output);
1198 $page = element_info('page');
1202 function devel_variable_form() {
1204 'name' => array('data' => t('Name'), 'field' => 'name', 'sort' => 'asc'),
1205 'value' => array('data' => t('Value'), 'field' => 'value'),
1206 'length' => array('data' => t('Length'), 'field' => 'length'),
1207 'edit' => array('data' => t('Operations')),
1209 // TODO: we could get variables out of $conf but that would include hard coded ones too. ideally i would highlight overrridden/hard coded variables
1210 $query = db_select('variable', 'v')->extend('TableSort');
1211 $query->fields('v', array('name', 'value'));
1212 switch (db_driver()) {
1214 $query->addExpression("COL_LENGTH('{variable}', 'value')", 'length');
1217 $query->addExpression("CONVERT(LENGTH(v.value), UNSIGNED INTEGER)", 'length');
1221 ->orderByHeader($header)
1224 foreach ($result as
$row) {
1225 // $variables[$row->name] = '';
1226 $options[$row->name
]['name'] = check_plain($row->name
);
1227 if (merits_krumo($row->value
)) {
1228 $value = krumo_ob(variable_get($row->name
, NULL
));
1231 if (drupal_strlen($row->value
) > 70) {
1232 $value = check_plain(drupal_substr($row->value
, 0, 65)) .
'...';
1235 $value = check_plain($row->value
);
1238 $options[$row->name
]['value'] = $value;
1239 $options[$row->name
]['length'] = $row->length
;
1240 $options[$row->name
]['edit'] = l(t('Edit'), "devel/variable/edit/$row->name");
1242 $form['variables'] = array(
1243 '#type' => 'tableselect',
1244 '#header' => $header,
1245 '#options' => $options,
1246 '#empty' => t('No variables.'),
1248 $form['submit'] = array(
1249 '#type' => 'submit',
1250 '#value' => t('Delete'),
1257 function devel_variable_form_submit($form, &$form_state) {
1258 $deletes = array_filter($form_state['values']['variables']);
1259 array_walk($deletes, 'variable_del');
1260 drupal_set_message(format_plural(count($deletes), 'One variable deleted.', '@count variables deleted.'));
1263 function devel_variable_edit($form, &$form_state, $name) {
1264 $value = variable_get($name, 'not found');
1265 $form['name'] = array(
1269 $form['value'] = array(
1271 '#title' => t('Old value'),
1272 '#markup' => dpr($value, TRUE
),
1274 if (is_string($value) || is_numeric($value)) {
1275 $form['new'] = array(
1276 '#type' => 'textarea',
1277 '#title' => t('New value'),
1278 '#default_value' => $value
1280 $form['submit'] = array(
1281 '#type' => 'submit',
1282 '#value' => t('Submit'),
1286 $api = variable_get('devel_api_url', 'api.drupal.org');
1287 $form['new'] = array(
1289 '#title' => t('New value'),
1290 '#value' => t('Sorry, complex variable types may not be edited yet. Use the <em>Execute PHP</em> block and the <a href="@variable-set-doc">variable_set()</a> function.', array('@variable-set-doc' => "http://$api/api/HEAD/function/variable_set"))
1293 drupal_set_title($name);
1297 function devel_variable_edit_submit($form, &$form_state) {
1298 variable_set($form_state['values']['name'], $form_state['values']['new']);
1299 drupal_set_message(t('Saved new value for %name.', array('%name' => $form_state['values']['name'])));
1304 * Menu callback: display the session.
1306 function devel_session() {
1308 $output = kprint_r($_SESSION, TRUE
);
1309 $headers = array(t('Session name'), t('Session ID'));
1310 $output .
= theme('table', array('headers' => $headers, 'rows' => array(array(session_name(), session_id()))));
1315 * Switch from original user to another user and back.
1316 * We don't call session_save_session() because we really want to change users. Usually unsafe!
1318 * @param $name The username to switch to.
1320 function devel_switch_user($name = NULL
) {
1322 static
$orig_user = array();
1325 $user = user_load_by_name($name);
1327 // Retrieve the initial user. Can be called multiple times.
1328 elseif (count($orig_user)) {
1329 $user = array_shift($orig_user);
1330 array_unshift($orig_user, $user);
1332 // Store the initial user.
1334 $orig_user[] = $user;
1340 * Menu callback; prints the loaded structure of the current node/user.
1342 function devel_load_object($object, $name = NULL
) {
1343 $title = isset($object->title
) ?
$object->title
: $object->name
;
1344 drupal_set_title($title);
1345 return kdevel_print_object($object, '$'.
$name .
'->');
1350 * Menu callback; prints the render structure of the a term.
1352 function devel_render_term($term) {
1354 $build += field_attach_view('taxonomy_term', $term);
1355 if (!empty($term->description
)) {
1356 $build['term_description'] = array(
1357 '#markup' => filter_xss_admin($term->description
),
1359 '#prefix' => '<div class="taxonomy-term-description">',
1360 '#suffix' => '</div>',
1363 return kdevel_print_object($build, '$term->');
1367 * Menu callback; prints the render structure of the current object (currently node or user).
1369 function devel_render_object($type, $object) {
1371 $title = isset($object->title
) ?
$object->title
: $object->name
;
1372 // not sure why menu system doesn't give us a reasonable title here.
1373 drupal_set_title($title);
1374 $function = $type .
'_build_content';
1375 $content = $function($object);
1376 return kdevel_print_object($content, '$'.
$type .
'->');
1379 function devel_elements_page() {
1380 return kdevel_print_object(module_invoke_all('elements'));
1384 * Print an object or array using either Krumo (if installed) or devel_print_object()
1387 * array or object to print
1389 * prefixing for output items
1391 function kdevel_print_object($object, $prefix = NULL
) {
1392 return has_krumo() ?
krumo_ob($object) : devel_print_object($object, $prefix);
1395 // Save krumo htlm using output buffering.
1396 function krumo_ob($object) {
1399 $output = ob_get_contents();
1405 * Display an object or array
1408 * the object or array
1410 * prefix for the output items (example "$node->", "$user->", "$")
1412 * set to FALSE to suppress the output of the h3
1414 function devel_print_object($object, $prefix = NULL
, $header = TRUE
) {
1415 drupal_add_css(drupal_get_path('module', 'devel') .
'/devel.css');
1416 $output = '<div class="devel-obj-output">';
1418 $output .
= '<h3>'.
t('Display of !type !obj', array('!type' => str_replace(array('$', '->'), '', $prefix), '!obj' => gettype($object))) .
'</h3>';
1420 $output .
= _devel_print_object($object, $prefix);
1421 $output .
= '</div>';
1426 * Recursive (and therefore magical) function goes through an array or object and
1427 * returns a nicely formatted listing of its contents.
1430 * array or object to recurse through
1432 * prefix for the output items (example "$node->", "$user->", "$")
1441 * currently there are problems sending an array with a varname
1443 function _devel_print_object($obj, $prefix = NULL
, $parents = NULL
, $object = FALSE
) {
1444 static
$root_type, $out_format;
1446 // TODO: support objects with references. See http://drupal.org/node/234581.
1447 if (isset($obj->view
)) {
1451 if (!isset($root_type)) {
1452 $root_type = gettype($obj);
1453 if ($root_type == 'object') {
1458 if (is_object($obj)) {
1461 if (is_array($obj)) {
1463 foreach ($obj as
$field => $value) {
1464 if ($field == 'devel_flag_reference') {
1467 if (!is_null($parents)) {
1469 $field = $parents .
'->'.
$field;
1472 if (is_int($field)) {
1473 $field = $parents .
'['.
$field .
']';
1476 $field = $parents .
'[\''.
$field .
'\']';
1481 $type = gettype($value);
1483 $show_summary = TRUE
;
1485 if ($show_summary) {
1490 if (strlen($value) == 0) {
1491 $summary = t("{empty}");
1493 elseif (strlen($value) < 40) {
1494 $summary = htmlspecialchars($value);
1497 $summary = format_plural(drupal_strlen($value), '1 character', '@count characters');
1502 $summary = format_plural(count((array)$value), '1 element', '@count elements');
1505 $summary = $value ?
t('TRUE') : t('FALSE');
1509 if (!is_null($summary)) {
1510 $typesum = '('.
$type .
', <em>'.
$summary .
'</em>)';
1513 $typesum = '('.
$type .
')';
1516 $output .
= '<span class="devel-attr">';
1517 $output .
= "<dt><span class=\"field\">{$prefix}{$field}</span> $typesum</dt>\n";
1518 $output .
= "<dd>\n";
1519 // Check for references.
1520 if (is_array($value) && isset($value['devel_flag_reference'])) {
1521 $value['devel_flag_reference'] = TRUE
;
1523 // Check for references to prevent errors from recursions.
1524 if (is_array($value) && isset($value['devel_flag_reference']) && !$value['devel_flag_reference']) {
1525 $value['devel_flag_reference'] = FALSE
;
1526 $output .
= _devel_print_object($value, $prefix, $field);
1528 elseif (is_object($value)) {
1529 $value->devel_flag_reference
= FALSE
;
1530 $output .
= _devel_print_object((array)$value, $prefix, $field, TRUE
);
1533 $value = is_bool($value) ?
($value ?
'TRUE' : 'FALSE') : $value;
1534 $output .
= htmlspecialchars(print_r($value, TRUE
)) .
"\n";
1536 $output .
= "</dd></span>\n";
1538 $output .
= "</dl>\n";
1544 * Adds a table at the bottom of the page cataloguing data on all the database queries that were made to
1545 * generate the page.
1547 function devel_query_table($queries, $counts) {
1548 $version = devel_get_core_version(VERSION
);
1549 $header = array ('ms', '#', 'where', 'query', 'target');
1551 $api = variable_get('devel_api_url', 'api.drupal.org');
1552 foreach ($queries as
$query) {
1553 $function = $query['caller']['function'];
1554 $count = isset($counts[$query['query']]) ?
$counts[$query['query']] : 0;
1556 $diff = round($query['time'] * 1000, 2);
1557 if ($diff > variable_get('devel_execution', 5)) {
1558 $cell[$i][] = array ('data' => $diff, 'class' => 'marker');
1561 $cell[$i][] = $diff;
1564 $cell[$i][] = array ('data' => $count, 'class' => 'marker');
1567 $cell[$i][] = $count;
1569 $cell[$i][] = l($function, "http://$api/api/$version/function/$function");
1570 $cell[$i][] = check_plain($query['query']);
1571 $cell[$i][] = $query['target'];
1573 unset($diff, $count);
1575 if (variable_get('devel_query_sort', DEVEL_QUERY_SORT_BY_SOURCE
)) {
1576 usort($cell, '_devel_table_sort');
1578 return theme('devel_querylog', array('header' => $header, 'rows' => $cell));
1581 function theme_devel_querylog_row($variables) {
1582 $row = $variables['row'];
1585 foreach ($row as
$cell) {
1588 if (is_array($cell)) {
1589 $data = !empty($cell['data']) ?
$cell['data'] : '';
1590 unset($cell['data']);
1598 if (!empty($attr['class'])) {
1599 $attr['class'] .
= " cell cell-$i";
1602 $attr['class'] = "cell cell-$i";
1604 $attr = drupal_attributes($attr);
1606 $output .
= "<div $attr>$data</div>";
1611 function theme_devel_querylog($variables) {
1612 $header = $variables['header'];
1613 $rows = $variables['rows'];
1615 if (!empty($header)) {
1616 $output .
= "<div class='devel-querylog devel-querylog-header clear-block'>";
1617 $output .
= theme('devel_querylog_row', array('row' => $header));
1618 $output .
= "</div>";
1620 if (!empty($rows)) {
1622 foreach ($rows as
$row) {
1624 $zebra = ($i %
2) == 0 ?
'even' : 'odd';
1625 $output .
= "<div class='devel-querylog devel-querylog-$zebra clear-block'>";
1626 $output .
= theme('devel_querylog_row', array('row' => $row));
1627 $output .
= "</div>";
1633 function _devel_table_sort($a, $b) {
1634 $a = is_array($a[0]) ?
$a[0]['data'] : $a[0];
1635 $b = is_array($b[0]) ?
$b[0]['data'] : $b[0];
1646 * Displays page execution time at the bottom of the page.
1648 function devel_timer() {
1649 $time = timer_read('page');
1650 return t_safe(' Page execution time was @time ms.', array('@time' => $time));
1653 // An alias for drupal_debug().
1654 function dd($data, $label = NULL
) {
1655 return drupal_debug($data, $label);
1658 // Log any variable to a drupal_debug.log in the site's temp directory.
1659 // See http://drupal.org/node/314112
1660 function drupal_debug($data, $label = NULL
) {
1663 $string = ob_get_clean();
1665 $out = $label .
': '.
$string;
1672 // The temp directory does vary across multiple simpletest instances.
1673 $file = file_directory_path('temporary') .
'/drupal_debug.txt';
1674 if (file_put_contents($file, $out, FILE_APPEND
) === FALSE
) {
1675 drupal_set_message(t('The file could not be written.'), 'error');
1681 * Prints the arguments passed into the current function
1683 function dargs($always = TRUE
) {
1685 if ($always || !$printed) {
1686 $bt = debug_backtrace();
1687 print kdevel_print_object($bt[1]['args']);
1693 * Print a variable to the 'message' area of the page. Uses drupal_set_message()
1695 function dpm($input, $name = NULL
) {
1696 if (user_access('access devel information')) {
1697 $export = kprint_r($input, TRUE
, $name);
1698 drupal_set_message($export);
1703 * Var_dump() a variable to the 'message' area of the page. Uses drupal_set_message()
1705 function dvm($input, $name = NULL
) {
1706 if (user_access('access devel information')) {
1707 $export = dprint_r($input, TRUE
, $name, 'var_dump', FALSE
);
1708 drupal_set_message($export);
1712 // legacy function that was poorly named. use dpm() instead, since the 'p' maps to 'print_r'
1713 function dsm($input, $name = NULL
) {
1718 * An alias for dprint_r(). Saves carpal tunnel syndrome.
1720 function dpr($input, $return = FALSE
, $name = NULL
) {
1721 return dprint_r($input, $return, $name);
1725 * An alias for kprint_r(). Saves carpal tunnel syndrome.
1727 function kpr($input, $return = FALSE
, $name = NULL
) {
1728 return kprint_r($input, $return, $name);
1732 * Like dpr, but uses var_dump() instead
1734 function dvr($input, $return = FALSE
, $name = NULL
) {
1735 return dprint_r($input, $return, $name, 'var_dump', FALSE
);
1738 function kprint_r($input, $return = FALSE
, $name = NULL
, $function = 'print_r') {
1739 // We do not want to krumo() strings and integers and such
1740 if (merits_krumo($input)) {
1741 if (user_access('access devel information')) {
1742 return $return ?
(isset($name) ?
$name .
' => ' : '') .
krumo_ob($input) : krumo($input);
1746 return dprint_r($input, $return, $name, $function);
1751 * Pretty-print a variable to the browser (no krumo).
1752 * Displays only for users with proper permissions. If
1753 * you want a string returned instead of a print, use the 2nd param.
1755 function dprint_r($input, $return = FALSE
, $name = NULL
, $function = 'print_r', $check= TRUE
) {
1756 if (user_access('access devel information')) {
1762 $output = ob_get_clean();
1764 $output = check_plain($output);
1766 if (count($input, COUNT_RECURSIVE
) > DEVEL_MIN_TEXTAREA
) {
1767 // don't use fapi here because sometimes fapi will not be loaded
1768 $printed_value = "<textarea rows=30 style=\"width: 100%;\">\n".
$name .
$output .
'</textarea>';
1771 $printed_value = '<pre>'.
$name .
$output .
'</pre>';
1775 return $printed_value;
1778 print $printed_value;
1784 * Print the function call stack.
1786 function ddebug_backtrace() {
1787 if (user_access('access devel information')) {
1788 $trace = debug_backtrace();
1789 array_shift($trace);
1790 foreach ($trace as
$key => $value) {
1791 $rich_trace[$value['function']] = $value;
1794 print krumo($rich_trace);
1797 dprint_r($rich_trace);
1803 * migration related functions
1808 * Update node_comment_statistics table for nodes with comments.
1809 * TODO: if 2 comments have exact same timestamp, the function can get wrong uid and name fields.
1810 * Handles when comment timestamps have been manually set in admin
1814 function devel_rebuild_node_comment_statistics() {
1816 db_delete('node_comment_statistics');
1818 $sql = "INSERT INTO {node_comment_statistics} (nid, last_comment_timestamp, last_comment_name, last_comment_uid, comment_count) (SELECT nid, c.timestamp, name, uid, comment_count FROM {comments} c INNER JOIN (SELECT MAX(timestamp) AS timestamp, COUNT(*) AS comment_count FROM {comments} WHERE status=%d GROUP BY nid) AS c2 ON c.timestamp=c2.timestamp)";
1819 db_query($sql, COMMENT_PUBLISHED
);
1821 // Insert 0 count records into the node_comment_statistics for nodes that are missing. See comment_enable()
1822 db_query_temporary("SELECT n.nid, n.changed, n.uid FROM {node} n LEFT JOIN {node_comment_statistics} c ON n.nid = c.nid WHERE c.comment_count IS NULL", 'missing_nids');
1823 db_query("INSERT INTO {node_comment_statistics} (nid, last_comment_timestamp, last_comment_name, last_comment_uid, comment_count) SELECT n.nid, n.changed, NULL, n.uid, 0 FROM missing_nids n");