#599196 by joelstein | salvis: Fixed Allow switch user block to show only autocomplet...
[project/devel.git] / devel.module
CommitLineData
22c2656f 1<?php
8a6faf5a 2// $Id$
22c2656f 3
9d450e49 4// This module holds functions useful for Drupal development.
22c2656f 5// Please contribute!
6
0f79f53f 7// Suggested profiling and stacktrace library from http://www.xdebug.org/index.php
defc4865 8
c01a051a 9define('DEVEL_QUERY_SORT_BY_SOURCE', 0);
10define('DEVEL_QUERY_SORT_BY_DURATION', 1);
dcd35dd9 11
1c411384 12define('DEVEL_ERROR_HANDLER_NONE', 0);
13define('DEVEL_ERROR_HANDLER_STANDARD', 1);
14define('DEVEL_ERROR_HANDLER_BACKTRACE', 2);
15
eb8f82fc 16define('DEVEL_MIN_TEXTAREA', 50);
17
1e7a3874 18/**
9d450e49 19 * Implementation of hook_help().
1e7a3874 20 */
9d450e49 21function devel_help($section) {
22 switch ($section) {
24b0f0d8 23 case 'devel/reference':
7a2bcf56 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>';
24b0f0d8 25 case 'devel/session':
7a2bcf56 26 return '<p>'. t('Here are the contents of your <code>$_SESSION</code> variable.') .'</p>';
24b0f0d8 27 case 'devel/variable':
2ad63d14 28 $api = variable_get('devel_api_url', 'api.drupal.org');
7a2bcf56 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>';
d17db88e 30 case 'devel/reinstall':
31 return t('Warning - will delete your module tables and variables.');
fc909860 32 }
defc4865 33}
34
1e7a3874 35/**
c7414be0 36 * Implementationation of hook_menu().
1e7a3874 37 */
b50db35f 38function devel_menu() {
1e7a3874 39 $items = array();
049a7540 40 // Note: we can't dynamically append destination to querystring. Do so at theme layer. Fix in D7?
b50db35f 41 $items['devel/cache/clear'] = array(
a30583d1 42 'title' => 'Empty cache',
b50db35f 43 'page callback' => 'devel_cache_clear',
7ce30870 44 'description' => 'Clear the CSS cache and all database cache tables which store page, node, theme and variable caches.',
b50db35f 45 'access arguments' => array('access devel information'),
61b651a0 46 'file' => 'devel.pages.inc',
7ce30870 47 'menu_name' => 'devel',
b50db35f 48 );
83013f8f 49
b50db35f 50 $items['devel/reference'] = array(
612cee0e 51 'title' => 'Function reference',
52 'description' => 'View a list of currently defined user functions with documentation links.',
b50db35f 53 'page callback' => 'devel_function_reference',
54 'access arguments' => array('access devel information'),
61b651a0 55 'file' => 'devel.pages.inc',
612cee0e 56 'menu_name' => 'devel',
b50db35f 57 );
58 $items['devel/reinstall'] = array(
a30583d1 59 'title' => 'Reinstall modules',
5b73e5e4 60 'page callback' => 'drupal_get_form',
61 'page arguments' => array('devel_reinstall'),
612cee0e 62 'description' => 'Run hook_uninstall() and then hook_install() for a given module.',
b50db35f 63 'access arguments' => array('access devel information'),
61b651a0 64 'file' => 'devel.pages.inc',
612cee0e 65 'menu_name' => 'devel',
b50db35f 66 );
ff31e645
JR
67 $items['devel/source'] = array(
68 'title' => 'Display the PHP code of any file in your Drupal installation',
69 'page callback' => 'devel_display_source',
70 'access arguments' => array('display source code'),
71 'type' => MENU_CALLBACK,
61b651a0 72 'file' => 'devel.pages.inc',
612cee0e 73 'menu_name' => 'devel',
ff31e645 74 );
c714f2b3 75 $items['devel/menu/reset'] = array(
fdea4254 76 'title' => 'Rebuild menus',
612cee0e 77 'description' => 'Rebuild menu based on hook_menu() and revert any custom changes. All menu items return to their default settings.',
c714f2b3 78 'page callback' => 'drupal_get_form',
fdea4254 79 'page arguments' => array('devel_menu_rebuild'),
c714f2b3 80 'access arguments' => array('access devel information'),
61b651a0 81 'file' => 'devel.pages.inc',
612cee0e 82 'menu_name' => 'devel',
c714f2b3 83 );
f9624262 84 $items['devel/menu/item'] = array(
85 'title' => 'Menu item',
86 'description' => 'Details about a given menu item.',
87 'page callback' => 'devel_menu_item',
88 'access arguments' => array('access devel information'),
61b651a0 89 'file' => 'devel.pages.inc',
f9624262 90 'menu_name' => 'devel',
91 );
b50db35f 92 $items['devel/variable'] = array(
a30583d1 93 'title' => 'Variable editor',
612cee0e 94 'description' => 'Edit and delete site variables.',
b50db35f 95 'page callback' => 'devel_variable_page',
96 'access arguments' => array('access devel information'),
61b651a0 97 'file' => 'devel.pages.inc',
612cee0e 98 'menu_name' => 'devel',
b50db35f 99 );
2ad63d14 100 // we don't want the abbreviated version provided by status report
101 $items['devel/phpinfo'] = array(
a30583d1 102 'title' => 'PHPinfo()',
612cee0e 103 'description' => 'View your server\'s PHP configuration',
2ad63d14 104 'page callback' => 'devel_phpinfo',
105 'access arguments' => array('access devel information'),
61b651a0 106 'file' => 'devel.pages.inc',
612cee0e 107 'menu_name' => 'devel',
c7a47f27 108 );
109 $items['devel/php'] = array(
110 'title' => 'Execute PHP Code',
111 'description' => 'Execute some PHP code',
112 'page callback' => 'drupal_get_form',
113 'page arguments' => array('devel_execute_form'),
114 'access arguments' => array('execute php code'),
61b651a0 115 'file' => 'devel.pages.inc',
c7a47f27 116 'menu_name' => 'devel',
2ad63d14 117 );
118 $items['devel/theme/registry'] = array(
a30583d1 119 'title' => 'Theme registry',
683a91e6 120 'description' => 'View a list of available theme functions across the whole site.',
2ad63d14 121 'page callback' => 'devel_theme_registry',
122 'access arguments' => array('access devel information'),
61b651a0 123 'file' => 'devel.pages.inc',
612cee0e 124 'menu_name' => 'devel',
2ad63d14 125 );
b5e3b6c4 126 $items['devel/field/info'] = array(
127 'title' => 'Field info',
128 'description' => 'View fields information across the whole site.',
71f1ca71 129 'page callback' => 'devel_field_info_page',
b5e3b6c4 130 'access arguments' => array('access devel information'),
61b651a0 131 'file' => 'devel.pages.inc',
b5e3b6c4 132 'menu_name' => 'devel',
133 );
fc524554 134 $items['devel/elements'] = array(
612cee0e 135 'title' => 'Hook_elements()',
136 'description' => 'View the active form/render elements for this site.',
fc524554 137 'page callback' => 'devel_elements_page',
138 'access arguments' => array('access devel information'),
61b651a0 139 'file' => 'devel.pages.inc',
612cee0e 140 'menu_name' => 'devel',
fc524554 141 );
a30583d1 142 $items['devel/variable/edit/%'] = array(
143 'title' => 'Variable editor',
b50db35f 144 'page callback' => 'drupal_get_form',
a30583d1 145 'page arguments' => array('devel_variable_edit', 3),
b50db35f 146 'access arguments' => array('access devel information'),
147 'type' => MENU_CALLBACK,
61b651a0 148 'file' => 'devel.pages.inc',
612cee0e 149 'menu_name' => 'devel',
b50db35f 150 );
151 $items['devel/session'] = array(
a30583d1 152 'title' => 'Session viewer',
612cee0e 153 'description' => 'List the contents of $_SESSION.',
b50db35f 154 'page callback' => 'devel_session',
155 'access arguments' => array('access devel information'),
61b651a0 156 'file' => 'devel.pages.inc',
612cee0e 157 'menu_name' => 'devel',
b50db35f 158 );
159 $items['devel/switch'] = array(
a30583d1 160 'title' => 'Switch user',
b50db35f 161 'page callback' => 'devel_switch_user',
162 'access arguments' => array('switch users'),
163 'type' => MENU_CALLBACK,
61b651a0 164 'file' => 'devel.pages.inc',
612cee0e 165 'menu_name' => 'devel',
b50db35f 166 );
2b11d164 167 $items['devel/explain'] = array(
168 'title' => 'Explain query',
169 'page callback' => 'devel_querylog_explain',
170 'description' => 'Run an EXPLAIN on a given query. Used by query log',
171 'access arguments' => array('access devel information'),
61b651a0 172 'file' => 'devel.pages.inc',
2b11d164 173 'type' => MENU_CALLBACK
174 );
175 $items['devel/arguments'] = array(
176 'title' => 'Arguments query',
177 'page callback' => 'devel_querylog_arguments',
178 'description' => 'Return a giben query, with arguments instead of placeholders. Used by query log',
179 'access arguments' => array('access devel information'),
61b651a0 180 'file' => 'devel.pages.inc',
2b11d164 181 'type' => MENU_CALLBACK
182 );
1705c3ee
DR
183 $items['devel/run-cron'] = array(
184 'title' => 'Run cron',
185 'page callback' => 'system_run_cron',
186 'access arguments' => array('administer site configuration'),
187 'file' => 'system.admin.inc',
188 'file path' => drupal_get_path('module', 'system'),
189 'menu_name' => 'devel',
190 );
97db0981 191
192 // Duplicate path in 2 different menus. See http://drupal.org/node/601788.
193 $items['devel/settings'] = array(
612cee0e 194 'title' => 'Devel settings',
37e4c4f5 195 '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.',
b50db35f 196 'page callback' => 'drupal_get_form',
197 'page arguments' => array('devel_admin_settings'),
198 'access arguments' => array('administer site configuration'),
61b651a0 199 'file' => 'devel.admin.inc',
612cee0e 200 'menu_name' => 'devel',
b50db35f 201 );
97db0981 202 $items['admin/config/development/devel'] = array(
203 'title' => 'Devel settings',
204 '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.',
205 'page callback' => 'drupal_get_form',
206 'page arguments' => array('devel_admin_settings'),
61b651a0 207 'file' => 'devel.admin.inc',
97db0981 208 'access arguments' => array('administer site configuration'),
209 );
210
548c199e 211 $items['node/%node/devel'] = array(
212 'title' => 'Devel',
b50db35f 213 'page callback' => 'devel_load_object',
3efc2536 214 'page arguments' => array(1, 'node'),
b50db35f 215 'access arguments' => array('access devel information'),
216 'type' => MENU_LOCAL_TASK,
410b980f 217 'file' => 'devel.pages.inc',
fd3d06ac 218 'weight' => 100,
b50db35f 219 );
548c199e 220 $items['node/%node/devel/load'] = array(
221 'title' => 'Load',
222 'page callback' => 'devel_load_object',
223 'page arguments' => array(1, 'node'),
224 'access arguments' => array('access devel information'),
410b980f 225 'file' => 'devel.pages.inc',
548c199e 226 'type' => MENU_DEFAULT_LOCAL_TASK,
227 );
f5be6a5f 228 $items['node/%node/devel/render'] = array(
548c199e 229 'title' => 'Render',
16ee1727 230 'page callback' => 'devel_render_object',
231 'page arguments' => array('node', 1),
b50db35f 232 'access arguments' => array('access devel information'),
410b980f 233 'file' => 'devel.pages.inc',
b50db35f 234 'type' => MENU_LOCAL_TASK,
fd3d06ac 235 'weight' => 100,
b50db35f 236 );
548c199e 237 $items['comment/%comment/devel'] = array(
238 'title' => 'Devel',
2fa92e41 239 'page callback' => 'devel_load_object',
240 'page arguments' => array(1, 'comment'),
2fa92e41 241 'access arguments' => array('access devel information'),
242 'type' => MENU_LOCAL_TASK,
410b980f 243 'file' => 'devel.pages.inc',
2fa92e41 244 'weight' => 100,
245 );
548c199e 246 $items['comment/%comment/devel/load'] = array(
247 'title' => 'Load',
248 'page callback' => 'devel_load_object',
249 'page arguments' => array(1, 'comment'),
250 'access arguments' => array('access devel information'),
251 'type' => MENU_DEFAULT_LOCAL_TASK,
410b980f 252 'file' => 'devel.pages.inc',
548c199e 253 );
2fa92e41 254 $items['comment/%comment/devel/render'] = array(
548c199e 255 'title' => 'Render',
2fa92e41 256 'page callback' => 'devel_render_object',
257 'page arguments' => array('comment', 1),
2fa92e41 258 'access arguments' => array('access devel information'),
259 'type' => MENU_LOCAL_TASK,
410b980f 260 'file' => 'devel.pages.inc',
2fa92e41 261 'weight' => 100,
262 );
548c199e 263 $items['user/%user/devel'] = array(
264 'title' => 'Devel',
b50db35f 265 'page callback' => 'devel_load_object',
3efc2536 266 'page arguments' => array(1, 'user'),
b50db35f 267 'access arguments' => array('access devel information'),
268 'type' => MENU_LOCAL_TASK,
410b980f 269 'file' => 'devel.pages.inc',
fd3d06ac 270 'weight' => 100,
b50db35f 271 );
548c199e 272 $items['user/%user/devel/load'] = array(
273 'title' => 'Load',
274 'page callback' => 'devel_load_object',
275 'page arguments' => array(1, 'user'),
276 'access arguments' => array('access devel information'),
410b980f 277 'file' => 'devel.pages.inc',
548c199e 278 'type' => MENU_DEFAULT_LOCAL_TASK,
279 );
f5be6a5f 280 $items['user/%user/devel/render'] = array(
548c199e 281 'title' => 'Render',
16ee1727 282 'page callback' => 'devel_render_object',
283 'page arguments' => array('user', 1),
16ee1727 284 'access arguments' => array('access devel information'),
410b980f 285 'file' => 'devel.pages.inc',
16ee1727 286 'type' => MENU_LOCAL_TASK,
fd3d06ac 287 'weight' => 100,
16ee1727 288 );
548c199e 289 $items['taxonomy/term/%taxonomy_term/devel'] = array(
290 'title' => 'Devel',
42f270f9 291 'page callback' => 'devel_load_object',
292 'page arguments' => array(2, 'term'),
42f270f9 293 'access arguments' => array('access devel information'),
410b980f 294 'file' => 'devel.pages.inc',
42f270f9 295 'type' => MENU_LOCAL_TASK,
296 'weight' => 100,
297 );
548c199e 298 $items['taxonomy/term/%taxonomy_term/devel/load'] = array(
299 'title' => 'Load',
300 'page callback' => 'devel_load_object',
301 'page arguments' => array(2, 'term'),
302 'access arguments' => array('access devel information'),
410b980f 303 'file' => 'devel.pages.inc',
548c199e 304 'type' => MENU_DEFAULT_LOCAL_TASK,
305 );
42f270f9 306 $items['taxonomy/term/%taxonomy_term/devel/render'] = array(
548c199e 307 'title' => 'Render',
42f270f9 308 'page callback' => 'devel_render_term',
309 'page arguments' => array(2),
42f270f9 310 'access arguments' => array('access devel information'),
311 'type' => MENU_LOCAL_TASK,
410b980f 312 'file' => 'devel.pages.inc',
42f270f9 313 'weight' => 100,
314 );
8b832e7b 315
1e7a3874
JC
316 return $items;
317}
318
7ce30870 319function devel_menu_need_destination() {
de0da612 320 return array('devel/cache/clear', 'devel/reinstall', 'devel/menu/reset', 'devel/variable', 'admin/reports/status/run-cron');
7ce30870 321}
322
323/**
324 * An implementation of hook_menu_link_alter(). Flag this link as needing alter at display time.
2b11d164 325 * This is more robust that setting alter in hook_menu().
326 * @see devel_translated_menu_link_alter().
7ce30870 327 *
328 **/
e7aa9505 329function devel_menu_link_alter(&$item) {
f9624262 330 if (in_array($item['link_path'], devel_menu_need_destination()) || $item['link_path'] == 'devel/menu/item') {
7ce30870 331 $item['options']['alter'] = TRUE;
332 }
333}
334
b4a77be0 335/**
8b832e7b 336 * An implementation of hook_translated_menu_item_alter(). Append dynamic
b4a77be0 337 * querystring 'destination' to several of our own menu items.
8b832e7b 338 *
b4a77be0 339 **/
340function devel_translated_menu_link_alter(&$item) {
7ce30870 341 if (in_array($item['href'], devel_menu_need_destination())) {
b4a77be0 342 $item['localized_options']['query'] = drupal_get_destination();
343 }
f9624262 344 elseif ($item['href'] == 'devel/menu/item') {
345 $item['localized_options']['query'] = array('path' => $_GET['q']);
346 }
b4a77be0 347}
348
9d450e49 349/**
715d62f8 350 * Implementation of hook_theme()
351 */
d46ac02a 352function devel_theme() {
6407fb84 353 return array(
5d9df190 354 'devel_querylog' => array(
f01aa6ec 355 'variables' => array('header' => array(), 'rows' => array()),
5d9df190 356 ),
357 'devel_querylog_row' => array(
f01aa6ec 358 'variables' => array('row' => array()),
5d9df190 359 ),
6407fb84 360 );
715d62f8 361}
362
ff31e645 363/**
2ad63d14 364 * Implementation of hook_init().
9d450e49 365 */
1234041c 366function devel_init() {
2ad63d14 367 if (!devel_silent()) {
1234041c 368 if (user_access('access devel information')) {
2ad63d14 369 devel_set_handler(variable_get('devel_error_handler', DEVEL_ERROR_HANDLER_STANDARD));
f129ddc8 370 // We want to include the class early so that anyone may call krumo() as needed. See http://krumo.sourceforge.net/
00399d8f 371 has_krumo();
c9f90533 372
50a4afca 373 // Initialize XHProf.
374 if (extension_loaded('xhprof')) {
375 $path = variable_get('devel_xhprof_directory', '');
376 if ($path) {
377 include_once $path . '/xhprof_lib/utils/xhprof_lib.php';
378 include_once $path . '/xhprof_lib/utils/xhprof_runs.php';
379 if (variable_get('devel_xhprof_enabled', FALSE)) {
380 // @todo: consider a variable per-flag instead.
381 xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY);
382 }
383 }
384 }
385
722342b4 386 // See http://www.firephp.org/.
387 // Support Libraries API - http://drupal.org/project/libraries
6fae3934 388 if (module_exists('libraries')) {
6b928890 389 $path = libraries_get_path('FirePHPCore') . '/lib/FirePHPCore/';
722342b4 390 }
391 else {
6b928890 392 $path = './'. drupal_get_path('module', 'devel') .'/FirePHPCore/lib/FirePHPCore/';
722342b4 393 }
6b928890 394 if (file_exists($path .'fb.php')) {
395 include_once $path .'fb.php';
396 include_once $path .'FirePHP.class.php';
72671f4a 397 }
5d9df190 398 // Add CSS for query log if should be displayed.
399 if (variable_get('devel_query_display', 0)) {
7a2bcf56 400 drupal_add_css(drupal_get_path('module', 'devel') .'/devel.css');
2b11d164 401 drupal_add_js(drupal_get_path('module', 'devel'). '/devel.js');
5d9df190 402 }
1234041c 403 }
1c411384 404 }
a3c7d492 405 if (variable_get('devel_rebuild_theme_registry', FALSE)) {
406 drupal_theme_rebuild();
407 if (flood_is_allowed('devel_rebuild_registry_warning', 1)) {
408 flood_register_event('devel_rebuild_registry_warning');
409 if (!devel_silent() && user_access('access devel information')) {
5de9665a 410 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'))));
a3c7d492 411 }
412 }
413 }
2ad63d14 414}
415
897503be 416function devel_set_message($msg, $type = NULL) {
9efcdc47 417 $function = function_exists('drush_log') ? 'drush_log' : 'drupal_set_message';
418 $function($msg, $type);
897503be 419}
420
6531524e 421// Return boolean. No need for cache here.
00399d8f 422function has_krumo() {
423 // see README.txt or just download from http://krumo.sourceforge.net/
6531524e 424 @include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'devel') .'/krumo/class.krumo.php';
425 return function_exists('krumo') && !drupal_is_cli();
00399d8f 426}
427
f129ddc8 428/**
47257719 429 * Decide whether or not to print a debug variable using krumo().
430 *
8b832e7b 431 * @param $input
47257719 432 * @return boolean
433 */
434function merits_krumo($input) {
8b832e7b 435 return (is_object($input) || is_array($input)) && has_krumo() && variable_get('devel_krumo_skin', '') != 'disabled';
47257719 436}
437
438/**
f129ddc8 439 * Calls the http://www.firephp.org/ fb() function if it is found.
440 *
441 * @return void
442 */
443function dfb() {
cd4e857b 444 if (function_exists('fb') && user_access('access devel information') && !headers_sent()) {
f129ddc8 445 $args = func_get_args();
446 call_user_func_array('fb', $args);
447 }
448}
449
6b928890 450/**
451 * Calls dfb() to output a backtrace.
452 */
453function dfbt($label) {
454 dfb($label, FirePHP::TRACE);
455}
456
457/**
458 * Implements hook_watchdog().
459 */
460function devel_watchdog(array $log_entry) {
461 if (class_exists('FirePHP')) {
462 switch ($log_entry['severity']) {
6437f4c5 463 case WATCHDOG_EMERGENCY:
6b928890 464 case WATCHDOG_ALERT:
465 case WATCHDOG_CRITICAL:
466 case WATCHDOG_ERROR:
467 $type = FirePHP::ERROR;
468 break;
469 case WATCHDOG_WARNING:
470 $type = FirePHP::WARN;
471 break;
472 case WATCHDOG_NOTICE:
473 case WATCHDOG_INFO:
474 $type = FirePHP::INFO;
475 break;
476 case WATCHDOG_DEBUG:
477 DEFAULT:
478 $type = FirePHP::LOG;
479 }
480 }
481 else {
482 $type = 'watchdog';
483 }
6d8ccb50 484 $function = function_exists('decode_entities') ? 'decode_entities' : 'html_entity_decode';
6b928890 485 $watchdog = array(
486 'type' => $log_entry['type'],
6d8ccb50 487 'message' => $function(strtr($log_entry['message'], (array)$log_entry['variables'])),
6b928890 488 );
489 if (isset($log_entry['link'])) {
490 $watchdog['link'] = $log_entry['link'];
491 }
492 dfb($watchdog, $type);
493}
f129ddc8 494
2ad63d14 495function devel_set_handler($handler) {
496 switch ($handler) {
497 case DEVEL_ERROR_HANDLER_STANDARD:
498 // do nothing
499 break;
500 case DEVEL_ERROR_HANDLER_BACKTRACE:
7a2bcf56 501 if (has_krumo()) {
854d02b8 502 set_error_handler('backtrace_error_handler');
503 }
2ad63d14 504 break;
505 case DEVEL_ERROR_HANDLER_NONE:
506 restore_error_handler();
507 break;
508 }
509}
510
2ad63d14 511function devel_silent() {
a75b4d50 512 // isset($_GET['q']) is needed on IIS when calling the front page. q is not set.
8ab0a6c5 513 // Don't interfere with private files/images.
8b832e7b 514 return
2b11d164 515 function_exists('drupal_is_cli') && drupal_is_cli() ||
961d95ef 516 strpos($_SERVER['HTTP_USER_AGENT'], 'ApacheBench') !== FALSE ||
1ee49902 517 !empty($_REQUEST['XDEBUG_PROFILE']) ||
8b832e7b 518 isset($GLOBALS['devel_shutdown']) ||
519 strstr($_SERVER['PHP_SELF'], 'update.php') ||
e75a563a 520 (isset($_GET['q']) && (
bb99b63a 521 in_array($_GET['q'], array( 'admin/content/node-settings/rebuild')) ||
e75a563a 522 substr($_GET['q'], 0, strlen('system/files')) == 'system/files' ||
9a5ab9a9 523 substr($_GET['q'], 0, strlen('batch')) == 'batch') ||
524 substr($_GET['q'], 0, strlen('file/ajax')) == 'file/ajax'
e75a563a 525 );
2ad63d14 526}
527
528/**
529 * Implementation of hook_boot(). Runs even for cached pages.
530 */
531function devel_boot() {
532 if (!devel_silent()) {
533 devel_start();
1c411384 534 }
535}
536
b3c77c37 537// Kickoff our tricks. Put here all code which must run for cached pages too. Called from both devel_boot() and devel_init().
2ad63d14 538function devel_start() {
5fac26f0 539 if (variable_get('dev_mem', 0)) {
2ad63d14 540 global $memory_init;
541 $memory_init = memory_get_usage();
542 }
83013f8f 543
c7414be0 544 if (devel_query_enabled()) {
c7414be0 545 @include_once DRUPAL_ROOT . '/includes/database/log.inc';
546 Database::startLog('devel');;
547 }
83013f8f 548
4a35980f 549 // We need user_access() in the shutdown function. make sure it gets loaded.
550 // Also prime the drupal_get_filename() static with user.module's location to
551 // avoid a stray query.
552 drupal_get_filename('module', 'user', 'modules/user/user.module');
4c129b0b 553 drupal_load('module', 'user');
2ad63d14 554 register_shutdown_function('devel_shutdown');
555}
556
1c411384 557function backtrace_error_handler($errno, $message, $filename, $line) {
97fffd59 558 // Don't respond to the error if it was suppressed with a '@'
559 if (error_reporting() == 0) return;
8b832e7b 560
1c411384 561 if ($errno & (E_ALL ^ E_NOTICE)) {
de502988 562 // We can't use the PHP E_* constants here as not all versions of PHP have all
563 // the constants defined, so for consistency, we just use the numeric equivelant.
564 $types = array(
565 1 => 'error',
566 2 => 'warning',
567 4 => 'parse error',
568 8 => 'notice',
569 16 => 'core error',
570 32 => 'core warning',
571 64 => 'compile error',
572 128 => 'compile warning',
573 256 => 'user error',
574 512 => 'user warning',
575 1024 => 'user notice',
576 2048 => 'strict warning',
577 4096 => 'recoverable error',
578 8192 => 'deprecated',
579 16384 => 'user deprecated',
580 );
1c411384 581 $entry = $types[$errno] .': '. $message .' in '. $filename .' on line '. $line .'.';
582
583 if (variable_get('error_level', 1) == 1) {
584 $backtrace = debug_backtrace();
854d02b8 585 foreach ($backtrace as $call) {
586 $nicetrace[$call['function']] = $call;
1c411384 587 }
854d02b8 588 krumo($nicetrace);
1c411384 589 }
590
93ae3df4 591 watchdog('php', '%message in %file on line %line.', array('%error' => $types[$errno], '%message' => $message, '%file' => $filename, '%line' => $line), WATCHDOG_ERROR);
c380bd17 592 }
74b35ebe 593}
594
9d450e49 595/**
1e12a7ae 596 * Implement hook_permission().
9d450e49 597 */
1e12a7ae 598function devel_permission() {
32c93e75 599 return array(
e33d0a65 600 'access devel information' => array(
83013f8f 601 'description' => t('View developer output like variable printouts, query log, etc.'),
e33d0a65 602 'title' => t('Access developer information'),
603 ),
604 'execute php code' => array(
605 'title' => t('Execute PHP code'),
606 'description' => t('Run arbitrary PHP from a block. Danger!'),
607 ),
608 'switch users' => array(
609 'title' => t('Switch users'),
610 'description' => t('Become any user on the site with just a click. Danger!'),
611 ),
612 'display source code' => array(
613 'title' => t('Display source code'),
614 'description' => t('View the site\'s php source code. Danger!'),
615 ),
32c93e75 616 );
9d450e49 617}
618
d5e87c9b 619function devel_block_info() {
620 $blocks['execute_php'] = array(
621 'info' => t('Execute PHP'),
622 'cache' => DRUPAL_NO_CACHE,
623 );
624 $blocks['switch_user'] = array(
625 'info' => t('Switch user'),
626 'cache' => DRUPAL_NO_CACHE,
627 );
e83aafef 628 return $blocks;
629}
630
631/**
d5e87c9b 632 * Implementation of hook_block_configure().
e83aafef 633 */
634function devel_block_configure($delta) {
d5e87c9b 635 if ($delta == 'switch_user') {
714bcfeb
DW
636 $form['devel_switch_user_list_size'] = array(
637 '#type' => 'textfield',
638 '#title' => t('Number of users to display in the list'),
639 '#default_value' => variable_get('devel_switch_user_list_size', 10),
640 '#size' => '3',
641 '#maxlength' => '4',
642 );
643 return $form;
644 }
e83aafef 645}
646
647function devel_block_save($delta, $edit = array()) {
d5e87c9b 648 if ($delta == 'switch_user') {
714bcfeb
DW
649 variable_set('devel_switch_user_list_size', $edit['devel_switch_user_list_size']);
650 }
e83aafef 651}
652
653function devel_block_view($delta) {
654 $block = array();
655 switch ($delta) {
d5e87c9b 656 case 'switch_user':
e83aafef 657 $block = devel_block_switch_user();
658 break;
d5e87c9b 659
660 case 'execute_php':
e83aafef 661 if (user_access('execute php code')) {
662 $block['subject'] = t('Execute PHP');
a2d2befa 663 $block['content'] = drupal_get_form('devel_execute_form');
e83aafef 664 }
665 break;
2ad63d14 666 }
e83aafef 667 return $block;
2ad63d14 668}
9d450e49 669
2ad63d14 670function devel_block_switch_user() {
a200276a 671 $links = devel_switch_user_list();
43c43215 672 if (!empty($links) || user_access('switch users')) {
e83aafef 673 $block['subject'] = t('Switch user');
ecea88f1 674 $build['devel_links'] = array('#theme' => 'links', '#links' => $links);
a2d2befa 675 $build['devel_form'] = drupal_get_form('devel_switch_user_form');
676 $block['content'] = $build;
a200276a
DW
677 return $block;
678 }
679}
680
681function devel_switch_user_list() {
2ad63d14 682 $links = array();
683 if (user_access('switch users')) {
2cbf0d1e 684 $list_size = variable_get('devel_switch_user_list_size', 10);
2ad63d14 685 $dest = drupal_get_destination();
2cbf0d1e 686 // Try to find at least $list_size users that can switch.
2ad63d14 687 $roles = user_roles(1, 'switch users');
50c2fef7
DW
688 if (isset($roles[2])) {
689 // If authenticated users have this permission, just grab
2cbf0d1e 690 // the last $list_size users, since there won't be records in
50c2fef7 691 // {user_roles} and every user on the system can switch.
2cbf0d1e 692 $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);
50c2fef7
DW
693 }
694 else {
695 $where = array('u.uid = 1');
696 if (count($roles)) {
697 $where[] = 'r.rid IN ('. implode(',', array_keys($roles)) .')';
698 }
699 $where_sql = implode(' OR ', $where);
2cbf0d1e 700 $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);
2ad63d14 701 }
eb20c8dd 702 foreach ($users as $user) {
e83aafef 703 $links[$user->uid] = array(
c9f90533 704 'title' => drupal_placeholder(array('text' => $user->name)),
7a2bcf56 705 'href' => 'devel/switch/'. $user->name,
e83aafef 706 'query' => $dest,
707 'attributes' => array('title' => t('This user can switch back.')),
708 'html' => TRUE,
709 );
2ad63d14 710 }
711 $num_links = count($links);
2cbf0d1e
DW
712 if ($num_links < $list_size) {
713 // If we don't have enough, add distinct uids until we hit $list_size.
714 $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);
eb20c8dd 715 foreach ($users as $user) {
716 if (count($links) >= $list_size) {
717 break;
718 }
e83aafef 719 $links[$user->uid] = array(
720 'title' => $user->name ? $user->name : 'anon',
7a2bcf56 721 'href' => 'devel/switch/'. $user->name,
e83aafef 722 'query' => $dest,
723 'attributes' => array('title' => t('Caution: this user will be unable switch back.')),
724 );
2ad63d14 725 }
726 }
727 }
a200276a 728 return $links;
2ad63d14 729}
730
15dcd830 731function devel_switch_user_form() {
732 $form['username'] = array(
733 '#type' => 'textfield',
734 '#description' => t('Enter username'),
d47ac609
DW
735 '#autocomplete_path' => 'user/autocomplete',
736 '#maxlength' => USERNAME_MAX_LENGTH,
15dcd830 737 '#size' => 16,
15dcd830 738 );
739 $form['submit'] = array(
1234041c 740 '#type' => 'submit',
15dcd830 741 '#value' => t('Switch'),
742 );
743 return $form;
1234041c 744
15dcd830 745}
746
2ad63d14 747function devel_doc_function_form() {
f2a633ed 748 $version = devel_get_core_version(VERSION);
2ad63d14 749 $form['function'] = array(
750 '#type' => 'textfield',
751 '#description' => t('Enter function name for api lookup'),
752 '#size' => 16,
753 '#maxlength' => 255,
754 );
755 $form['version'] = array('#type' => 'value', '#value' => $version);
756 $form['submit_button'] = array(
1234041c 757 '#type' => 'submit',
2ad63d14 758 '#value' => t('Submit'),
759 );
760 return $form;
761}
762
1234041c
YC
763function devel_doc_function_form_submit($form, &$form_state) {
764 $version = $form_state['values']['version'];
765 $function = $form_state['values']['function'];
2ad63d14 766 $api = variable_get('devel_api_url', 'api.drupal.org');
767 $form_state['redirect'] = "http://$api/api/$version/function/$function";
768}
769
1234041c 770function devel_switch_user_form_validate($form, &$form_state) {
a2d2befa 771 if (!$account = user_load_by_name($form_state['values']['username'])) {
15dcd830 772 form_set_error('username', t('Username not found'));
1234041c 773 }
15dcd830 774}
775
1234041c
YC
776function devel_switch_user_form_submit($form, &$form_state) {
777 $form_state['redirect'] = 'devel/switch/'. $form_state['values']['username'];
15dcd830 778}
779
97db0981 780// An implementation of hook_drupal_goto_alter().
781function devel_drupal_goto_alter($path, $options, $http_response_code) {
f6f6cda4 782 global $user;
a30583d1 783
97db0981 784 if (isset($path) && !devel_silent()) {
54fecd4a
JC
785 // The page we are leaving is a drupal_goto(). Present a redirection page
786 // so that the developer can see the intermediate query log.
e87cf471 787 // We don't want to load user module here, so keep function_exists() call.
71d543d7 788 if (isset($user) && function_exists('user_access') && user_access('access devel information') && variable_get('devel_redirect_page', 0)) {
97db0981 789 $destination = function_exists('url') ? url($path, $options) : $path;
71d543d7 790 $output = t_safe('<p>The user is being redirected to <a href="@destination">@destination</a>.</p>', array('@destination' => $destination));
97db0981 791 drupal_deliver_page($output);
8416c64c 792
54fecd4a 793 // Don't allow the automatic redirect to happen.
54fecd4a
JC
794 exit();
795 }
8416c64c 796 else {
3f06b311 797 $GLOBALS['devel_redirecting'] = TRUE;
8416c64c 798 }
54fecd4a 799 }
ba836d3a 800}
8416c64c 801
ba836d3a 802/**
6ff481db 803 * See devel_start() which registers this function as a shutdown function.
ba836d3a 804 */
805function devel_shutdown() {
b0771e29 806 // Register the real shutdown function so it runs later than other shutdown functions.
807 register_shutdown_function('devel_shutdown_real');
808}
809
d131f63d 810function devel_page_alter($page) {
811 if (variable_get('devel_page_alter', FALSE) && user_access('access devel information')) {
812 dpm($page, 'page');
813 }
814}
d89dc447 815
68347211 816// AJAX render reponses sometimers are sent as text/html so we have to catch them here
817// and disable our footer stuff.
818function devel_ajax_render_alter() {
819 $GLOBALS['devel_shutdown'] = FALSE;
820}
821
b0771e29 822/**
823 * See devel_shutdown() which registers this function as a shutdown function. Displays developer information in the footer.
824 */
825function devel_shutdown_real() {
19f75490 826 global $user;
0f79f53f 827 $output = $txt = '';
2ad63d14 828
91abefea 829 // Set $GLOBALS['devel_shutdown'] = FALSE in order to supress the
830 // devel footer for a page. Not necessary if your page outputs any
831 // of the Content-type http headers tested below (e.g. text/xml,
832 // text/javascript, etc). This is is advised where applicable.
3f06b311 833 if (!isset($GLOBALS['devel_shutdown']) && !isset($GLOBALS['devel_redirecting'])) {
3386015e 834 // Try not to break non html pages.
bb99b63a 835 if (function_exists('drupal_get_http_header')) {
836 $header = drupal_get_http_header('content-type');
837 if ($header) {
851bdad2 838 $formats = array('xml', 'javascript', 'json', 'plain', 'image', 'application', 'csv', 'x-comma-separated-values');
839 foreach ($formats as $format) {
bb99b63a 840 if (strstr($header, $format)) {
851bdad2 841 return;
842 }
91abefea 843 }
3386015e 844 }
631ca979 845 }
8416c64c 846
f6f6cda4 847 if (isset($user) && user_access('access devel information')) {
19f75490 848 $queries = Database::getLog('devel', 'default');
849 $output .= devel_shutdown_summary($queries);
850 $output .= devel_shutdown_query($queries);
40d052a2 851 }
3efc2536 852
40d052a2 853 if ($output) {
b3c77c37 854 // TODO: gzip this text if we are sending a gzip page. see drupal_page_header().
d5e87c9b 855 // For some reason, this is not actually printing for cached pages even though it gets executed
5eb79cbf 856 // and $output looks good.
40d052a2 857 print $output;
1234041c 858 }
3386015e 859 }
ba836d3a 860}
861
19f75490 862function devel_shutdown_summary($queries) {
863 $sum = 0;
8865b684 864 $output = '';
c7414be0 865 list($counts, $query_summary) = devel_query_summary($queries);
19f75490 866
d6032121 867 $output .= '<div class="dev-query">';
868
869 if (variable_get('devel_query_display', FALSE)) {
19f75490 870 // Query log on.
c7414be0 871 $output .= $query_summary;
d6032121 872 $output .= t_safe(' Queries exceeding @threshold ms are <span class="marker">highlighted</span>.', array('@threshold' => variable_get('devel_execution', 5)));
c7414be0 873 }
83013f8f 874
d6032121 875 if (variable_get('dev_timer', 0)) {
876 $output .= devel_timer();
877 }
878
50a4afca 879 if (variable_get('devel_xhprof_enabled', FALSE)) {
880 $output .= devel_xhprof_output();
881 }
882
d6032121 883 $output .= devel_shutdown_memory();
884
885 $output .= '</div>';
886
c7414be0 887 return $output;
888}
889
50a4afca 890function devel_xhprof_output() {
891 $namespace = variable_get('site_name', ''); // namespace for your application
892 $xhprof_data = xhprof_disable();
893
894 $xhprof_runs = new XHProfRuns_Default();
895 $run_id = $xhprof_runs->save_run($xhprof_data, $namespace);
896 // @todo: render results from within Drupal.
897 $xhprof_url = variable_get('devel_xhprof_url', '');
898 if ($xhprof_url) {
899 return t('<a href="@xhprof">XHProf output</a>', array('@xhprof' => $xhprof_url . "/index.php?run=$run_id&source=$namespace"));
900 }
901}
902
19f75490 903function devel_shutdown_memory() {
904 global $memory_init;
905
906 if (variable_get('dev_mem', FALSE)) {
907 $memory_shutdown = memory_get_usage();
908 $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));
909 $msg = '<span class="dev-memory-usages"> Memory used at: devel_boot()=<strong>@memory_boot</strong> MB, devel_shutdown()=<strong>@memory_shutdown</strong> MB, PHP peak=<strong>@memory_peak</strong> MB.</span>';
910 // theme() may not be available. not t() either.
911 return t_safe($msg, $args);
912 }
913}
914
915function devel_shutdown_query($queries) {
04a5cd55 916 if (!empty($queries)) {
04a5cd55 917 if (function_exists('theme_get_registry') && theme_get_registry()) {
918 // Safe to call theme('table).
919 list($counts, $query_summary) = devel_query_summary($queries);
321e2c8a 920 $output = devel_query_table($queries, $counts);
19f75490 921
04a5cd55 922 // Save all queries to a file in temp dir. Retrieved via AJAX.
923 devel_query_put_contents($queries);
924 }
925 else {
321e2c8a 926 $output = '</div>' . dprint_r($queries, TRUE);
04a5cd55 927 }
321e2c8a 928 return $output;
19f75490 929 }
19f75490 930}
931
2b11d164 932// Write the variables information to the a file. It will be retrieved on demand via AJAX.
933function devel_query_put_contents($queries) {
934 $request_id = mt_rand(1, 1000000);
935 $path = "temporary://devel_querylog";
936
937 // Create the devel_querylog within the temp folder, if needed.
938 file_prepare_directory($path, FILE_CREATE_DIRECTORY);
2b11d164 939
940 // Occassionally wipe the querylog dir so that files don't accumulate.
941 if (mt_rand(1, 1000) == 401) {
942 devel_empty_dir($path);
943 }
944
a1cc25d1 945 $path .= "/$request_id.txt";
946 $path = file_stream_wrapper_uri_normalize($path);
2b11d164 947 // Save queries as a serialized array.
948 file_put_contents($path, serialize($queries));
949 $settings['devel'] = array(
950 // A random string that is sent to the browser. It enables the AJAX to retrieve queries from this request.
951 'request_id' => $request_id,
2b11d164 952 );
953 print '<script type="text/javascript">jQuery.extend(Drupal.settings, '. json_encode($settings) .");</script>\n";
954}
955
c7414be0 956function devel_query_enabled() {
1b6f0f46 957 return method_exists('Database', 'getLog') && variable_get('devel_query_display', FALSE);
c7414be0 958}
959
c7414be0 960function devel_query_summary($queries) {
1b6f0f46 961 if (variable_get('devel_query_display', FALSE) && is_array($queries)) {
c5c59e20 962 $sum = 0;
a0610ff4 963 foreach ($queries as $query) {
c7414be0 964 $text[] = $query['query'];
965 $sum += $query['time'];
a0610ff4 966 }
967 $counts = array_count_values($text);
19f75490 968 return array($counts, t_safe('Executed @queries queries in @time ms.', array('@queries' => count($queries), '@time' => round($sum * 1000, 2))));
ed856aa6 969 }
ed856aa6 970}
971
2ad63d14 972function t_safe($string, $args) {
6407fb84 973 // get_t caused problems here with theme registry after changing on admin/build/modules. the theme_get_registry call is needed.
0f79f53f 974 if (function_exists('t') && function_exists('theme_get_registry')) {
975 theme_get_registry();
976 return t($string, $args);
977 }
978 else {
979 strtr($string, $args);
980 }
3386015e 981}
982
f2a633ed
DW
983function devel_get_core_version($version) {
984 $version_parts = explode('.', $version);
985 // Map from 4.7.10 -> 4.7
986 if ($version_parts[0] < 5) {
987 return $version_parts[0] .'.'. $version_parts[1];
988 }
989 // Map from 5.5 -> 5 or 6.0-beta2 -> 6
990 else {
991 return $version_parts[0];
992 }
2ad63d14 993}
1234041c 994
c871aa0b 995// See http://drupal.org/node/126098
996function devel_is_compatible_optimizer() {
997 ob_start();
998 phpinfo();
999 $info = ob_get_contents();
1000 ob_end_clean();
1001
fdd7e742 1002 // Match the Zend Optimizer version in the phpinfo information
c871aa0b 1003 $found = preg_match('/Zend&nbsp;Optimizer&nbsp;v([0-9])\.([0-9])\.([0-9])/', $info, $matches);
1004
1005 if ($matches) {
1006 $major = $matches[1];
1007 $minor = $matches[2];
1008 $build = $matches[3];
8b832e7b 1009
c871aa0b 1010 if ($major >= 3) {
1011 if ($minor >= 3) {
1012 return TRUE;
8b832e7b 1013 }
c871aa0b 1014 elseif ($minor == 2 && $build >= 8) {
1015 return TRUE;
8b832e7b 1016 }
c871aa0b 1017 else {
1018 return FALSE;
1019 }
8b832e7b 1020 }
c871aa0b 1021 else {
1022 return FALSE;
1023 }
1024 }
1025 else {
1026 return TRUE;
1027 }
1028}
1029
1e7a3874 1030/**
d31522e4 1031 * Generates the execute block form.
1e7a3874 1032 */
d31522e4 1033function devel_execute_form() {
f04f2a57
Z
1034 $form['code'] = array(
1035 '#type' => 'textarea',
9d450e49 1036 '#title' => t('PHP code to execute'),
f04f2a57 1037 '#description' => t('Enter some code. Do not use <code>&lt;?php ?&gt;</code> tags.')
9d450e49 1038 );
1039 $form['op'] = array('#type' => 'submit', '#value' => t('Execute'));
d31522e4 1040 $form['#redirect'] = FALSE;
e75e900d 1041 $form['#skip_duplicate_check'] = TRUE;
d31522e4 1042 return $form;
e0379979 1043}
1044
1e7a3874 1045/**
d31522e4 1046 * Process PHP execute form submissions.
1e7a3874 1047 */
91f34f17 1048function devel_execute_form_submit($form, &$form_state) {
d31522e4 1049 ob_start();
278b805f 1050 print eval($form_state['values']['code']);
d31522e4 1051 dsm(ob_get_clean());
22c2656f 1052}
1053
f3ef93f8 1054/**
9d450e49 1055 * Switch from original user to another user and back.
5b73e5e4 1056 * We don't call session_save_session() because we really want to change users. Usually unsafe!
9d450e49 1057 *
15dcd830 1058 * @param $name The username to switch to.
9f4ee613 1059 */
15dcd830 1060function devel_switch_user($name = NULL) {
9d450e49 1061 global $user;
1062 static $orig_user = array();
1063
15dcd830 1064 if (isset($name)) {
a2d2befa 1065 $user = user_load_by_name($name);
9d450e49 1066 }
1067 // Retrieve the initial user. Can be called multiple times.
7a2bcf56 1068 elseif (count($orig_user)) {
9d450e49 1069 $user = array_shift($orig_user);
1070 array_unshift($orig_user, $user);
9f4ee613 1071 }
9d450e49 1072 // Store the initial user.
1073 else {
1074 $orig_user[] = $user;
1075 }
1076 drupal_goto();
9f4ee613
MI
1077}
1078
0356e334 1079/**
3efc2536
JR
1080 * Print an object or array using either Krumo (if installed) or devel_print_object()
1081 *
1082 * @param $object
1083 * array or object to print
1084 * @param $prefix
1085 * prefixing for output items
1086 */
1087function kdevel_print_object($object, $prefix = NULL) {
1088 return has_krumo() ? krumo_ob($object) : devel_print_object($object, $prefix);
00399d8f 1089}
1090
1091// Save krumo htlm using output buffering.
1092function krumo_ob($object) {
1093 ob_start();
1094 krumo($object);
1095 $output = ob_get_contents();
1096 ob_end_clean();
1097 return $output;
fc524554 1098}
1099
3efc2536
JR
1100/**
1101 * Display an object or array
1102 *
1103 * @param $object
1104 * the object or array
1105 * @param $prefix
1106 * prefix for the output items (example "$node->", "$user->", "$")
1107 * @param $header
1108 * set to FALSE to suppress the output of the h3
1109 */
1110function devel_print_object($object, $prefix = NULL, $header = TRUE) {
7a2bcf56 1111 drupal_add_css(drupal_get_path('module', 'devel') .'/devel.css');
3efc2536
JR
1112 $output = '<div class="devel-obj-output">';
1113 if ($header) {
7a2bcf56 1114 $output .= '<h3>'. t('Display of !type !obj', array('!type' => str_replace(array('$', '->'), '', $prefix), '!obj' => gettype($object))) .'</h3>';
3efc2536
JR
1115 }
1116 $output .= _devel_print_object($object, $prefix);
1117 $output .= '</div>';
1118 return $output;
1119}
0356e334 1120
3efc2536
JR
1121/**
1122 * Recursive (and therefore magical) function goes through an array or object and
1123 * returns a nicely formatted listing of its contents.
1124 *
1125 * @param $obj
1126 * array or object to recurse through
1127 * @param $prefix
1128 * prefix for the output items (example "$node->", "$user->", "$")
1129 * @param $parents
1130 * used by recursion
1131 * @param $object
1132 * used by recursion
1133 * @return
1134 * fomatted html
1135 *
1136 * @todo
1137 * currently there are problems sending an array with a varname
1138 */
381c8e5e 1139function _devel_print_object($obj, $prefix = NULL, $parents = NULL, $object = FALSE) {
3efc2536 1140 static $root_type, $out_format;
83013f8f 1141
e763e01d 1142 // TODO: support objects with references. See http://drupal.org/node/234581.
1143 if (isset($obj->view)) {
8b832e7b 1144 return;
e763e01d 1145 }
83013f8f 1146
3efc2536
JR
1147 if (!isset($root_type)) {
1148 $root_type = gettype($obj);
381c8e5e
JR
1149 if ($root_type == 'object') {
1150 $object = TRUE;
1151 }
3efc2536
JR
1152 }
1153
1154 if (is_object($obj)) {
1155 $obj = (array)$obj;
1156 }
1157 if (is_array($obj)) {
b0771e29 1158 $output = "<dl>\n";
7a2bcf56 1159 foreach ($obj as $field => $value) {
d1e79aee 1160 if ($field == 'devel_flag_reference') {
1161 continue;
1162 }
381c8e5e 1163 if (!is_null($parents)) {
3efc2536
JR
1164 if ($object) {
1165 $field = $parents .'->'. $field;
1166 }
1167 else {
1168 if (is_int($field)) {
1169 $field = $parents .'['. $field .']';
1170 }
1171 else {
1172 $field = $parents .'[\''. $field .'\']';
1173 }
1174 }
1175 }
1176
6ff481db
JR
1177 $type = gettype($value);
1178
1179 $show_summary = TRUE;
124c3715 1180 $summary = NULL;
6ff481db
JR
1181 if ($show_summary) {
1182 switch ($type) {
1183 case 'string' :
1184 case 'float' :
1185 case 'integer' :
1186 if (strlen($value) == 0) {
1187 $summary = t("{empty}");
1188 }
1189 elseif (strlen($value) < 40) {
82a6dbc2 1190 $summary = htmlspecialchars($value);
ff31e645
JR
1191 }
1192 else {
1193 $summary = format_plural(drupal_strlen($value), '1 character', '@count characters');
6ff481db
JR
1194 }
1195 break;
1196 case 'array' :
1197 case 'object' :
ff31e645 1198 $summary = format_plural(count((array)$value), '1 element', '@count elements');
6ff481db
JR
1199 break;
1200 case 'boolean' :
1201 $summary = $value ? t('TRUE') : t('FALSE');
1202 break;
1203 }
1204 }
124c3715 1205 if (!is_null($summary)) {
6ff481db
JR
1206 $typesum = '('. $type .', <em>'. $summary .'</em>)';
1207 }
1208 else {
1209 $typesum = '('. $type .')';
3efc2536
JR
1210 }
1211
1212 $output .= '<span class="devel-attr">';
6ff481db 1213 $output .= "<dt><span class=\"field\">{$prefix}{$field}</span> $typesum</dt>\n";
3efc2536 1214 $output .= "<dd>\n";
d1e79aee 1215 // Check for references.
1216 if (is_array($value) && isset($value['devel_flag_reference'])) {
1217 $value['devel_flag_reference'] = TRUE;
1218 }
1219 // Check for references to prevent errors from recursions.
1220 if (is_array($value) && isset($value['devel_flag_reference']) && !$value['devel_flag_reference']) {
1221 $value['devel_flag_reference'] = FALSE;
1222 $output .= _devel_print_object($value, $prefix, $field);
3efc2536
JR
1223 }
1224 elseif (is_object($value)) {
d1e79aee 1225 $value->devel_flag_reference = FALSE;
3efc2536
JR
1226 $output .= _devel_print_object((array)$value, $prefix, $field, TRUE);
1227 }
1228 else {
1229 $value = is_bool($value) ? ($value ? 'TRUE' : 'FALSE') : $value;
1230 $output .= htmlspecialchars(print_r($value, TRUE)) ."\n";
1231 }
1232 $output .= "</dd></span>\n";
1233 }
1234 $output .= "</dl>\n";
0356e334 1235 }
0356e334
JC
1236 return $output;
1237}
9f6e6334
GK
1238
1239/**
9d450e49 1240 * Adds a table at the bottom of the page cataloguing data on all the database queries that were made to
1241 * generate the page.
9f6e6334 1242 */
9d450e49 1243function devel_query_table($queries, $counts) {
f2a633ed 1244 $version = devel_get_core_version(VERSION);
2b11d164 1245 $header = array ('ms', '#', 'where', 'ops', 'query', 'target');
9d450e49 1246 $i = 0;
2ad63d14 1247 $api = variable_get('devel_api_url', 'api.drupal.org');
9d450e49 1248 foreach ($queries as $query) {
efaea73d 1249 $function = !empty($query['caller']['class']) ? $query['caller']['class'] . '::' : '';
1250 $function .= $query['caller']['function'];
c7414be0 1251 $count = isset($counts[$query['query']]) ? $counts[$query['query']] : 0;
9d450e49 1252
c7414be0 1253 $diff = round($query['time'] * 1000, 2);
9d450e49 1254 if ($diff > variable_get('devel_execution', 5)) {
1255 $cell[$i][] = array ('data' => $diff, 'class' => 'marker');
1256 }
1257 else {
1258 $cell[$i][] = $diff;
1259 }
d7d07506 1260 $cell[$i][] = $count;
2ad63d14 1261 $cell[$i][] = l($function, "http://$api/api/$version/function/$function");
269cf33d 1262 $ops[] = l('P', '', array('attributes' => array('title' => 'Show placeholders', 'class' => 'dev-placeholders', 'qid' => $i)));
1263 $ops[] = l('A', '', array('attributes' => array('title' => 'Show arguments', 'class' => 'dev-arguments', 'qid' => $i)));
32d7f5c7 1264 // EXPLAIN only valid for select queries.
1265 if (strpos($query['query'], 'UPDATE') === FALSE && strpos($query['query'], 'INSERT') === FALSE && strpos($query['query'], 'DELETE') === FALSE) {
1266 $ops[] = l('E', '', array('attributes' => array('title' => 'Show EXPLAIN', 'class' => 'dev-explain', 'qid' => $i)));
1267 }
2b11d164 1268 $cell[$i][] = implode(' ', $ops);
1269 // 3 divs for each variation of the query. Last 2 are hidden by default.
1270 $placeholders = '<div class="dev-placeholders">' . check_plain($query['query']) . "</div>\n";
1271 $args = '<div class="dev-arguments" style="display: none;"></div>' . "\n";
1272 $explain = '<div class="dev-explain" style="display: none;"></div>' . "\n";
269cf33d 1273 $cell[$i][] = array(
1274 'id' => "devel-query-$i",
1275 'data' => $placeholders . $args . $explain,
1276 );
35c0c4c3 1277 $cell[$i][] = $query['target'];
9d450e49 1278 $i++;
2b11d164 1279 unset($diff, $count, $ops);
9d450e49 1280 }
dcd35dd9 1281 if (variable_get('devel_query_sort', DEVEL_QUERY_SORT_BY_SOURCE)) {
1282 usort($cell, '_devel_table_sort');
1283 }
fe87489f 1284 return theme('devel_querylog', array('header' => $header, 'rows' => $cell));
5d9df190 1285}
1286
f01aa6ec
DR
1287function theme_devel_querylog_row($variables) {
1288 $row = $variables['row'];
5d9df190 1289 $i = 0;
1290 $output = '';
1291 foreach ($row as $cell) {
1292 $i++;
1293
1294 if (is_array($cell)) {
1295 $data = !empty($cell['data']) ? $cell['data'] : '';
1296 unset($cell['data']);
1297 $attr = $cell;
1298 }
1299 else {
1300 $data = $cell;
1301 $attr = array();
1302 }
1303
1304 if (!empty($attr['class'])) {
1305 $attr['class'] .= " cell cell-$i";
1306 }
1307 else {
1308 $attr['class'] = "cell cell-$i";
1309 }
1310 $attr = drupal_attributes($attr);
1311
1312 $output .= "<div $attr>$data</div>";
1313 }
1314 return $output;
1315}
1316
fe87489f 1317function theme_devel_querylog($variables) {
1318 $header = $variables['header'];
1319 $rows = $variables['rows'];
5d9df190 1320 $output = '';
1321 if (!empty($header)) {
1322 $output .= "<div class='devel-querylog devel-querylog-header clear-block'>";
f01aa6ec 1323 $output .= theme('devel_querylog_row', array('row' => $header));
5d9df190 1324 $output .= "</div>";
1325 }
1326 if (!empty($rows)) {
1327 $i = 0;
1328 foreach ($rows as $row) {
1329 $i++;
1330 $zebra = ($i % 2) == 0 ? 'even' : 'odd';
1331 $output .= "<div class='devel-querylog devel-querylog-$zebra clear-block'>";
f01aa6ec 1332 $output .= theme('devel_querylog_row', array('row' => $row));
5d9df190 1333 $output .= "</div>";
1334 }
1335 }
1336 return $output;
9f6e6334
GK
1337}
1338
dcd35dd9 1339function _devel_table_sort($a, $b) {
1234041c
YC
1340 $a = is_array($a[0]) ? $a[0]['data'] : $a[0];
1341 $b = is_array($b[0]) ? $b[0]['data'] : $b[0];
d5e87c9b 1342 if ($a < $b) {
7a2bcf56 1343 return 1;
1344 }
d5e87c9b 1345 if ($a > $b) {
7a2bcf56 1346 return -1;
1347 }
1234041c 1348 return 0;
dcd35dd9 1349}
1350
9f6e6334 1351/**
9d450e49 1352 * Displays page execution time at the bottom of the page.
9f6e6334 1353 */
9d450e49 1354function devel_timer() {
1355 $time = timer_read('page');
683a91e6 1356 return t_safe(' Page execution time was @time ms.', array('@time' => $time));
9d450e49 1357}
1358
8637423e 1359// An alias for drupal_debug().
1360function dd($data, $label = NULL) {
1361 return drupal_debug($data, $label);
1362}
1363
1364// Log any variable to a drupal_debug.log in the site's temp directory.
1365// See http://drupal.org/node/314112
1366function drupal_debug($data, $label = NULL) {
1367 ob_start();
1368 print_r($data);
1369 $string = ob_get_clean();
1370 if ($label) {
7a2bcf56 1371 $out = $label .': '. $string;
8637423e 1372 }
1373 else {
1374 $out = $string;
1375 }
1376 $out .= "\n";
8b832e7b 1377
8637423e 1378 // The temp directory does vary across multiple simpletest instances.
81e04679 1379 $file = file_directory_path('temporary') . '/drupal_debug.txt';
8637423e 1380 if (file_put_contents($file, $out, FILE_APPEND) === FALSE) {
1381 drupal_set_message(t('The file could not be written.'), 'error');
1382 return FALSE;
1383 }
1384}
1385
9d450e49 1386/**
976f7b43 1387 * Prints the arguments passed into the current function
9d450e49 1388 */
8e015c96 1389function dargs($always = TRUE) {
1390 static $printed;
1391 if ($always || !$printed) {
1392 $bt = debug_backtrace();
976f7b43 1393 print kdevel_print_object($bt[1]['args']);
8e015c96 1394 $printed = TRUE;
1395 }
1234041c 1396}
8e015c96 1397
1398/**
1399 * Print a variable to the 'message' area of the page. Uses drupal_set_message()
1400 */
eb8f82fc 1401function dpm($input, $name = NULL) {
9d450e49 1402 if (user_access('access devel information')) {
83fa67d2 1403 $export = kprint_r($input, TRUE, $name);
8e015c96 1404 drupal_set_message($export);
9d450e49 1405 }
1406}
1407
1408/**
cc43a9ce 1409 * Var_dump() a variable to the 'message' area of the page. Uses drupal_set_message()
1410 */
eb8f82fc 1411function dvm($input, $name = NULL) {
cc43a9ce 1412 if (user_access('access devel information')) {
6115f4e2 1413 $export = dprint_r($input, TRUE, $name, 'var_dump', FALSE);
cc43a9ce 1414 drupal_set_message($export);
1415 }
1416}
eb8f82fc 1417
1418// legacy function that was poorly named. use dpm() instead, since the 'p' maps to 'print_r'
1419function dsm($input, $name = NULL) {
e8432427 1420 dpm($input, $name);
eb8f82fc 1421}
1422
cc43a9ce 1423/**
8e015c96 1424 * An alias for dprint_r(). Saves carpal tunnel syndrome.
1425 */
cc43a9ce 1426function dpr($input, $return = FALSE, $name = NULL) {
773d198e 1427 return dprint_r($input, $return, $name);
1428}
1429
1430/**
1431 * An alias for kprint_r(). Saves carpal tunnel syndrome.
1432 */
1433function kpr($input, $return = FALSE, $name = NULL) {
83fa67d2 1434 return kprint_r($input, $return, $name);
8e015c96 1435}
1436
1437/**
cc43a9ce 1438 * Like dpr, but uses var_dump() instead
1439 */
1440function dvr($input, $return = FALSE, $name = NULL) {
6115f4e2 1441 return dprint_r($input, $return, $name, 'var_dump', FALSE);
cc43a9ce 1442}
1443
00399d8f 1444function kprint_r($input, $return = FALSE, $name = NULL, $function = 'print_r') {
47257719 1445 // We do not want to krumo() strings and integers and such
1446 if (merits_krumo($input)) {
00399d8f 1447 if (user_access('access devel information')) {
0acb632a 1448 return $return ? (isset($name) ? $name .' => ' : '') . krumo_ob($input) : krumo($input);
00399d8f 1449 }
1450 }
1451 else {
412e2c0d 1452 return dprint_r($input, $return, $name, $function);
00399d8f 1453 }
1454}
1455
cc43a9ce 1456/**
773d198e 1457 * Pretty-print a variable to the browser (no krumo).
9d450e49 1458 * Displays only for users with proper permissions. If
8e015c96 1459 * you want a string returned instead of a print, use the 2nd param.
9d450e49 1460 */
6115f4e2 1461function dprint_r($input, $return = FALSE, $name = NULL, $function = 'print_r', $check= TRUE) {
9d450e49 1462 if (user_access('access devel information')) {
1234041c
YC
1463 if ($name) {
1464 $name .= ' => ';
1465 }
1466 ob_start();
1467 $function($input);
6115f4e2 1468 $output = ob_get_clean();
1469 if ($check) {
1470 $output = check_plain($output);
1471 }
eb8f82fc 1472 if (count($input, COUNT_RECURSIVE) > DEVEL_MIN_TEXTAREA) {
00399d8f 1473 // don't use fapi here because sometimes fapi will not be loaded
7a2bcf56 1474 $printed_value = "<textarea rows=30 style=\"width: 100%;\">\n". $name . $output .'</textarea>';
eb8f82fc 1475 }
1234041c 1476 else {
7a2bcf56 1477 $printed_value = '<pre>'. $name . $output .'</pre>';
1234041c 1478 }
8b832e7b 1479
9d450e49 1480 if ($return) {
3d5b4cb9 1481 return $printed_value;
1234041c 1482 }
cc43a9ce 1483 else {
3d5b4cb9 1484 print $printed_value;
9d450e49 1485 }
1486 }
1487}
1488
1489/**
0f79f53f 1490 * Print the function call stack.
9d450e49 1491 */
1492function ddebug_backtrace() {
1493 if (user_access('access devel information')) {
7c412e59 1494 $trace = debug_backtrace();
1495 array_shift($trace);
12ee375b 1496 foreach ($trace as $key => $value) {
1497 $rich_trace[$value['function']] = $value;
1498 }
7c412e59 1499 if (has_krumo()) {
12ee375b 1500 print krumo($rich_trace);
9d450e49 1501 }
1502 else {
12ee375b 1503 dprint_r($rich_trace);
9d450e49 1504 }
1505 }
9f6e6334 1506}
61d85437 1507
2b11d164 1508// Delete all files in a dir. http://www.plus2net.com/php_tutorial/php-files-delete.php
1509function devel_empty_dir($dir) {
a1cc25d1 1510 foreach (new DirectoryIterator($dir) as $fileInfo) {
1511 unlink($fileInfo->getPathname());
2b11d164 1512 }
2b11d164 1513}
1514
b2de72f1 1515/*
1516 * migration related functions
1517 */
a30583d1 1518
b2de72f1 1519
1520/**
86f98486 1521 * Update node_comment_statistics table for nodes with comments.
b2de72f1 1522 * TODO: if 2 comments have exact same timestamp, the function can get wrong uid and name fields.
1523 * Handles when comment timestamps have been manually set in admin
a30583d1 1524 *
b2de72f1 1525 * @return void
a30583d1 1526 **/
b2de72f1 1527function devel_rebuild_node_comment_statistics() {
092ef28f 1528 // Empty table
5b73e5e4 1529 db_delete('node_comment_statistics');
83013f8f 1530
5b73e5e4 1531 $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)";
b2de72f1 1532 db_query($sql, COMMENT_PUBLISHED);
8b832e7b 1533
092ef28f 1534 // Insert 0 count records into the node_comment_statistics for nodes that are missing. See comment_enable()
1535 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');
1536 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");
8a210f69 1537}