[ Index ]

PHP Cross Reference of Drupal 6 (gatewave)

title

Body

[close]

/sites/all/modules/devel/ -> devel.module (source)

   1  <?php
   2  // $Id: devel.module,v 1.258.2.87 2010/12/01 23:42:15 salvis Exp $
   3  
   4  // This module holds functions useful for Drupal development.
   5  // Please contribute!
   6  
   7  // Suggested profiling and stacktrace library from http://www.xdebug.org/index.php
   8  
   9  define('DEVEL_QUERY_SORT_BY_SOURCE', 0);
  10  define('DEVEL_QUERY_SORT_BY_DURATION', 1);
  11  
  12  define('DEVEL_ERROR_HANDLER_NONE', 0);
  13  define('DEVEL_ERROR_HANDLER_STANDARD', 1);
  14  define('DEVEL_ERROR_HANDLER_BACKTRACE', 2);
  15  
  16  define('DEVEL_MIN_TEXTAREA', 50);
  17  
  18  /**
  19   * Implementation of hook_help().
  20   */
  21  function devel_help($section) {
  22    switch ($section) {
  23      case 'devel/reference':
  24        return '<p>'. t('This is a list of defined user functions that generated this current request lifecycle. Click on a function name to view its documention.') .'</p>';
  25      case 'devel/session':
  26        return '<p>'. t('Here are the contents of your <code>$_SESSION</code> variable.') .'</p>';
  27      case 'devel/variable':
  28        $api = variable_get('devel_api_url', 'api.drupal.org');
  29        return '<p>'. t('This is a list of the variables and their values currently stored in variables table and the <code>$conf</code> array of your settings.php file. These variables are usually accessed with <a href="@variable-get-doc">variable_get()</a> and <a href="@variable-set-doc">variable_set()</a>. Variables that are too long can slow down your pages.', array('@variable-get-doc' => "http://$api/api/HEAD/function/variable_get", '@variable-set-doc' => "http://$api/api/HEAD/function/variable_set")) .'</p>';
  30    }
  31  }
  32  
  33  /**
  34   * Implementation of hook_menu().
  35   */
  36  function devel_menu() {
  37    $items = array();
  38    // note: we can't dynamically append destination to querystring. do so at theme layer. fix in D7?
  39    $items['devel/cache/clear'] = array(
  40      'title' => 'Empty cache',
  41      'page callback' => 'devel_cache_clear',
  42      'description' => 'Clear the CSS cache and all database cache tables which store page, node, theme and variable caches.',
  43      'access arguments' => array('access devel information'),
  44      'menu_name' => 'devel',
  45    );
  46  
  47    $items['devel/queries'] = array(
  48      'title' => 'Database queries',
  49      'page callback' => 'devel_queries',
  50      'access callback' => 'devel_menu_access_store_queries',
  51      'access arguments' => array(),
  52      'menu_name' => 'devel',
  53    );
  54    $items['devel/queries/empty'] = array(
  55      'title' => 'Empty database queries',
  56      'page callback' => 'devel_queries_empty',
  57      'access callback' => 'devel_menu_access_store_queries',
  58      'access arguments' => array(),
  59      'menu_name' => 'devel',
  60    );
  61    $items['devel/reference'] = array(
  62      'title' => 'Function reference',
  63      'description' => 'View a list of currently defined user functions with documentation links.',
  64      'page callback' => 'devel_function_reference',
  65      'access arguments' => array('access devel information'),
  66      'menu_name' => 'devel',
  67    );
  68    $items['devel/reinstall'] = array(
  69      'title' => 'Reinstall modules',
  70      'page callback' => 'drupal_get_form',
  71      'page arguments' => array('devel_reinstall'),
  72      'description' => 'Run hook_uninstall() and then hook_install() for a given module.',
  73      'access arguments' => array('access devel information'),
  74      'menu_name' => 'devel',
  75    );
  76    $items['devel/source'] = array(
  77      'title' => 'Display the PHP code of any file in your Drupal installation',
  78      'page callback' => 'devel_display_source',
  79      'access arguments' => array('display source code'),
  80      'type' => MENU_CALLBACK,
  81      'menu_name' => 'devel',
  82    );
  83    $items['devel/menu/reset'] = array(
  84      'title' => 'Rebuild menus',
  85      'description' => 'Rebuild menu based on hook_menu() and revert any custom changes. All menu items return to their default settings.',
  86      'page callback' => 'drupal_get_form',
  87      'page arguments' => array('devel_menu_rebuild'),
  88      'access arguments' => array('access devel information'),
  89      'menu_name' => 'devel',
  90    );
  91    $items['devel/variable'] = array(
  92      'title' => 'Variable editor',
  93      'description' => 'Edit and delete site variables.',
  94      'page callback' => 'devel_variable_page',
  95      'access arguments' => array('access devel information'),
  96      'menu_name' => 'devel',
  97    );
  98    // we don't want the abbreviated version provided by status report
  99    $items['devel/phpinfo'] = array(
 100      'title' => 'PHPinfo()',
 101      'description' => 'View your server\'s PHP configuration',
 102      'page callback' => 'devel_phpinfo',
 103      'access arguments' => array('access devel information'),
 104      'menu_name' => 'devel',
 105    );
 106    $items['devel/php'] = array(
 107      'title' => 'Execute PHP Code',
 108      'description' => 'Execute some PHP code',
 109      'page callback' => 'drupal_get_form',
 110      'page arguments' => array('devel_execute_form'),
 111      'access arguments' => array('execute php code'),
 112      'menu_name' => 'devel',
 113    );
 114    $items['devel/elements'] = array(
 115      'title' => 'Hook_elements()',
 116      'description' => 'View the active form/render elements for this site.',
 117      'page callback' => 'devel_elements_page',
 118      'access arguments' => array('access devel information'),
 119      'menu_name' => 'devel',
 120    );
 121    $items['devel/variable/edit/%'] = array(
 122      'title' => 'Variable editor',
 123      'page callback' => 'drupal_get_form',
 124      'page arguments' => array('devel_variable_edit', 3),
 125      'access arguments' => array('access devel information'),
 126      'type' => MENU_CALLBACK,
 127      'menu_name' => 'devel',
 128    );
 129    $items['devel/session'] = array(
 130      'title' => 'Session viewer',
 131      'description' => 'List the contents of $_SESSION.',
 132      'page callback' => 'devel_session',
 133      'access arguments' => array('access devel information'),
 134      'menu_name' => 'devel',
 135    );
 136    $items['devel/switch'] = array(
 137      'title' => 'Switch user',
 138      'page callback' => 'devel_switch_user',
 139      'access arguments' => array('switch users'),
 140      'type' => MENU_CALLBACK,
 141      'menu_name' => 'devel',
 142    );
 143    $items['admin/settings/devel'] = array(
 144      'title' => 'Devel settings',
 145      'description' => 'Helper functions, pages, and blocks to assist Drupal developers. The devel blocks can be managed via the <a href="/admin/build/block">block administration</a> page.',
 146      'page callback' => 'drupal_get_form',
 147      'page arguments' => array('devel_admin_settings'),
 148      'access arguments' => array('administer site configuration'),
 149      'menu_name' => 'devel',
 150    );
 151    $items['node/%node/devel'] = array(
 152      'title' => 'Devel',
 153      'page callback' => 'devel_load_object',
 154      'page arguments' => array(1, 'node'),
 155      'access callback' => 'user_access',
 156      'access arguments' => array('access devel information'),
 157      'type' => MENU_LOCAL_TASK,
 158      'weight' => 100,
 159    );
 160    $items['node/%node/devel/load'] = array(
 161      'title' => 'Dev load',
 162      'page callback' => 'devel_load_object',
 163      'page arguments' => array(1, 'node'),
 164      'access callback' => 'user_access',
 165      'access arguments' => array('access devel information'),
 166      'type' => MENU_DEFAULT_LOCAL_TASK,
 167      'weight' => 0,
 168    );
 169    $items['node/%node/devel/render'] = array(
 170      'title' => 'Dev render',
 171      'page callback' => 'devel_render_object',
 172      'page arguments' => array('node', 1),
 173      'access callback' => 'user_access',
 174      'access arguments' => array('access devel information'),
 175      'type' => MENU_LOCAL_TASK,
 176      'weight' => 10,
 177    );
 178    $items['user/%user/devel'] = array(
 179      'title' => 'Devel',
 180      'page callback' => 'devel_load_object',
 181      'page arguments' => array(1, 'user'),
 182      'access callback' => 'user_access',
 183      'access arguments' => array('access devel information'),
 184      'type' => MENU_LOCAL_TASK,
 185      'weight' => 100,
 186    );
 187    $items['user/%user/devel/load'] = array(
 188      'title' => 'Dev load',
 189      'page callback' => 'devel_load_object',
 190      'page arguments' => array(1, 'user'),
 191      'access callback' => 'user_access',
 192      'access arguments' => array('access devel information'),
 193      'type' => MENU_DEFAULT_LOCAL_TASK,
 194      'weight' => 0,
 195    );
 196    $items['user/%user/devel/render'] = array(
 197      'title' => 'Dev render',
 198      'page callback' => 'devel_render_object',
 199      'page arguments' => array('user', 1),
 200      'access callback' => 'user_access',
 201      'access arguments' => array('access devel information'),
 202      'type' => MENU_LOCAL_TASK,
 203      'weight' => 10,
 204    );
 205    $items['devel/theme/registry'] = array(
 206      'title' => 'Theme registry',
 207      'description' => 'View a list of available theme functions across the whole site.',
 208      'page callback' => 'devel_theme_registry',
 209      'access arguments' => array('access devel information'),
 210      'menu_name' => 'devel',
 211    );
 212  
 213    return $items;
 214  }
 215  
 216  function devel_menu_need_destination() {
 217    return array('devel/cache/clear', 'devel/reinstall', 'devel/menu/reset', 'admin/og/og', 'devel/variable', 'admin/reports/status/run-cron');
 218  }
 219  
 220  /**
 221   * An implementation of hook_menu_link_alter(). Flag this link as needing alter at display time.
 222   * This is more robust that setting alter in hook_menu(). See devel_translated_menu_link_alter().
 223   *
 224   **/
 225  function devel_menu_link_alter(&$item, $menu) {
 226    if (in_array($item['link_path'], devel_menu_need_destination())) {
 227      $item['options']['alter'] = TRUE;
 228    }
 229  }
 230  
 231  /**
 232   * An implementation of hook_translated_menu_item_alter(). Append dynamic
 233   * querystring 'destination' to several of our own menu items.
 234   *
 235   **/
 236  function devel_translated_menu_link_alter(&$item) {
 237    if (in_array($item['href'], devel_menu_need_destination())) {
 238      $item['localized_options']['query'] = drupal_get_destination();
 239    }
 240  }
 241  
 242  function devel_menu_access_store_queries() {
 243    return user_access('access devel information') && variable_get('devel_store_queries', 0);
 244  }
 245  
 246  /**
 247   * Implementation of hook_theme()
 248   */
 249  function devel_theme() { // &$cache, $type, $theme, $path
 250    return array(
 251      'devel_variable_form' => array(
 252          'arguments' => array('form' => NULL)
 253      ),
 254      'devel_querylog' => array(
 255        'arguments' => array('header' => array(), 'rows' => array()),
 256      ),
 257      'devel_querylog_row' => array(
 258        'arguments' => array('row' => array()),
 259      ),
 260    );
 261  }
 262  
 263  /**
 264   * Page callback to display syntax hilighted source code
 265   *
 266   * note: the path for this function is received via $_GET['path']
 267   *  example http://www.example.com/devel/source?file=modules/node/node.module
 268   *
 269   * @param $standalone
 270   *   Set to FALSE to place the code inside a Drupal page. Otherwise code displays on its own.
 271   */
 272  function devel_display_source($standalone = TRUE) {
 273    $path = $_GET['file'];
 274    // take out the nasties
 275    $path = str_replace('../', '', $path);
 276    $output = devel_highlight_file($path, $standalone);
 277    if ($output) {
 278      if ($standalone) {
 279        print $output;
 280        exit();
 281      }
 282      return $output;
 283    }
 284    else {
 285      drupal_set_message(t('Invalid file path'), 'error');
 286      drupal_not_found();
 287    }
 288  }
 289  
 290  /**
 291   * Return PHP highlighted file
 292   *
 293   * @param $path
 294   *   path to the file
 295   *    *warning* there is NO VALIDATION in this function
 296   *    Beware of paths such as '../../../../../etc/apache/httpd.conf'
 297   *
 298   * @param $standalone
 299   *   should the returned HTML be wrapped in a full <html> page or will it be output by Drupal?
 300   */
 301  function devel_highlight_file($path = NULL, $standalone = FALSE) {
 302    if (file_exists($path)) {
 303      $source = highlight_file($path, TRUE);
 304      // add anchor links before all functions so that we can link to a function within the source
 305      // ** commented out because regexes aren't working **
 306      //$source = preg_replace('!(\/\*\*.*?\*\/.*?)<br.*?function.*?#0000BB">(.*?)<\/span>!', '<a id="$2"></a> $0', $source);
 307      //$source = preg_replace('!(\/\*\*.*?\*\/).*?function.*?#0000BB">(.*?)<\/span>!', '<a id="$2"></a> $0', $source);
 308      if ($standalone) {
 309        $source = <<<EOT
 310  <head><title>$path</title></head>
 311  <body>$source</body>
 312  EOT;
 313      }
 314      return $source;
 315    }
 316    else {
 317      return FALSE;
 318    }
 319  }
 320  
 321  /**
 322   * Implementation of hook_init().
 323   */
 324  function devel_init() {
 325    if (!devel_silent()) {
 326      if (user_access('access devel information')) {
 327        devel_set_handler(variable_get('devel_error_handler', DEVEL_ERROR_HANDLER_STANDARD));
 328        // We want to include the class early so that anyone may call krumo() as needed. See http://krumo.sourceforge.net/
 329        has_krumo();
 330  
 331        // See http://www.firephp.org/.
 332        // Support Libraries API - http://drupal.org/project/libraries
 333        if (module_exists('libraries')) {
 334          $firephp_path = libraries_get_path('FirePHPCore') . '/lib/FirePHPCore/';
 335          $chromephp_path = libraries_get_path('chromephp');
 336        }
 337        else {
 338          $firephp_path = './'. drupal_get_path('module', 'devel') .'/FirePHPCore/lib/FirePHPCore/';
 339          $chromephp_path = './' . drupal_get_path('module', 'devel') .'/chromephp';
 340        }
 341        // include FirePHP if exists...
 342        if (file_exists($firephp_path .'fb.php')) {
 343          include_once $firephp_path .'fb.php';
 344          include_once $firephp_path .'FirePHP.class.php';
 345        }
 346  
 347        // include ChromePHP if exists...
 348          if (file_exists($chromephp_path . '/ChromePhp.php')) {
 349            include_once $chromephp_path . '/ChromePhp.php';
 350          }
 351  
 352        // Add CSS for query log if should be displayed.
 353        if (variable_get('devel_query_display', 0)) {
 354          drupal_add_css(drupal_get_path('module', 'devel') .'/devel.css');
 355        }
 356      }
 357    }
 358    if (variable_get('devel_rebuild_theme_registry', FALSE)) {
 359      drupal_rebuild_theme_registry();
 360      if (flood_is_allowed('devel_rebuild_registry_warning', 1)) {
 361        flood_register_event('devel_rebuild_registry_warning');
 362        if (!devel_silent() && user_access('access devel information')) {
 363          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/settings/devel'))));
 364        }
 365      }
 366    }
 367  }
 368  
 369  // return boolean. no need for cache here.
 370  function has_krumo() {
 371    // see README.txt or just download from http://krumo.sourceforge.net/
 372    @include_once './'. drupal_get_path('module', 'devel') .'/krumo/class.krumo.php';
 373    if (function_exists('krumo') && php_sapi_name() != 'cli') {
 374      return TRUE;
 375    }
 376    else {
 377      return FALSE;
 378    }
 379  }
 380  
 381  /**
 382   * Decide whether or not to print a debug variable using krumo().
 383   *
 384   * @param $input
 385   * @return boolean
 386   */
 387  function merits_krumo($input) {
 388    return (is_object($input) || is_array($input)) && has_krumo() && variable_get('devel_krumo_skin', '') != 'disabled';
 389  }
 390  
 391  /**
 392   * Calls the http://www.firephp.org/ fb() function if it is found.
 393   *
 394   * @return void
 395   */
 396  function dfb() {
 397    if (function_exists('fb') && user_access('access devel information')) {
 398      $args = func_get_args();
 399      call_user_func_array('fb', $args);
 400    }
 401  }
 402  
 403  /**
 404   * Calls dfb() to output a backtrace.
 405   */
 406  function dfbt($label) {
 407    dfb($label, FirePHP::TRACE);
 408  }
 409  
 410  function dcp() {
 411    if (class_exists('ChromePhp') && user_access('access devel information')) {
 412      $args = func_get_args();
 413      call_user_func_array(array('ChromePhp', 'log'), $args);
 414    }
 415  }
 416  
 417  /**
 418   * Implements hook_watchdog().
 419   */
 420  function devel_watchdog($log_entry) {
 421    if (class_exists('FirePHP')) {
 422      switch ($log_entry['severity']) {
 423        case WATCHDOG_EMERG:
 424        case WATCHDOG_ALERT:
 425        case WATCHDOG_CRITICAL:
 426        case WATCHDOG_ERROR:
 427          $type = FirePHP::ERROR;
 428          break;
 429        case WATCHDOG_WARNING:
 430          $type = FirePHP::WARN;
 431          break;
 432        case WATCHDOG_NOTICE:
 433        case WATCHDOG_INFO:
 434          $type = FirePHP::INFO;
 435          break;
 436        case WATCHDOG_DEBUG:
 437        DEFAULT:
 438          $type = FirePHP::LOG;
 439      }
 440    }
 441    else {
 442      $type = 'watchdog';
 443    }
 444    $watchdog = array(
 445      'type' => $log_entry['type'],
 446      'message' => decode_entities(strtr($log_entry['message'], (array)$log_entry['variables'])),
 447    );
 448    if (isset($log_entry['link'])) {
 449      $watchdog['link'] = $log_entry['link'];
 450    }
 451    dfb($watchdog, $type);
 452  }
 453  
 454  function devel_set_handler($handler) {
 455    switch ($handler) {
 456      case DEVEL_ERROR_HANDLER_STANDARD:
 457        // do nothing
 458        break;
 459      case DEVEL_ERROR_HANDLER_BACKTRACE:
 460        if (has_krumo()) {
 461          set_error_handler('backtrace_error_handler');
 462        }
 463        break;
 464      case DEVEL_ERROR_HANDLER_NONE:
 465        restore_error_handler();
 466        break;
 467    }
 468  }
 469  
 470  
 471  function devel_silent() {
 472    // isset($_GET['q']) is needed on IIS when calling the front page. q is not set.
 473    // Don't interfere with private files/images.
 474    return
 475      devel_verify_cli() ||
 476      isset($GLOBALS['devel_shutdown']) ||
 477      strstr($_SERVER['PHP_SELF'], 'update.php') ||
 478      (isset($_GET['q']) && (
 479        in_array($_GET['q'], array('upload/js', 'admin/content/node-settings/rebuild')) ||
 480        substr($_GET['q'], 0, strlen('system/files')) == 'system/files' ||
 481        substr($_GET['q'], 0, strlen('batch')) == 'batch')
 482      );
 483  }
 484  
 485  function devel_xhprof_enable() {
 486    if (extension_loaded('xhprof') && variable_get('devel_xhprof_enabled', FALSE)) {
 487      if ($path = variable_get('devel_xhprof_directory', '')) {
 488        include_once $path . '/xhprof_lib/utils/xhprof_lib.php';
 489        include_once $path . '/xhprof_lib/utils/xhprof_runs.php';
 490        // @todo: consider a variable per-flag instead.
 491        xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY);
 492  
 493        register_shutdown_function('devel_shutdown_xhprof_drush');
 494      }
 495    }
 496  }
 497  
 498  
 499  /**
 500   * Implementation of hook_boot(). Runs even for cached pages.
 501   */
 502  function devel_boot() {
 503    // Initialize XHProf.
 504    devel_xhprof_enable();
 505  
 506    if (!devel_silent()) {
 507      devel_start();
 508    }
 509  }
 510  
 511  // kickoff all our tricks. put here all code which must run for ached pages too. called from both hook_boot and hook_init.
 512  function devel_start() {
 513    if (variable_get('dev_mem', 0) && function_exists('memory_get_usage')) {
 514      global $memory_init;
 515      $memory_init = memory_get_usage();
 516    }
 517  
 518    // we need user_access() in the shutdown function. make sure it gets loaded
 519    drupal_load('module', 'user');
 520    register_shutdown_function('devel_shutdown');
 521  }
 522  
 523  function backtrace_error_handler($errno, $message, $filename, $line) {
 524    // Don't respond to the error if it was suppressed with a '@'
 525    if (error_reporting() == 0) {
 526      return;
 527    }
 528    if ($errno & (E_ALL ^ E_DEPRECATED)) {
 529      // We can't use the PHP E_* constants here as not all versions of PHP have all
 530      // the constants defined, so for consistency, we just use the numeric equivelant.
 531      $types = array(
 532        1 => 'error',
 533        2 => 'warning',
 534        4 => 'parse error',
 535        8 => 'notice',
 536        16 => 'core error',
 537        32 => 'core warning',
 538        64 => 'compile error',
 539        128 => 'compile warning',
 540        256 => 'user error',
 541        512 => 'user warning',
 542        1024 => 'user notice',
 543        2048 => 'strict warning',
 544        4096 => 'recoverable error',
 545        8192 => 'deprecated',
 546        16384 => 'user deprecated',
 547      );
 548      $type = $types[$errno];
 549      $backtrace = debug_backtrace();
 550      array_shift($backtrace);
 551      $variables = array('%error' => $type, '%message' => $message, '%function' => $backtrace[0]['function'] .'()', '%file' => $filename, '%line' => $line);
 552      $counter = 0;
 553  
 554      if (variable_get('error_level', 1) == 1) {
 555        foreach ($backtrace as $call) {
 556          $nicetrace[$call['function'] .'<span class="'. $counter++ .'" />'] = $call;
 557        }
 558        print t('%error: %message in %function (line %line of %file).', $variables) ." =&gt;\n";
 559        krumo($nicetrace);
 560      }
 561  
 562      watchdog('php', '%error: %message in %function (line %line of %file).', $variables, WATCHDOG_ERROR);
 563    }
 564  }
 565  
 566  /**
 567   * Implementation of hook_perm().
 568   */
 569  function devel_perm() {
 570    return array('access devel information', 'execute php code', 'switch users', 'display source code');
 571  }
 572  
 573  /**
 574   * Implementation of hook_block().
 575   */
 576  function devel_block($op = 'list', $delta = 0, $edit = array()) {
 577    if ($op == 'list') {
 578      $blocks[0]['info'] = t('Switch user');
 579  //     $blocks[1]['info'] = t('Devel');
 580      $blocks[2] = array(
 581        'info' => t('Execute PHP'),
 582        'visibility' => 0,
 583        'pages' => 'devel/php',
 584      );
 585      return $blocks;
 586    }
 587    elseif ($op == 'configure' && $delta == 0) {
 588      $form['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),
 592        '#size' => '3',
 593        '#maxlength' => '4',
 594      );
 595      $form['show_form'] = array(
 596        '#type' => 'checkbox',
 597        '#title' => t('Allow entering any user name'),
 598        '#default_value' => variable_get('devel_switch_user_show_form', TRUE),
 599      );
 600      return $form;
 601    }
 602    elseif ($op == 'save' && $delta == 0) {
 603      variable_set('devel_switch_user_list_size', $edit['list_size']);
 604      variable_set('devel_switch_user_show_form', $edit['show_form']);
 605    }
 606    elseif ($op == 'view') {
 607      $block = array();
 608      switch ($delta) {
 609        case 0:
 610          $block = devel_block_switch_user();
 611          break;
 612        case 1:
 613          // Deleted in favor of custom menu. do not reuse this index.
 614          break;
 615        case 2:
 616          if (user_access('execute php code')) {
 617            $block['content'] = drupal_get_form('devel_execute_block_form');
 618          }
 619          break;
 620      }
 621      return $block;
 622    }
 623  }
 624  
 625  function devel_block_switch_user() {
 626    $links = devel_switch_user_list();
 627    if (!empty($links) || user_access('switch users')) {
 628      $block['subject'] = t('Switch user');
 629      $block['content'] = theme('links', $links);
 630      if (variable_get('devel_switch_user_show_form', TRUE)) {
 631        $block['content'] .= drupal_get_form('devel_switch_user_form');
 632      }
 633      return $block;
 634    }
 635  }
 636  
 637  function devel_switch_user_list() {
 638    $links = array();
 639    if (user_access('switch users')) {
 640      $list_size = variable_get('devel_switch_user_list_size', 10);
 641      $dest = drupal_get_destination();
 642      // Try to find at least $list_size users that can switch.
 643      // Inactive users are omitted from all of the following db selects.
 644      $roles = user_roles(TRUE, 'switch users');
 645      if (isset($roles[DRUPAL_AUTHENTICATED_RID])) {
 646        // If authenticated users have this permission, just grab
 647        // the last $list_size users, since there won't be records in
 648        // {user_roles} and every user on the system can switch.
 649        $accounts = db_query_range("SELECT DISTINCT u.uid, u.name, u.access FROM {users} u WHERE u.uid > 0 AND u.status > 0 ORDER BY u.access DESC", 0, $list_size);
 650      }
 651      else {
 652        $where = array('u.uid = 1');
 653        if (count($roles)) {
 654          $where[] = 'r.rid IN ('. implode(',', array_keys($roles)) .')';
 655        }
 656        $where_sql = implode(' OR ', $where);
 657        $accounts = 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) AND u.status > 0 ORDER BY u.access DESC", 0, $list_size);
 658      }
 659      while ($account = db_fetch_object($accounts)) {
 660        $links[$account->uid] = array(
 661          'title' => theme('placeholder', $account->name),
 662          'href' => 'devel/switch/'. $account->name,
 663          'query' => $dest,
 664          'attributes' => array('title' => t('This user can switch back.')),
 665          'html' => TRUE,
 666          'last_access' => $account->access,
 667        );
 668      }
 669      $num_links = count($links);
 670      if ($num_links < $list_size) {
 671        // If we don't have enough, add distinct uids until we hit $list_size.
 672        $accounts = db_query_range('SELECT uid, name, access FROM {users} WHERE uid > 0 AND uid NOT IN ('. implode(',', array_keys($links)) .') AND status > 0 ORDER BY access DESC', 0, $list_size - $num_links);
 673        while ($account = db_fetch_object($accounts)) {
 674          $links[$account->uid] = array(
 675            'title' => $account->name ? $account->name : 'anon',
 676            'href' => 'devel/switch/'. $account->name,
 677            'query' => $dest,
 678            'attributes' => array('title' => t('Caution: this user will be unable to switch back.')),
 679            'last_access' => $account->access,
 680          );
 681        }
 682        uasort($links, '_devel_switch_user_list_cmp');
 683      }
 684    }
 685    return $links;
 686  }
 687  
 688  /**
 689   * Comparison helper function for uasort() in devel_switch_user_list().
 690   *
 691   * Sorts the Switch User links by the user's last access timestamp.
 692   */
 693  function _devel_switch_user_list_cmp($a, $b) {
 694    return $b['last_access'] - $a['last_access'];
 695  }
 696  
 697  function devel_phpinfo() {
 698    print phpinfo();
 699    exit;
 700  }
 701  
 702  function devel_switch_user_form() {
 703    $form['username'] = array(
 704      '#type' => 'textfield',
 705      '#description' => t('Enter username'),
 706      '#autocomplete_path' => 'user/autocomplete',
 707      '#maxlength' => USERNAME_MAX_LENGTH,
 708      '#size' => 16,
 709    );
 710    $form['submit'] = array(
 711      '#type' => 'submit',
 712      '#value' => t('Switch'),
 713    );
 714    return $form;
 715  
 716  }
 717  
 718  function devel_doc_function_form() {
 719    $version = devel_get_core_version(VERSION);
 720    $form['function'] = array(
 721      '#type' => 'textfield',
 722      '#description' => t('Enter function name for api lookup'),
 723      '#size' => 16,
 724      '#maxlength' => 255,
 725    );
 726    $form['version'] = array('#type' => 'value', '#value' => $version);
 727    $form['submit_button'] = array(
 728      '#type' => 'submit',
 729      '#value' => t('Submit'),
 730    );
 731    return $form;
 732  }
 733  
 734  function devel_doc_function_form_submit($form, &$form_state) {
 735    $version = $form_state['values']['version'];
 736    $function = $form_state['values']['function'];
 737    $api = variable_get('devel_api_url', 'api.drupal.org');
 738    $form_state['redirect'] =   "http://$api/api/function/$function/$version";
 739  }
 740  
 741  function devel_switch_user_form_validate($form, &$form_state) {
 742    if (!$account = user_load(array('name' => $form_state['values']['username']))) {
 743      form_set_error('username', t('Username not found'));
 744    }
 745  }
 746  
 747  function devel_switch_user_form_submit($form, &$form_state) {
 748    $form_state['redirect'] = 'devel/switch/'. $form_state['values']['username'];
 749  }
 750  
 751  function devel_exit($destination = NULL) {
 752    global $user;
 753  
 754    if (isset($destination) && !devel_silent()) {
 755      // The page we are leaving is a drupal_goto(). Present a redirection page
 756      // so that the developer can see the intermediate query log.
 757      // We don't want to load user module here, so keep function_exists() call.
 758      if (isset($user) && function_exists('user_access') && user_access('access devel information') && variable_get('devel_redirect_page', 0)) {
 759        $output = t_safe('<p>The user is being redirected to <a href="@destination">@destination</a>.</p>', array('@destination' => $destination));
 760        print theme('page', $output);
 761  
 762        // Don't allow the automatic redirect to happen.
 763        drupal_page_footer();
 764        exit();
 765      }
 766      else {
 767        $GLOBALS['devel_redirecting'] = TRUE;
 768      }
 769    }
 770  }
 771  
 772  // Borrowed from Drupal 7 - drupal_is_cli().
 773  function devel_verify_cli() {
 774    return ((!isset($_SERVER['SERVER_SOFTWARE']) || $_SERVER['SERVER_SOFTWARE'] == 'PHP CLI')
 775        && (php_sapi_name() == 'cli' || (is_numeric($_SERVER['argc']) && $_SERVER['argc'] > 0)));
 776  }
 777  
 778  
 779  /**
 780   * See devel_start() which registers this function as a shutdown function.
 781   */
 782  function devel_shutdown() {
 783    // Register the real shutdown function so it runs later than other shutdown functions.
 784    register_shutdown_function('devel_shutdown_real');
 785  }
 786  
 787  /**
 788   * Log URL to Drush log if this is a drush request.
 789   *
 790   * This shutdown function is registered by devel_xhprof_enable()
 791   */
 792  function devel_shutdown_xhprof_drush() {
 793    global $devel_run_id;
 794    $devel_run_id = variable_get('devel_xhprof_enabled', FALSE) ? devel_shutdown_xhprof(): NULL;
 795    if ($devel_run_id && function_exists('drush_log')) {
 796      drush_log('xhprof link: ' . devel_xhprof_link($devel_run_id, 'url'), 'notice');
 797    }
 798  }
 799  
 800  /**
 801   * See devel_shutdown() which registers this function as a shutdown function. Displays developer information in the footer.
 802   */
 803  function devel_shutdown_real() {
 804    global $queries, $memory_init, $user;
 805    $output = $txt = '';
 806  
 807    // Set $GLOBALS['devel_shutdown'] = FALSE in order to supress the
 808    // devel footer for a page.  Not necessary if your page outputs any
 809    // of the Content-type http headers tested below (e.g. text/xml,
 810    // text/javascript, etc).  This is is advised where applicable.
 811    if (!isset($GLOBALS['devel_shutdown']) && !isset($GLOBALS['devel_redirecting'])) {
 812      // Try not to break non html pages.
 813      if (function_exists('drupal_get_headers')) {
 814        $headers = drupal_get_headers();
 815        $formats = array('xml', 'javascript', 'json', 'plain', 'image', 'application', 'csv', 'x-comma-separated-values');
 816        foreach ($formats as $format) {
 817          if (strstr($headers, $format)) {
 818            return;
 819          }
 820        }
 821      }
 822  
 823      if (isset($user) && user_access('access devel information')) {
 824        list($counts, $query_summary) = devel_query_summary();
 825        // Query log off, timer on.
 826        if (!variable_get('devel_query_display', 0) && variable_get('dev_timer', 0)) {
 827          $output .= '<div class="dev-timer">'. devel_timer() .' '. $query_summary .'</div>';
 828        }
 829  
 830        // Query log on.
 831        $sum = 0;
 832        if (variable_get('devel_query_display', FALSE)) {
 833          $output .= '<div class="dev-query">';
 834          $output .= $query_summary;
 835          // calling theme() during shutdown is very bad if registry gets rebuilt like when making a change on  admin/build/modules
 836          // so we check for presence of theme registry before calling theme()
 837          if (function_exists('theme_get_registry') && theme_get_registry()) {
 838            $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)));
 839            if (variable_get('dev_timer', 0)) {
 840              $txt .= devel_timer();
 841            }
 842            $output .= $txt;
 843            $output .= '</div>';
 844            $output .= devel_query_table($queries, $counts);
 845          }
 846          else {
 847            $output .= $txt;
 848            $output .= '</div>';
 849            ob_start();
 850            dprint_r($queries);
 851            $output .= ob_get_clean();
 852          }
 853        }
 854  
 855        if (variable_get('dev_mem', FALSE) && function_exists('memory_get_usage')) {
 856          $memory_shutdown = memory_get_usage();
 857          $args = array('@memory_init' => round($memory_init / 1024 / 1024, 2), '@memory_shutdown' => round($memory_shutdown / 1024 / 1024, 2));
 858          $msg = '<div class="dev-memory-usage"><h3>Memory usage:</h3> Memory used at: devel_init()=<strong>@memory_init</strong> MB, devel_shutdown()=<strong>@memory_shutdown</strong> MB.</div>';
 859          // theme() may not be available. not t() either.
 860          $output .= t_safe($msg, $args);
 861        }
 862  
 863        $output .= devel_xhprof_link_show();
 864  
 865        // TODO: gzip this text if we are sending a gzip page. see drupal_page_header().
 866      }
 867  
 868      if ($output) {
 869        print $output;
 870      }
 871    }
 872    devel_store_queries();
 873  }
 874  
 875  function devel_shutdown_xhprof() {
 876    $namespace = variable_get('site_name', '');  // namespace for your application
 877    $xhprof_data = xhprof_disable();
 878    $xhprof_runs = new XHProfRuns_Default();
 879    return $xhprof_runs->save_run($xhprof_data, $namespace);
 880  }
 881  
 882  
 883  function devel_store_queries() {
 884    if (variable_get('devel_store_queries', 0) && rand(1, variable_get('devel_store_random', 1)) == 1) {
 885      global $active_db, $queries, $db_type;
 886  
 887      $qids = array();
 888      $values = array();
 889      $fields = array();
 890      // We need this for the devel_queries insert below.
 891      setlocale(LC_NUMERIC, 'C');
 892      foreach ($queries as $value) {
 893        list($function, $query) = explode("\n", $value[0]);
 894        $query = preg_replace(array("/'.*'/s", "/\d.*\.\d.*/", "/\d.*/"), array("S", "F", "D"), $query);
 895        $hash = md5($function . $query);
 896        if (!isset($qids[$hash])) {
 897          $qids[$hash] = db_result(devel_db_query("SELECT qid FROM {devel_queries} WHERE hash = '%s'", $hash));
 898          if (!$qids[$hash]) {
 899            devel_db_query("INSERT INTO {devel_queries} (query, function, hash) VALUES ('%s', '%s', '%s')", $query, $function, $hash);
 900            $qids[$hash] = db_last_insert_id('devel_queries', 'qid');
 901          }
 902        }
 903        $fields[] = "(%d, '%f')";
 904        $values[] = $qids[$hash];
 905        $values[] = $value[1];
 906      }
 907      if (count($fields)) {
 908        devel_db_query('INSERT INTO {devel_times} (qid, time) VALUES '. implode(',', $fields), $values);
 909      }
 910    }
 911  }
 912  
 913  function devel_query_summary() {
 914    global $queries;
 915    if (variable_get('dev_query', FALSE) && is_array($queries)) {
 916      $sum = 0;
 917      foreach ($queries as $query) {
 918        $text[] = $query[0];
 919        $sum += $query[1];
 920      }
 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))));
 923    }
 924  }
 925  
 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);
 931    }
 932    else {
 933      strtr($string, $args);
 934    }
 935  }
 936  
 937  /**
 938   * Returns a list of all currently defined user functions in the current
 939   * request lifecycle, with links their documentation.
 940   */
 941  function devel_function_reference() {
 942    $functions = get_defined_functions();
 943    $version = devel_get_core_version(VERSION);
 944    $ufunctions = $functions['user'];
 945    sort($ufunctions);
 946    $api = variable_get('devel_api_url', 'api.drupal.org');
 947    foreach ($ufunctions as $function) {
 948      $links[] = l($function, "http://$api/api/function/$function/$version");
 949    }
 950    return theme('item_list', $links);
 951  }
 952  
 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];
 958    }
 959    // Map from 5.5 -> 5 or 6.0-beta2 -> 6
 960    else {
 961      return $version_parts[0];
 962    }
 963  }
 964  
 965  function devel_db_query($query) {
 966    global $active_db;
 967    $args = func_get_args();
 968    array_shift($args);
 969    $query = db_prefix_tables($query);
 970    if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
 971      $args = $args[0];
 972    }
 973    _db_query_callback($args, TRUE);
 974    $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
 975    return db_query($query, $active_db);
 976  }
 977  
 978  // See http://drupal.org/node/126098
 979  function devel_is_compatible_optimizer() {
 980     ob_start();
 981     phpinfo();
 982     $info = ob_get_contents();
 983     ob_end_clean();
 984  
 985     // Match the Zend Optimezer version in the phpinfo information
 986     $found = preg_match('/Zend&nbsp;Optimizer&nbsp;v([0-9])\.([0-9])\.([0-9])/', $info, $matches);
 987  
 988     if ($matches) {
 989       $major = $matches[1];
 990       $minor = $matches[2];
 991       $build = $matches[3];
 992  
 993       if ($major >= 3) {
 994         if ($minor >= 3) {
 995           return TRUE;
 996         }
 997         elseif ($minor == 2 && $build >= 8) {
 998           return TRUE;
 999         }
1000         else {
1001           return FALSE;
1002         }
1003      }
1004      else {
1005        return FALSE;
1006      }
1007    }
1008    else {
1009      return TRUE;
1010    }
1011  }
1012  
1013  function devel_admin_settings() {
1014    $form['queries'] = array('#type' => 'fieldset', '#title' => t('Query log'));
1015  
1016    $description = t("Collect query info. If disabled, no query log functionality will work.");
1017    if (!devel_is_compatible_optimizer()) {
1018      $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;
1019    }
1020    $form['queries']['dev_query'] = array('#type' => 'checkbox',
1021      '#title' => t('Collect query info'),
1022      '#default_value' => variable_get('dev_query', 0),
1023      '#disabled' => !devel_is_compatible_optimizer() ? TRUE : FALSE,
1024      '#description' => $description,
1025    );
1026  
1027    $form['queries']['devel_query_display'] = array('#type' => 'checkbox',
1028      '#title' => t('Display query log'),
1029      '#default_value' => variable_get('devel_query_display', 0),
1030      '#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.'));
1031    $form['queries']['devel_query_sort'] = array('#type' => 'radios',
1032      '#title' => t('Sort query log'),
1033      '#default_value' =>   variable_get('devel_query_sort', DEVEL_QUERY_SORT_BY_SOURCE),
1034      '#options' => array(t('by source'), t('by duration')),
1035      '#description' => t('The query table can be sorted in the order that the queries were executed or by descending duration.'),
1036    );
1037    $form['queries']['devel_execution'] = array('#type' => 'textfield',
1038      '#title' => t('Slow query highlighting'),
1039      '#default_value' => variable_get('devel_execution', 5),
1040      '#size' => 4,
1041      '#maxlength' => 4,
1042      '#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.'),
1043    );
1044    $form['queries']['devel_store_queries'] = array('#type' => 'checkbox',
1045      '#title' => t('Store executed queries'),
1046      '#default_value' => variable_get('devel_store_queries', 0),
1047      '#description' => t('Store statistics about executed queries. See the devel_x tables.'));
1048    $form['queries']['devel_store_random'] = array('#type' => 'textfield',
1049      '#title' => t('Sampling interval'),
1050      '#default_value' => variable_get('devel_store_random', 1),
1051      '#size' => 4,
1052      '#description' => t('If storing query statistics, only store every nth page view. 1 means every page view, 2 every second, and so on.'));
1053  
1054      $form['xhprof'] = array(
1055        '#type' => 'fieldset',
1056        '#title' => 'XHProf',
1057        '#description' => t('XHProf is a php extension which is essential for profiling your Drupal site. It pinpoints slow functions, and also memory hogging functions.'),
1058      );
1059      $description = extension_loaded('xhprof') ? t('Profile requests with the xhprof php extension.') : '<span class="warning">' . t('You must enable the <a href="!url">xhprof php extension</a> to use this feature.', array('!url' => url('http://techportal.ibuildings.com/2009/12/01/profiling-with-xhprof/'))) . '</span>';
1060      $form['xhprof']['devel_xhprof_enabled'] = array(
1061        '#type' => 'checkbox',
1062        '#title' => t('Enable profiling of all page views and <a href="!drush">drush</a> requests.', array('!drush' => url('http://drush.ws'))),
1063        '#default_value' => variable_get('devel_xhprof_enabled', FALSE),
1064        '#description' => $description,
1065        '#disabled' => !extension_loaded('xhprof'),
1066      );
1067      $form['xhprof']['devel_xhprof_directory'] = array(
1068        '#type' => 'textfield',
1069        '#title' => 'xhprof directory',
1070        '#description' => t('Location of the xhprof source code on your system, usually somewhere in /usr/local/share or /usr/share, include the leading forward slash.'),
1071        '#default_value' => variable_get('devel_xhprof_directory', ''),
1072      );
1073      $form['xhprof']['devel_xhprof_url'] = array(
1074        '#type' => 'textfield',
1075        '#title' => 'XHProf URL',
1076        '#description' => t('Path to the publically accessible xhprof_html - required to display profiler reports. You will need to set this up outside Drupal, for example at http://xhprof.localhost/xhprof_html'),
1077        '#default_value' => variable_get('devel_xhprof_url', ''),
1078      );
1079  
1080    $form['devel_api_url'] = array('#type' => 'textfield',
1081      '#title' => t('API Site'),
1082      '#default_value' => variable_get('devel_api_url', 'api.drupal.org'),
1083      '#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'))));
1084    $form['dev_timer'] = array('#type' => 'checkbox',
1085      '#title' => t('Display page timer'),
1086      '#default_value' => variable_get('dev_timer', 0),
1087      '#description' => t('Display page execution time in the query log box.'),
1088    );
1089    $form['dev_mem'] = array('#type' => 'checkbox',
1090      '#title' => t('Display memory usage'),
1091      '#default_value' => variable_get('dev_mem', 0),
1092      '#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. PHP must have been compiled with the <em>--enable-memory-limit</em> configuration option for this feature to work.'),
1093    );
1094    $form['devel_redirect_page'] = array('#type' => 'checkbox',
1095      '#title' => t('Display redirection page'),
1096      '#default_value' => variable_get('devel_redirect_page', 0),
1097      '#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.'),
1098    );
1099    $form['devel_error_handler'] = array('#type' => 'radios',
1100      '#title' => t('Error handler'),
1101      '#default_value' => variable_get('devel_error_handler', DEVEL_ERROR_HANDLER_STANDARD),
1102      '#options' => array(DEVEL_ERROR_HANDLER_NONE => t('None'), DEVEL_ERROR_HANDLER_STANDARD => t('Standard drupal')),
1103      '#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'))),
1104    );
1105    if (has_krumo()) {
1106      $form['devel_error_handler']['#options'][DEVEL_ERROR_HANDLER_BACKTRACE] = t('Backtrace');
1107    }
1108  
1109    $options = drupal_map_assoc(array('default', 'blue', 'green', 'orange', 'white', 'disabled'));
1110    $form['devel_krumo_skin'] = array(
1111     '#type' => 'radios',
1112     '#title' => t('Krumo display'),
1113     '#description' => t('Select a skin for your debug messages or select <em>disabled</em> to display object and array output in standard PHP format.'),
1114     '#options' => $options,
1115     '#default_value' => variable_get('devel_krumo_skin', 'default'),
1116    );
1117  
1118    // Save any old SMTP library
1119    if (variable_get('smtp_library', '') != '' && variable_get('smtp_library', '') != drupal_get_filename('module', 'devel')) {
1120      variable_set('devel_old_smtp_library', variable_get('smtp_library', ''));
1121    }
1122    $smtp_options = array(
1123      '' => t('Default'),
1124      drupal_get_filename('module', 'devel') => t('Log only'),
1125    );
1126    if (variable_get('devel_old_smtp_library', '') != '') {
1127      $smtp_options[variable_get('devel_old_smtp_library', '')] = t('Other (!library)', array('!library' => variable_get('devel_old_smtp_library', '')));
1128    }
1129    $form['smtp_library'] = array(
1130      '#type' => 'radios',
1131      '#title' => t('SMTP library'),
1132      '#options' => $smtp_options,
1133      '#default_value' => variable_get('smtp_library', ''),
1134    );
1135  
1136    $form['devel_rebuild_theme_registry'] = array(
1137     '#type' => 'checkbox',
1138     '#title' => t('Rebuild the theme registry on every page load'),
1139     '#description' => t('While creating new templates and theme_ overrides the theme registry needs to be rebuilt.'),
1140     '#default_value' => variable_get('devel_rebuild_theme_registry', FALSE),
1141    );
1142  
1143    return system_settings_form($form);
1144  }
1145  
1146  /**
1147   * Menu callback; clears all caches, then redirects to the previous page.
1148   */
1149  function devel_cache_clear() {
1150    // If you used to implement our own cache clear hook, implement hook_flush_caches instead. See drupal_flush_all_caches()
1151    drupal_flush_all_caches();
1152  
1153    drupal_set_message('Cache cleared.');
1154  
1155    drupal_goto();
1156  }
1157  
1158  /**
1159   * Generates the execute block form.
1160   */
1161  function devel_execute_block_form() {
1162    $form['execute'] = array(
1163      '#type' => 'fieldset',
1164      '#title' => t('Execute PHP Code'),
1165      '#collapsible' => TRUE,
1166      '#collapsed' => (!isset($_POST['code'])),
1167    );
1168    $form['#submit'] = array('devel_execute_form_submit');
1169    return array_merge_recursive($form, devel_execute_form());
1170  }
1171  
1172  /**
1173   * Generates the execute form.
1174   */
1175  function devel_execute_form() {
1176    $form['execute']['code'] = array(
1177      '#type' => 'textarea',
1178      '#title' => t('PHP code to execute'),
1179      '#description' => t('Enter some code. Do not use <code>&lt;?php ?&gt;</code> tags.'),
1180      '#rows' => 20,
1181    );
1182    $form['execute']['op'] = array('#type' => 'submit', '#value' => t('Execute'));
1183    $form['#redirect'] = FALSE;
1184    $form['#skip_duplicate_check'] = TRUE;
1185    return $form;
1186  }
1187  
1188  /**
1189   * Process PHP execute form submissions.
1190   */
1191  function devel_execute_form_submit($form, &$form_state) {
1192    ob_start();
1193    print eval($form_state['values']['code']);
1194    dsm(ob_get_clean());
1195  }
1196  
1197  /**
1198   * Menu callback; clear the database, resetting the menu to factory defaults.
1199   */
1200  function devel_menu_rebuild() {
1201    menu_rebuild();
1202    drupal_set_message(t('The menu router has been rebuilt.'));
1203    drupal_goto();
1204  }
1205  
1206  /**
1207   * Display a list of installed modules with the option to reinstall them.
1208   */
1209  function devel_reinstall($form_state) {
1210    $output = '';
1211    $modules = module_list();
1212    sort($modules);
1213    $options = drupal_map_assoc($modules);
1214    $form['list'] = array(
1215      '#type' => 'checkboxes',
1216      '#options' => $options,
1217      '#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>.'),
1218    );
1219    $form['submit'] = array(
1220      '#value' => t('Reinstall'),
1221      '#type' => 'submit',
1222    );
1223    if (empty($form_state['post'])) {
1224      drupal_set_message(t('Warning - will delete your module tables and variables.'), 'error');
1225    }
1226    return $form;
1227  }
1228  
1229  /**
1230   * Process reinstall menu form submissions.
1231   */
1232  function devel_reinstall_submit($form, &$form_state) {
1233    require_once  './includes/install.inc';
1234    $modules = array_filter($form_state['values']['list']);
1235    foreach ($modules as $module) {
1236      module_load_install($module);
1237      $versions = drupal_get_schema_versions($module);
1238      drupal_set_installed_schema_version($module, SCHEMA_UNINSTALLED);
1239      module_invoke($module, 'uninstall');
1240      _drupal_install_module($module);
1241      module_invoke($module, 'enable');
1242      drupal_get_schema(NULL, TRUE);
1243      drupal_set_message(t('Uninstalled and installed the %name module.', array('%name' => $module)));
1244    }
1245  }
1246  
1247  // Menu callback.
1248  function devel_theme_registry() {
1249    init_theme();
1250    $hooks = theme_get_registry();
1251    return kprint_r($hooks, TRUE);
1252  }
1253  
1254  /**
1255   * Menu callback; display all variables.
1256   */
1257  function devel_variable_page() {
1258    // we print our own page so as to avoid blocks
1259    $output = drupal_get_form('devel_variable_form');
1260    print theme('page', $output, FALSE);
1261  }
1262  
1263  function devel_variable_form() {
1264    $header = array(
1265      array(''),
1266      array('data' => t('Name'), 'field' => 'name', 'sort' => 'asc'),
1267      array('data' => t('Value'), 'field' => 'value'),
1268      array('data' => t('Length'), 'field' => 'length'),
1269      array('data' => t('Operations')),
1270    );
1271    // TODO: we could get variables out of $conf but that would include hard coded ones too. ideally i would highlight overrridden/hard coded variables
1272    switch ($GLOBALS['db_type']) {
1273      case 'mssql':
1274        $sql = "SELECT *, COL_LENGTH('{variable}', 'value') AS length FROM {variable}";
1275        break;
1276      default:
1277        $sql = "SELECT *, LENGTH(value) AS length FROM {variable}";
1278        break;
1279    }
1280    $result = db_query($sql . tablesort_sql($header));
1281    while ($row = db_fetch_object($result)) {
1282      $variables[$row->name] = '';
1283      $form['name'][$row->name] = array('#value' => check_plain($row->name));
1284      if (merits_krumo($row->value)) {
1285        $value = krumo_ob(variable_get($row->name, NULL));
1286      }
1287      else {
1288        if (drupal_strlen($row->value) > 70) {
1289          $value = check_plain(drupal_substr($row->value, 0, 65)) .'...';
1290        }
1291        else {
1292          $value = check_plain($row->value);
1293        }
1294      }
1295      $form[$row->name]['value'] = array('#value' => $value);
1296      $form[$row->name]['length'] = array('#value' => $row->length);
1297      $form[$row->name]['edit'] = array('#value' => l(t('edit'), "devel/variable/edit/$row->name"));
1298    }
1299    $form['variables'] = array('#type' => 'checkboxes', '#options' => $variables);
1300    $form['submit'] = array(
1301      '#type' => 'submit',
1302      '#value' => t('Delete'),
1303    );
1304    return $form;
1305  }
1306  
1307  function theme_devel_variable_form($form) {
1308    // TODO: this is not being called for some reason
1309    // die(form);
1310    $children = element_children($form['name']);
1311    foreach ($children as $key) {
1312      $rows[] = array(
1313        drupal_render($form['variables'][$key]),
1314        drupal_render($form['name'][$key]),
1315        drupal_render($form[$key]['value']),
1316        drupal_render($form[$key]['length']),
1317        drupal_render($form[$key]['edit']),
1318      );
1319    }
1320    $header = array(
1321      theme('table_select_header_cell'),
1322      array('data' => t('Name'), 'field' => 'name', 'sort' => 'asc'),
1323      array('data' => t('Value'), 'field' => 'value'),
1324      array('data' => t('Length'), 'field' => 'length'),
1325      array('data' => t('Operations'), 'colspan' => 2),
1326    );
1327    $output = theme('table', $header, $rows);
1328    $output .= drupal_render($form);
1329  
1330    return $output;
1331  }
1332  
1333  function devel_variable_form_submit($form, &$form_state) {
1334    $deletes = array_filter($form_state['values']['variables']);
1335    array_walk($deletes, 'variable_del');
1336    if (count($deletes)) {
1337      drupal_set_message(format_plural(count($deletes), 'One variable deleted.', '@count variables deleted.'));
1338    }
1339  }
1340  
1341  function devel_variable_edit($form_state, $name) {
1342    $value = variable_get($name, 'not found');
1343    $form['name'] = array(
1344      '#type' => 'value',
1345      '#value' => $name
1346    );
1347    $form['value'] = array(
1348      '#type' => 'item',
1349      '#title' => t('Old value'),
1350      '#value' => dpr($value, TRUE),
1351    );
1352    if (is_string($value) || is_numeric($value)) {
1353      $form['new'] = array(
1354        '#type' => 'textarea',
1355        '#title' => t('New value'),
1356        '#default_value' => $value
1357      );
1358      $form['submit'] = array(
1359        '#type' => 'submit',
1360        '#value' => t('Submit'),
1361      );
1362    }
1363    else {
1364      $api = variable_get('devel_api_url', 'api.drupal.org');
1365      $form['new'] = array(
1366        '#type' => 'item',
1367        '#title' => t('New value'),
1368        '#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"))
1369      );
1370    }
1371    drupal_set_title(check_plain($name));
1372    return $form;
1373  }
1374  
1375  function devel_variable_edit_submit($form, &$form_state) {
1376    variable_set($form_state['values']['name'], $form_state['values']['new']);
1377    drupal_set_message(t('Saved new value for %name.', array('%name' => $form_state['values']['name'])));
1378     'devel/variable';
1379  }
1380  
1381  /**
1382   * Menu callback: display the session.
1383   */
1384  function devel_session() {
1385    global $user;
1386    $output = kprint_r($_SESSION, TRUE);
1387    $headers = array(t('Session name'), t('Session ID'));
1388    $output .= theme('table', $headers, array(array(session_name(), session_id())));
1389    return $output;
1390  }
1391  
1392  /**
1393   * Switch from original user to another user and back.
1394   *
1395   * Note: taken from mailhandler.module.
1396   *
1397   * Note: You first need to run devel_switch_user without
1398   * argument to store the current user. Call devel_switch_user
1399   * without argument to set the user back to the original user.
1400   *
1401   * @param $name The username to switch to.
1402   *
1403   */
1404  function devel_switch_user($name = NULL) {
1405    global $user;
1406    static $orig_user = array();
1407  
1408    if (isset($name)) {
1409      $user = user_load(array('name' => $name));
1410    }
1411    // Retrieve the initial user. Can be called multiple times.
1412    elseif (count($orig_user)) {
1413      $user = array_shift($orig_user);
1414      array_unshift($orig_user, $user);
1415    }
1416    // Store the initial user.
1417    else {
1418      $orig_user[] = $user;
1419    }
1420    drupal_goto();
1421  }
1422  
1423  /**
1424   * Menu callback; prints the loaded structure of the current node/user.
1425   */
1426  function devel_load_object($object, $name = NULL) {
1427    $title = isset($object->title) ? $object->title : $object->name;
1428    drupal_set_title(check_plain($title));
1429    return kdevel_print_object($object, '$'. $name .'->');
1430  }
1431  
1432  /**
1433   * Menu callback; prints the render structure of the current object (currently node or user).
1434   */
1435  function devel_render_object($type, $object) {
1436    $output = '';
1437    $title = isset($object->title) ? $object->title : $object->name;
1438    // not sure why menu system doesn't give us a reasonable title here.
1439    drupal_set_title(check_plain($title));
1440    $function = $type .'_build_content';
1441    $content = $function($object, FALSE, FALSE);
1442    return kdevel_print_object($content, '$'. $type .'->');
1443  }
1444  
1445  function devel_elements_page() {
1446    return kdevel_print_object(module_invoke_all('elements'));
1447  }
1448  
1449  /**
1450   * Print an object or array using either Krumo (if installed) or devel_print_object()
1451   *
1452   * @param $object
1453   *   array or object to print
1454   * @param $prefix
1455   *   prefixing for output items
1456   */
1457  function kdevel_print_object($object, $prefix = NULL) {
1458    return has_krumo() ? krumo_ob($object) : devel_print_object($object, $prefix);
1459  }
1460  
1461  // Save krumo htlm using output buffering.
1462  function krumo_ob($object) {
1463    ob_start();
1464    krumo($object);
1465    $output = ob_get_contents();
1466    ob_end_clean();
1467    return $output;
1468  }
1469  
1470  /**
1471   * Display an object or array
1472   *
1473   * @param $object
1474   *   the object or array
1475   * @param $prefix
1476   *   prefix for the output items (example "$node->", "$user->", "$")
1477   * @param $header
1478   *   set to FALSE to suppress the output of the h3
1479   */
1480  function devel_print_object($object, $prefix = NULL, $header = TRUE) {
1481    drupal_add_css(drupal_get_path('module', 'devel') .'/devel.css');
1482    $output = '<div class="devel-obj-output">';
1483    if ($header) {
1484      $output .= '<h3>'. t('Display of !type !obj', array('!type' => str_replace(array('$', '->'), '', $prefix), '!obj' => gettype($object))) .'</h3>';
1485    }
1486    $output .= _devel_print_object($object, $prefix);
1487    $output .= '</div>';
1488    return $output;
1489  }
1490  
1491  /**
1492   * Recursive (and therefore magical) function goes through an array or object and
1493   * returns a nicely formatted listing of its contents.
1494   *
1495   * @param $obj
1496   *   array or object to recurse through
1497   * @param $prefix
1498   *   prefix for the output items (example "$node->", "$user->", "$")
1499   * @param $parents
1500   *   used by recursion
1501   * @param $object
1502   *   used by recursion
1503   * @return
1504   *   fomatted html
1505   *
1506   * @todo
1507   *   currently there are problems sending an array with a varname
1508   */
1509  function _devel_print_object($obj, $prefix = NULL, $parents = NULL, $object = FALSE) {
1510    static $root_type, $out_format;
1511    // TODO: support objects with references. See http://drupal.org/node/234581.
1512    if (isset($obj->view)) {
1513      return;
1514    }
1515  
1516    if (!isset($root_type)) {
1517      $root_type = gettype($obj);
1518      if ($root_type == 'object') {
1519        $object = TRUE;
1520      }
1521    }
1522  
1523    if (is_object($obj)) {
1524      $obj = (array)$obj;
1525    }
1526    if (is_array($obj)) {
1527      $output = "<dl>\n";
1528      foreach ($obj as $field => $value) {
1529        if ($field == 'devel_flag_reference') {
1530          continue;
1531        }
1532        if (!is_null($parents)) {
1533          if ($object) {
1534            $field = $parents .'->'. $field;
1535          }
1536          else {
1537            if (is_int($field)) {
1538              $field = $parents .'['. $field .']';
1539            }
1540            else {
1541              $field = $parents .'[\''. $field .'\']';
1542            }
1543          }
1544        }
1545  
1546        $type = gettype($value);
1547  
1548        $show_summary = TRUE;
1549        $summary = NULL;
1550        if ($show_summary) {
1551          switch ($type) {
1552            case 'string' :
1553            case 'float' :
1554            case 'integer' :
1555              if (strlen($value) == 0) {
1556                $summary = t("{empty}");
1557              }
1558              elseif (strlen($value) < 40) {
1559                $summary = htmlspecialchars($value);
1560              }
1561              else {
1562                $summary = format_plural(drupal_strlen($value), '1 character', '@count characters');
1563              }
1564              break;
1565            case 'array' :
1566            case 'object' :
1567              $summary = format_plural(count((array)$value), '1 element', '@count elements');
1568              break;
1569            case 'boolean' :
1570              $summary = $value ? t('TRUE') : t('FALSE');
1571              break;
1572          }
1573        }
1574        if (!is_null($summary)) {
1575          $typesum = '('. $type .', <em>'. $summary .'</em>)';
1576        }
1577        else {
1578          $typesum = '('. $type .')';
1579        }
1580  
1581        $output .= '<span class="devel-attr">';
1582        $output .= "<dt><span class=\"field\">{$prefix}{$field}</span> $typesum</dt>\n";
1583        $output .= "<dd>\n";
1584        // Check for references.
1585        if (is_array($value) && isset($value['devel_flag_reference'])) {
1586          $value['devel_flag_reference'] = TRUE;
1587        }
1588        // Check for references to prevent errors from recursions.
1589        if (is_array($value) && isset($value['devel_flag_reference']) && !$value['devel_flag_reference']) {
1590          $value['devel_flag_reference'] = FALSE;
1591          $output .= _devel_print_object($value, $prefix, $field);
1592        }
1593        elseif (is_object($value)) {
1594          $value->devel_flag_reference = FALSE;
1595          $output .= _devel_print_object((array)$value, $prefix, $field, TRUE);
1596        }
1597        else {
1598          $value = is_bool($value) ? ($value ? 'TRUE' : 'FALSE') : $value;
1599          $output .= htmlspecialchars(print_r($value, TRUE)) ."\n";
1600        }
1601        $output .= "</dd></span>\n";
1602      }
1603      $output .= "</dl>\n";
1604    }
1605    return $output;
1606  }
1607  
1608  /**
1609   * Adds a table at the bottom of the page cataloguing data on all the database queries that were made to
1610   * generate the page.
1611   */
1612  function devel_query_table($queries, $counts) {
1613    if (empty($queries)) {
1614      return FALSE;
1615    }
1616    $version = devel_get_core_version(VERSION);
1617    $header = array('ms', '#', 'where', 'query');
1618    $i = 0;
1619    $api = variable_get('devel_api_url', 'api.drupal.org');
1620    foreach ($queries as $query) {
1621      // dprint_r($query);
1622      $ar = explode("\n", $query[0]);
1623      $function=array_shift($ar);
1624      $count = isset($counts[$query[0]]) ? $counts[$query[0]] : 0;
1625      $query[0]=join(' ', $ar);
1626  
1627      $diff = round($query[1] * 1000, 2);
1628      if ($diff > variable_get('devel_execution', 5)) {
1629        $cell[$i][] = array('data' => $diff, 'class' => 'marker');
1630      }
1631      else {
1632        $cell[$i][] = $diff;
1633      }
1634      if ($count > 1) {
1635        $cell[$i][] = array('data' => $count, 'class' => 'marker');
1636      }
1637      else {
1638        $cell[$i][] = $count;
1639      }
1640      $cell[$i][] = l($function, "http://$api/api/$version/function/$function");
1641      $pos = strpos($query[0], '*/') !== FALSE ? strpos($query[0], '*/') + 3 : 0;
1642      $cell[$i][] = check_plain(substr($query[0], $pos));
1643      $i++;
1644      unset($diff, $count);
1645    }
1646    if (variable_get('devel_query_sort', DEVEL_QUERY_SORT_BY_SOURCE)) {
1647      usort($cell, '_devel_table_sort');
1648    }
1649    return theme('devel_querylog', $header, $cell);
1650  }
1651  
1652  function theme_devel_querylog_row($row) {
1653    $i = 0;
1654    $output = '';
1655    foreach ($row as $cell) {
1656      $i++;
1657  
1658      if (is_array($cell)) {
1659        $data = !empty($cell['data']) ? $cell['data'] : '';
1660        unset($cell['data']);
1661        $attr = $cell;
1662      }
1663      else {
1664        $data = $cell;
1665        $attr = array();
1666      }
1667  
1668      if (!empty($attr['class'])) {
1669        $attr['class'] .= " cell cell-$i";
1670      }
1671      else {
1672        $attr['class'] = "cell cell-$i";
1673      }
1674      $attr = drupal_attributes($attr);
1675  
1676      $output .= "<div $attr>$data</div>";
1677    }
1678    return $output;
1679  }
1680  
1681  function theme_devel_querylog($header = array(), $rows = array()) {
1682    $output = '';
1683    if (!empty($header)) {
1684      $output .= "<div class='devel-querylog devel-querylog-header clear-block'>";
1685      $output .= theme('devel_querylog_row', $header);
1686      $output .= "</div>";
1687    }
1688    if (!empty($rows)) {
1689      $i = 0;
1690      foreach ($rows as $row) {
1691        $i++;
1692        $zebra = ($i % 2) == 0 ? 'even' : 'odd';
1693        $output .= "<div class='devel-querylog devel-querylog-$zebra clear-block'>";
1694        $output .= theme('devel_querylog_row', $row);
1695        $output .= "</div>";
1696      }
1697    }
1698    return $output;
1699  }
1700  
1701  function _devel_table_sort($a, $b) {
1702    $a = is_array($a[0]) ? $a[0]['data'] : $a[0];
1703    $b = is_array($b[0]) ? $b[0]['data'] : $b[0];
1704    if ($a < $b) {
1705      return 1;
1706    }
1707    if ($a > $b) {
1708      return -1;
1709    }
1710    return 0;
1711  }
1712  
1713  /**
1714   * Displays page execution time at the bottom of the page.
1715   */
1716  function devel_timer() {
1717    $time = timer_read('page');
1718    return t_safe(' Page execution time was @time ms.', array('@time' => $time));
1719  }
1720  
1721  /**
1722   * Displays page execution time at the bottom of the page.
1723   */
1724  function devel_xhprof_link_show() {
1725    if (variable_get('devel_xhprof_enabled', FALSE)) {
1726      return devel_xhprof_link($GLOBALS['devel_run_id']);
1727    }
1728  }
1729  
1730  function devel_xhprof_link($run_id, $type = 'link') {
1731    // @todo: render results from within Drupal.
1732    $xhprof_url = variable_get('devel_xhprof_url', '');
1733    $namespace = variable_get('site_name', '');  // namespace for your application
1734    if ($xhprof_url) {
1735      $url  = $xhprof_url . "/index.php?run=$run_id&source=$namespace";
1736      return $type == 'url' ? $url : t('<a href="@xhprof">XHProf output</a>. ', array('@xhprof' => $url));
1737    }
1738  }
1739  
1740  
1741  /**
1742   * Prints the arguments passed into the current function
1743   */
1744  function dargs($always = TRUE) {
1745    static $printed;
1746    if ($always || !$printed) {
1747      $bt = debug_backtrace();
1748      print kdevel_print_object($bt[1]['args']);
1749      $printed = TRUE;
1750    }
1751  }
1752  
1753  // An alias for drupal_debug().
1754  function dd($data, $label = NULL) {
1755    return drupal_debug($data, $label);
1756  }
1757  
1758  // Log any variable to a drupal_debug.log in the site's temp directory.
1759  // See http://drupal.org/node/314112
1760  function drupal_debug($data, $label = NULL) {
1761    ob_start();
1762    print_r($data);
1763    $string = ob_get_clean();
1764    if ($label) {
1765      $out = $label .': '. $string;
1766    }
1767    else {
1768      $out = $string;
1769    }
1770    $out .= "\n";
1771  
1772    // The temp directory does vary across multiple simpletest instances.
1773    $file = file_directory_temp() .'/drupal_debug.txt';
1774    if (file_put_contents($file, $out, FILE_APPEND) === FALSE) {
1775      drupal_set_message(t('The file could not be written.'), 'error');
1776      return FALSE;
1777    }
1778  }
1779  
1780  /**
1781   * Print a variable to the 'message' area of the page. Uses drupal_set_message()
1782   */
1783  function dpm($input, $name = NULL) {
1784    if (user_access('access devel information')) {
1785      $export = kprint_r($input, TRUE, $name);
1786      drupal_set_message($export);
1787    }
1788  }
1789  
1790  /**
1791   * Var_dump() a variable to the 'message' area of the page. Uses drupal_set_message()
1792   */
1793  function dvm($input, $name = NULL) {
1794    if (user_access('access devel information')) {
1795      $export = dprint_r($input, TRUE, $name, 'var_dump', FALSE);
1796      drupal_set_message($export);
1797    }
1798  }
1799  
1800  // legacy function that was poorly named. use dpm() instead, since the 'p' maps to 'print_r'
1801  function dsm($input, $name = NULL) {
1802    dpm($input, $name);
1803  }
1804  
1805  /**
1806   * An alias for dprint_r(). Saves carpal tunnel syndrome.
1807   */
1808  function dpr($input, $return = FALSE, $name = NULL) {
1809    return dprint_r($input, $return, $name);
1810  }
1811  
1812  /**
1813   * An alias for kprint_r(). Saves carpal tunnel syndrome.
1814   */
1815  function kpr($input, $return = FALSE, $name = NULL) {
1816    return kprint_r($input, $return, $name);
1817  }
1818  
1819  /**
1820   * Like dpr, but uses var_dump() instead
1821   */
1822  function dvr($input, $return = FALSE, $name = NULL) {
1823    return dprint_r($input, $return, $name, 'var_dump', FALSE);
1824  }
1825  
1826  function kprint_r($input, $return = FALSE, $name = NULL, $function = 'print_r') {
1827    // We do not want to krumo() strings and integers and such
1828    if (merits_krumo($input)) {
1829      if (user_access('access devel information')) {
1830        return $return ? (isset($name) ? $name .' => ' : '') . krumo_ob($input) : krumo($input);
1831      }
1832    }
1833    else {
1834      return dprint_r($input, $return, $name, $function);
1835    }
1836  }
1837  
1838  /**
1839   * Pretty-print a variable to the browser (no krumo).
1840   * Displays only for users with proper permissions. If
1841   * you want a string returned instead of a print, use the 2nd param.
1842   */
1843  function dprint_r($input, $return = FALSE, $name = NULL, $function = 'print_r', $check= TRUE) {
1844    if (user_access('access devel information')) {
1845      if ($name) {
1846        $name .= ' => ';
1847      }
1848      ob_start();
1849      $function($input);
1850      $output = ob_get_clean();
1851      if ($check) {
1852        $output = check_plain($output);
1853      }
1854      if (count($input, COUNT_RECURSIVE) > DEVEL_MIN_TEXTAREA) {
1855          // don't use fapi here because sometimes fapi will not be loaded
1856          $printed_value = "<textarea rows=30 style=\"width: 100%;\">\n". $name . $output .'</textarea>';
1857      }
1858      else {
1859        $printed_value = '<pre>'. $name . $output .'</pre>';
1860      }
1861  
1862      if ($return) {
1863        return $printed_value;
1864      }
1865      else {
1866        print $printed_value;
1867      }
1868    }
1869  }
1870  
1871  /**
1872   * Print the function call stack.
1873   */
1874  function ddebug_backtrace() {
1875    if (user_access('access devel information')) {
1876      $trace = debug_backtrace();
1877      array_shift($trace);
1878      foreach ($trace as $key => $value) {
1879        $rich_trace[$value['function']] = $value;
1880      }
1881      if (has_krumo()) {
1882        print krumo($rich_trace);
1883      }
1884      else {
1885        dprint_r($rich_trace);
1886      }
1887    }
1888  }
1889  
1890  /**
1891   * Debugging version of db_query().
1892   *
1893   * Echoes the query to the browser.
1894   */
1895  function db_queryd($query) {
1896    $args = func_get_args();
1897    array_shift($args);
1898    $query = db_prefix_tables($query);
1899    if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
1900      $args = $args[0];
1901    }
1902    _db_query_callback($args, TRUE);
1903    $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
1904    return _db_query($query, 1);
1905  }
1906  
1907  // Only define our mail wrapper if the devel module is the current mail
1908  // wrapper.
1909  if (variable_get('smtp_library', '') == drupal_get_filename('module', 'devel')) {
1910    /**
1911     * Log the mails sent out instead of mailing.
1912     */
1913    function drupal_mail_wrapper($message) {
1914    $mimeheaders = array();
1915    foreach ($message['headers'] as $name => $value) {
1916      // the check_plain nicely encodes <> chars for web presentation
1917      $mimeheaders[] = check_plain($name .': '. mime_header_encode($value));
1918    }
1919    watchdog(
1920      'devel',
1921      'Mail sent:<br />Id: %mail_id<br />To: %to<br />From: %from<br />Language: %lang<br />Subject: %subject<br />Body: %body<br /><br />Additional headers: <br />!header',
1922       array(
1923         '%mail_id' => $message['id'],
1924         '%to' => $message['to'],
1925         '%from' => $message['from'],
1926         '%lang' => $message['language']->language,
1927         '%subject' => $message['subject'],
1928         '%body' => $message['body'],
1929         '!header' => implode("<br />", $mimeheaders),
1930         WATCHDOG_INFO,
1931         )
1932    );
1933    return TRUE;
1934    }
1935  }
1936  
1937  function devel_queries() {
1938    $header = array(
1939                    array('data' => t('Total (ms)'), 'field' => 'total_time', 'sort' => 'desc'),
1940                    array('data' => t('Average (ms)'), 'field' => 'average', 'sort' => 'desc'),
1941                    array('data' => t('Std deviation (ms)')),
1942                    array('data' => t('Count'), 'field' => 'count'),
1943                    array('data' => t('Function'), 'field' => 'q.function'),
1944                    array('data' => t('Query'), 'field' => 'q.query'),
1945                    );
1946  
1947    global $db_type;
1948    if ($db_type == 'pgsql') {
1949      $result = pager_query('SELECT q.qid, q.query, q.function , t.* FROM {devel_queries} q INNER JOIN (SELECT t.qid, COUNT(t.qid) AS count,SUM(t.time) AS total_time, AVG(t.time) AS average, STDDEV(t.time) AS stddev FROM {devel_times} t GROUP BY t.qid) AS t ON t.qid=q.qid '. tablesort_sql($header), 30, 0, 'SELECT COUNT(qid) FROM {devel_queries}');
1950    }
1951    else {
1952      $result = pager_query('SELECT q.qid, q.query, q.function, t.*, COUNT(t.qid) AS count, SUM(t.time) AS total_time, AVG(t.time) AS average, STDDEV(t.time) AS stddev FROM {devel_queries} q INNER JOIN {devel_times} t ON q.qid = t.qid GROUP BY t.qid '. tablesort_sql($header), 30, 0, 'SELECT COUNT(qid) FROM {devel_queries}');
1953    }
1954    while ($log = db_fetch_object($result)) {
1955      $rows[] = array(
1956                      round($log->total_time * 1000, 3),
1957                      round($log->average * 1000, 3),
1958                      round($log->stddev * 1000, 3),
1959                      $log->count,
1960                      $log->function,
1961                      check_plain($log->query)
1962                      );
1963    }
1964  
1965    $output = theme('table', $header, $rows);
1966    $output .= theme('pager', NULL, 30, 0);
1967    $output .= l(t('Delete collected query statistics'), 'devel/queries/empty');
1968  
1969    print theme('page', $output, FALSE);
1970  }
1971  
1972  function devel_queries_empty() {
1973    db_query('DELETE FROM {devel_queries}');
1974    db_query('DELETE FROM {devel_times}');
1975    drupal_set_message(t('Stored query statistics deleted.'));
1976    drupal_goto('devel/queries');
1977  }
1978  
1979  /*
1980   * migration related functions
1981   */
1982  
1983  /**
1984   * Menu callback. Rebuild node _comment_stats table.
1985   *
1986   * @return void
1987   **/
1988  function devel_rebuild_node_comment_statistics_page() {
1989    devel_rebuild_node_comment_statistics();
1990    drupal_set_message('node_comment_statistics table has been rebuilt.');
1991    drupal_goto('admin');
1992  }
1993  
1994  /**
1995   * Regenerate the data in node_comment_statistics table.
1996   *
1997   * @return void
1998   **/
1999  function devel_rebuild_node_comment_statistics() {
2000    // Empty table
2001    $sql = "DELETE FROM {node_comment_statistics}";
2002    db_query($sql);
2003  
2004    $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.nid = c2.nid AND c.timestamp=c2.timestamp)";
2005    db_query($sql, COMMENT_PUBLISHED);
2006  
2007    // Insert 0 count records into the node_comment_statistics for nodes that are missing. See comment_enable()
2008    db_query_temporary("SELECT n.nid, n.created, n.uid FROM {node} n LEFT JOIN {node_comment_statistics} c ON n.nid = c.nid WHERE c.comment_count IS NULL", 'missing_nids');
2009    db_query("INSERT INTO {node_comment_statistics} (nid, last_comment_timestamp, last_comment_name, last_comment_uid, comment_count) SELECT n.nid, n.created, NULL, n.uid, 0 FROM missing_nids n");
2010  }


Generated: Thu Mar 24 11:18:33 2011 Cross-referenced by PHPXref 0.7