You are here

devel.module in Devel 7

This module holds functions useful for Drupal development.

Please contribute!

File

devel.module
View source
<?php

/**
 * @file
 * This module holds functions useful for Drupal development.
 *
 * Please contribute!
 */

// Suggested profiling and stacktrace library from http://www.xdebug.org/index.php
define('DEVEL_QUERY_SORT_BY_SOURCE', 0);
define('DEVEL_QUERY_SORT_BY_DURATION', 1);
define('DEVEL_ERROR_HANDLER_NONE', 0);
define('DEVEL_ERROR_HANDLER_STANDARD', 1);
define('DEVEL_ERROR_HANDLER_BACKTRACE_KRUMO', 2);
define('DEVEL_ERROR_HANDLER_BACKTRACE_DPM', 4);
define('DEVEL_MIN_TEXTAREA', 50);

/**
 * Implements hook_help().
 */
function devel_help($section) {
  switch ($section) {
    case 'devel/reference':
      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 documentation.') . '</p>';
    case 'devel/session':
      return '<p>' . t('Here are the contents of your <code>$_SESSION</code> variable.') . '</p>';
    case 'devel/variable':
      $api = variable_get('devel_api_url', 'api.drupal.org');
      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>';
    case 'devel/reinstall':
      return t('Warning - will delete your module tables and variables.');
  }
}

/**
 * Implements hook_modules_installed().
 *
 * @see devel_install()
 */
function devel_modules_installed($modules) {
  if (in_array('menu', $modules)) {
    $menu = array(
      'menu_name' => 'devel',
      'title' => t('Development'),
      'description' => t('Development link'),
    );
    menu_save($menu);
  }
}

/**
 * Implements hook_menu().
 */
function devel_menu() {

  // Note: we can't dynamically append destination to querystring.
  // Do so at theme layer. Fix in D7?
  $items['devel/cache/clear'] = array(
    'title' => 'Clear cache',
    'page callback' => 'devel_cache_clear',
    'description' => 'Clear the CSS cache and all database cache tables which store page, node, theme and variable caches.',
    'access arguments' => array(
      'access devel information',
    ),
    'file' => 'devel.pages.inc',
    'menu_name' => 'devel',
  );
  $items['devel/reference'] = array(
    'title' => 'Function reference',
    'description' => 'View a list of currently defined user functions with documentation links.',
    'page callback' => 'devel_function_reference',
    'access arguments' => array(
      'access devel information',
    ),
    'file' => 'devel.pages.inc',
    'menu_name' => 'devel',
  );
  $items['devel/reinstall'] = array(
    'title' => 'Reinstall modules',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'devel_reinstall',
    ),
    'description' => 'Run hook_uninstall() and then hook_install() for a given module.',
    'access arguments' => array(
      'access devel information',
    ),
    'file' => 'devel.pages.inc',
    'menu_name' => 'devel',
  );
  $items['devel/menu/reset'] = array(
    'title' => 'Rebuild menus',
    'description' => 'Rebuild menu based on hook_menu() and revert any custom changes. All menu items return to their default settings.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'devel_menu_rebuild',
    ),
    'access arguments' => array(
      'access devel information',
    ),
    'file' => 'devel.pages.inc',
    'menu_name' => 'devel',
  );
  $items['devel/menu/item'] = array(
    'title' => 'Menu item',
    'description' => 'Details about a given menu item.',
    'page callback' => 'devel_menu_item',
    'access arguments' => array(
      'access devel information',
    ),
    'file' => 'devel.pages.inc',
    'menu_name' => 'devel',
  );
  $items['devel/variable'] = array(
    'title' => 'Variable editor',
    'description' => 'Edit and delete site variables.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'devel_variable_form',
    ),
    'access arguments' => array(
      'access devel information',
    ),
    'file' => 'devel.pages.inc',
    'menu_name' => 'devel',
  );

  // We don't want the abbreviated version provided by status report.
  $items['devel/phpinfo'] = array(
    'title' => 'PHPinfo()',
    'description' => 'View your server\'s PHP configuration',
    'page callback' => 'devel_phpinfo',
    'access arguments' => array(
      'access devel information',
    ),
    'file' => 'devel.pages.inc',
    'menu_name' => 'devel',
  );
  $items['devel/php'] = array(
    'title' => 'Execute PHP Code',
    'description' => 'Execute some PHP code',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'devel_execute_form',
    ),
    'access arguments' => array(
      'execute php code',
    ),
    'file' => 'devel.pages.inc',
    'menu_name' => 'devel',
  );
  $items['devel/theme/registry'] = array(
    'title' => 'Theme registry',
    'description' => 'View a list of available theme functions across the whole site.',
    'page callback' => 'devel_theme_registry',
    'access arguments' => array(
      'access devel information',
    ),
    'file' => 'devel.pages.inc',
    'menu_name' => 'devel',
  );
  $items['devel/entity/info'] = array(
    'title' => 'Entity info',
    'description' => 'View entity information across the whole site.',
    'page callback' => 'devel_entity_info_page',
    'access arguments' => array(
      'access devel information',
    ),
    'file' => 'devel.pages.inc',
    'menu_name' => 'devel',
  );
  $items['devel/field/info'] = array(
    'title' => 'Field info',
    'description' => 'View fields information across the whole site.',
    'page callback' => 'devel_field_info_page',
    'access arguments' => array(
      'access devel information',
    ),
    'file' => 'devel.pages.inc',
    'menu_name' => 'devel',
  );
  $items['devel/elements'] = array(
    'title' => 'Hook_elements()',
    'description' => 'View the active form/render elements for this site.',
    'page callback' => 'devel_elements_page',
    'access arguments' => array(
      'access devel information',
    ),
    'file' => 'devel.pages.inc',
    'menu_name' => 'devel',
  );
  $items['devel/variable/edit/%'] = array(
    'title' => 'Variable editor',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'devel_variable_edit',
      3,
    ),
    'access arguments' => array(
      'access devel information',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'devel.pages.inc',
    'menu_name' => 'devel',
  );
  $items['devel/session'] = array(
    'title' => 'Session viewer',
    'description' => 'List the contents of $_SESSION.',
    'page callback' => 'devel_session',
    'access arguments' => array(
      'access devel information',
    ),
    'file' => 'devel.pages.inc',
    'menu_name' => 'devel',
  );
  $items['devel/switch'] = array(
    'title' => 'Switch user',
    'page callback' => 'devel_switch_user',
    'access callback' => '_devel_switch_user_access',
    'access arguments' => array(
      2,
    ),
    'type' => MENU_CALLBACK,
    'file' => 'devel.pages.inc',
    'menu_name' => 'devel',
  );
  $items['devel/explain'] = array(
    'title' => 'Explain query',
    'page callback' => 'devel_querylog_explain',
    'description' => 'Run an EXPLAIN on a given query. Used by query log',
    'access arguments' => array(
      'access devel information',
    ),
    'file' => 'devel.pages.inc',
    'type' => MENU_CALLBACK,
  );
  $items['devel/arguments'] = array(
    'title' => 'Arguments query',
    'page callback' => 'devel_querylog_arguments',
    'description' => 'Return a given query, with arguments instead of placeholders. Used by query log',
    'access arguments' => array(
      'access devel information',
    ),
    'file' => 'devel.pages.inc',
    'type' => MENU_CALLBACK,
  );
  $items['devel/run-cron'] = array(
    'title' => 'Run cron',
    'page callback' => 'system_run_cron',
    'access arguments' => array(
      'administer site configuration',
    ),
    'file' => 'system.admin.inc',
    'file path' => drupal_get_path('module', 'system'),
    'menu_name' => 'devel',
  );

  // Duplicate path in 2 different menus. See http://drupal.org/node/601788.
  $items['devel/settings'] = array(
    'title' => 'Devel settings',
    'description' => 'Helper functions, pages, and blocks to assist Drupal developers. The devel blocks can be managed via the <a href="/admin/structure/block">block administration</a> page.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'devel_admin_settings',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'file' => 'devel.admin.inc',
    'menu_name' => 'devel',
  );
  $items['admin/config/development/devel'] = array(
    'title' => 'Devel settings',
    'description' => 'Helper functions, pages, and blocks to assist Drupal developers. The devel blocks can be managed via the <a href="/admin/structure/block">block administration</a> page.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'devel_admin_settings',
    ),
    'file' => 'devel.admin.inc',
    'access arguments' => array(
      'administer site configuration',
    ),
  );
  $items['node/%node/devel'] = array(
    'title' => 'Devel',
    'page callback' => 'devel_load_object',
    'page arguments' => array(
      'node',
      1,
    ),
    'access arguments' => array(
      'access devel information',
    ),
    'type' => MENU_LOCAL_TASK,
    'file' => 'devel.pages.inc',
    'weight' => 100,
  );
  $items['node/%node/devel/load'] = array(
    'title' => 'Load',
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );
  $items['node/%node/devel/render'] = array(
    'title' => 'Render',
    'page callback' => 'devel_render_object',
    'page arguments' => array(
      'node',
      1,
    ),
    'access arguments' => array(
      'access devel information',
    ),
    'file' => 'devel.pages.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => 100,
  );
  $items['comment/%comment/devel'] = array(
    'title' => 'Devel',
    'page callback' => 'devel_load_object',
    'page arguments' => array(
      'comment',
      1,
    ),
    'access arguments' => array(
      'access devel information',
    ),
    'type' => MENU_LOCAL_TASK,
    'file' => 'devel.pages.inc',
    'weight' => 100,
  );
  $items['comment/%comment/devel/load'] = array(
    'title' => 'Load',
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );
  $items['comment/%comment/devel/render'] = array(
    'title' => 'Render',
    'page callback' => 'devel_render_object',
    'page arguments' => array(
      'comment',
      1,
    ),
    'access arguments' => array(
      'access devel information',
    ),
    'type' => MENU_LOCAL_TASK,
    'file' => 'devel.pages.inc',
    'weight' => 100,
  );
  $items['user/%user/devel'] = array(
    'title' => 'Devel',
    'page callback' => 'devel_load_object',
    'page arguments' => array(
      'user',
      1,
    ),
    'access arguments' => array(
      'access devel information',
    ),
    'type' => MENU_LOCAL_TASK,
    'file' => 'devel.pages.inc',
    'weight' => 100,
  );
  $items['user/%user/devel/load'] = array(
    'title' => 'Load',
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );
  $items['user/%user/devel/render'] = array(
    'title' => 'Render',
    'page callback' => 'devel_render_object',
    'page arguments' => array(
      'user',
      1,
    ),
    'access arguments' => array(
      'access devel information',
    ),
    'file' => 'devel.pages.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => 100,
  );
  $items['taxonomy/term/%taxonomy_term/devel'] = array(
    'title' => 'Devel',
    'page callback' => 'devel_load_object',
    'page arguments' => array(
      'taxonomy_term',
      2,
      'term',
    ),
    'access arguments' => array(
      'access devel information',
    ),
    'file' => 'devel.pages.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => 100,
  );
  $items['taxonomy/term/%taxonomy_term/devel/load'] = array(
    'title' => 'Load',
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );
  $items['taxonomy/term/%taxonomy_term/devel/render'] = array(
    'title' => 'Render',
    'page callback' => 'devel_render_object',
    'page arguments' => array(
      'taxonomy_term',
      2,
      'term',
    ),
    'access arguments' => array(
      'access devel information',
    ),
    'type' => MENU_LOCAL_TASK,
    'file' => 'devel.pages.inc',
    'weight' => 100,
  );
  return $items;
}

/**
 * Menu item access callback: checks permission and token for Switch User.
 */
function _devel_switch_user_access($name) {

  // Suppress notices when on other pages when menu system still checks access.
  return user_access('switch users') && drupal_valid_token(@$_GET['token'], "devel/switch/{$name}", TRUE);
}

/**
 * Implements hook_admin_paths().
 */
function devel_admin_paths() {
  $paths = array(
    'devel/*' => TRUE,
    'node/*/devel' => TRUE,
    'node/*/devel/*' => TRUE,
    'comment/*/devel' => TRUE,
    'comment/*/devel/*' => TRUE,
    'user/*/devel' => TRUE,
    'user/*/devel/*' => TRUE,
    'taxonomy/term/*/devel' => TRUE,
    'taxonomy/term/*/devel/*' => TRUE,
  );
  return $paths;
}

/**
 * Returns paths that need a destination.
 */
function devel_menu_need_destination() {
  return array(
    'devel/cache/clear',
    'devel/reinstall',
    'devel/menu/reset',
    'devel/variable',
    'admin/reports/status/run-cron',
  );
}

/**
 * Returns list of paths which need CSRF token protection.
 *
 * @return array
 *   An associative array in which every item is composed in the following way:
 *   - key: path which need token protection.
 *   - value: additional value used for generate the token.
 */
function devel_menu_need_token_protection() {
  return array(
    'devel/cache/clear' => 'devel-cache-clear',
    'devel/run-cron' => 'run-cron',
  );
}

/**
 * Implements hook_menu_link_alter().
 *
 * Flag this link as needing alter at display time.
 * This is more robust than setting alter in hook_menu().
 *
 * @see devel_translated_menu_link_alter()
 */
function devel_menu_link_alter(&$item) {
  if (in_array($item['link_path'], devel_menu_need_destination()) || array_key_exists($item['link_path'], devel_menu_need_token_protection()) || $item['link_path'] == 'devel/menu/item') {
    $item['options']['alter'] = TRUE;
  }
}

/**
 * Implements hook_translated_menu_item_alter().
 *
 * Append dynamic querystring 'destination' or 'token' (csfr protection) to
 * several of our own menu items.
 */
function devel_translated_menu_link_alter(&$item) {
  $need_destination = in_array($item['href'], devel_menu_need_destination());
  $token_protection = devel_menu_need_token_protection();
  $need_token = array_key_exists($item['href'], $token_protection);
  if ($need_destination || $need_token) {
    if ($need_destination) {
      $item['localized_options']['query'] = drupal_get_destination();
    }
    if ($need_token) {
      $item['localized_options']['query']['token'] = drupal_get_token($token_protection[$item['href']]);
    }
  }
  elseif ($item['href'] == 'devel/menu/item') {
    $item['localized_options']['query'] = array(
      'path' => $_GET['q'],
    );
  }
}

/**
 * Implements hook_theme().
 */
function devel_theme() {
  return array(
    'devel_querylog' => array(
      'variables' => array(
        'header' => array(),
        'rows' => array(),
      ),
    ),
    'devel_querylog_row' => array(
      'variables' => array(
        'row' => array(),
      ),
    ),
  );
}

/**
 * Implements hook_init().
 */
function devel_init() {
  if (!devel_silent()) {
    if (user_access('access devel information')) {
      devel_set_handler(devel_get_handlers());

      // We want to include the class early so that anyone may call krumo()
      // as needed. See http://krumo.sourceforge.net/
      has_krumo();

      // See http://www.firephp.org/HQ/Install.htm
      $path = NULL;
      if (@(include_once 'fb.php') || @(include_once 'FirePHPCore/fb.php')) {

        // FirePHPCore is in include_path. Probably a PEAR installation.
        $path = '';
      }
      elseif (module_exists('libraries')) {

        // Support Libraries API - http://drupal.org/project/libraries
        $firephp_path = libraries_get_path('FirePHPCore');
        $firephp_path = $firephp_path ? $firephp_path . '/lib/FirePHPCore/' : '';
        $chromephp_path = libraries_get_path('chromephp');
      }
      else {
        $firephp_path = './' . drupal_get_path('module', 'devel') . '/FirePHPCore/lib/FirePHPCore/';
        $chromephp_path = './' . drupal_get_path('module', 'devel') . '/chromephp';
      }

      // Include FirePHP if it exists.
      if (!empty($firephp_path) && file_exists($firephp_path . 'fb.php')) {
        include_once $firephp_path . 'fb.php';
        include_once $firephp_path . 'FirePHP.class.php';
      }

      // Include ChromePHP if it exists.
      if (!empty($chromephp_path) && file_exists($chromephp_path .= '/ChromePhp.php')) {
        include_once $chromephp_path;
      }

      // Add CSS for query log if should be displayed.
      if (variable_get('devel_query_display', 0)) {
        drupal_add_css(drupal_get_path('module', 'devel') . '/devel.css');
        drupal_add_js(drupal_get_path('module', 'devel') . '/devel.js');
      }
    }
  }
  if (variable_get('devel_rebuild_theme_registry', FALSE)) {
    drupal_theme_rebuild();
    if (flood_is_allowed('devel_rebuild_registry_warning', 1)) {
      flood_register_event('devel_rebuild_registry_warning');
      if (!devel_silent() && user_access('access devel information')) {
        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', array(
            'fragment' => 'edit-devel-rebuild-theme-registry',
          )),
        )));
      }
    }
  }
}

/**
 * Sets a message.
 */
function devel_set_message($msg, $type = NULL) {
  $function = function_exists('drush_log') ? 'drush_log' : 'drupal_set_message';
  $function($msg, $type);
}

/**
 * Determines if Krumo is available.
 *
 * No need for cache here.
 *
 * @return boolean
 *   TRUE if Krumo is available, FALSE otherwise.
 */
function has_krumo() {
  @(include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'devel') . '/krumo/class.krumo.php');
  if (function_exists('krumo') && !drupal_is_cli()) {
    drupal_add_js(drupal_get_path('module', 'devel') . '/devel_krumo.js');
    drupal_add_css(drupal_get_path('module', 'devel') . '/devel_krumo.css');

    // If dpm() is called after status messages have been rendered they will
    // instead appear on the next page load, so ensure the krumo CSS and
    // Javascript files are included.
    krumo::addCssJs();
    return TRUE;
  }
  return FALSE;
}

/**
 * Decides whether or not to print a debug variable using krumo().
 *
 * @param array|object $input
 *   The value to check.
 *
 * @return boolean
 *   TRUE if the input complex enough and Krumo is available and not disabled,
 *   FALSE otherwise.
 */
function merits_krumo($input) {
  return (is_object($input) || is_array($input)) && has_krumo() && variable_get('devel_krumo_skin', '') != 'disabled';
}

/**
 * Calls the fb() function if it is found.
 *
 * @see http://www.firephp.org/
 */
function dfb() {
  if (function_exists('fb') && user_access('access devel information') && !headers_sent()) {
    $args = func_get_args();
    call_user_func_array('fb', $args);
  }
}

/**
 * Calls dfb() to output a backtrace.
 */
function dfbt($label) {
  dfb($label, FirePHP::TRACE);
}

/**
 * Wrapper for ChromePHP Class log method.
 */
function dcp() {
  if (class_exists('ChromePhp', FALSE) && user_access('access devel information')) {
    $args = func_get_args();
    call_user_func_array(array(
      'ChromePhp',
      'log',
    ), $args);
  }
}

/**
 * Implements hook_watchdog().
 */
function devel_watchdog(array $log_entry) {
  if (class_exists('FirePHP', FALSE) && !drupal_is_cli()) {
    switch ($log_entry['severity']) {
      case WATCHDOG_EMERGENCY:
      case WATCHDOG_ALERT:
      case WATCHDOG_CRITICAL:
      case WATCHDOG_ERROR:
        $type = FirePHP::ERROR;
        break;
      case WATCHDOG_WARNING:
        $type = FirePHP::WARN;
        break;
      case WATCHDOG_NOTICE:
      case WATCHDOG_INFO:
        $type = FirePHP::INFO;
        break;
      case WATCHDOG_DEBUG:
      default:
        $type = FirePHP::LOG;
    }
  }
  else {
    $type = 'watchdog';
  }
  $function = function_exists('decode_entities') ? 'decode_entities' : 'html_entity_decode';
  $watchdog = array(
    'type' => $log_entry['type'],
    'message' => $function(strtr($log_entry['message'], (array) $log_entry['variables'])),
  );
  if (isset($log_entry['link'])) {
    $watchdog['link'] = $log_entry['link'];
  }
  dfb($watchdog, $type);
}

/**
 * Gets error handlers.
 *
 * @return array
 */
function devel_get_handlers() {
  $error_handlers = variable_get('devel_error_handlers', array(
    DEVEL_ERROR_HANDLER_STANDARD => DEVEL_ERROR_HANDLER_STANDARD,
  ));
  if (!empty($error_handlers)) {
    unset($error_handlers[DEVEL_ERROR_HANDLER_NONE]);
  }
  return $error_handlers;
}

/**
 * Sets a new error handler or restores the prior one.
 *
 * @param array $handlers
 *   An array of error handlers to set.  Use an empty array to restore the
 *   previous one.
 */
function devel_set_handler($handlers) {
  if (empty($handlers)) {
    restore_error_handler();
  }
  elseif (count($handlers) == 1 && isset($handlers[DEVEL_ERROR_HANDLER_STANDARD])) {

    // Do nothing.
  }
  else {
    if (has_krumo()) {
      set_error_handler('backtrace_error_handler');
    }
  }
}

/**
 * Checks whether Devel may be active.
 *
 * @return boolean
 */
function devel_silent() {

  // isset($_GET['q']) is needed when calling the front page. q is not set.
  // Don't interfere with private files/images.
  return function_exists('drupal_is_cli') && drupal_is_cli() || isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'ApacheBench') !== FALSE || !empty($_REQUEST['XDEBUG_PROFILE']) || isset($GLOBALS['devel_shutdown']) || strstr($_SERVER['PHP_SELF'], 'update.php') || isset($_GET['q']) && (in_array($_GET['q'], array(
    'admin/content/node-settings/rebuild',
  )) || substr($_GET['q'], 0, strlen('system/files')) == 'system/files' || substr($_GET['q'], 0, strlen('batch')) == 'batch' || substr($_GET['q'], 0, strlen('file/ajax')) == 'file/ajax');
}

/**
 * Implements hook_boot().
 *
 * Runs even for cached pages.
 */
function devel_boot() {
  if (!devel_silent()) {
    if (variable_get('devel_memory', 0)) {
      global $memory_init;
      $memory_init = memory_get_usage();
    }
    if (devel_query_enabled()) {
      @(include_once DRUPAL_ROOT . '/includes/database/log.inc');
      Database::startLog('devel');
    }
  }

  // We need user_access() in the shutdown function. make sure it gets loaded.
  // Also prime the drupal_get_filename() static with user.module's location to
  // avoid a stray query.
  drupal_get_filename('module', 'user', 'modules/user/user.module');
  drupal_load('module', 'user');
  drupal_register_shutdown_function('devel_shutdown');
}

/**
 * Displays backtrace showing the route of calls to the current error.
 *
 * @param int $error_level
 *   The level of the error raised.
 * @param string $message
 *   The error message.
 * @param string $filename
 *   The filename that the error was raised in.
 * @param int $line
 *   The line number the error was raised at.
 * @param array $context
 *   An array that points to the active symbol table at the point the error
 *   occurred.
 */
function backtrace_error_handler($error_level, $message, $filename, $line, $context) {

  // Hide stack trace and parameters from unqualified users.
  if (!user_access('access devel information')) {

    // Do what core does in bootstrap.inc and errors.inc.
    // (We need to duplicate the core code here rather than calling it
    // to avoid having the backtrace_error_handler() on top of the call stack.)
    require_once DRUPAL_ROOT . '/includes/errors.inc';
    if ($error_level & error_reporting()) {
      $types = drupal_error_levels();
      list($severity_msg, $severity_level) = $types[$error_level];
      $backtrace = debug_backtrace();
      $caller = _drupal_get_last_caller($backtrace);
      if (!function_exists('filter_xss_admin')) {
        require_once DRUPAL_ROOT . '/includes/common.inc';
      }

      // We treat recoverable errors as fatal.
      _drupal_log_error(array(
        '%type' => isset($types[$error_level]) ? $severity_msg : 'Unknown error',
        // The standard PHP error handler considers that the error messages
        // are HTML. We mimick this behavior here.
        '!message' => filter_xss_admin($message),
        '%function' => $caller['function'],
        '%file' => $caller['file'],
        '%line' => $caller['line'],
        'severity_level' => $severity_level,
      ), $error_level == E_RECOVERABLE_ERROR);
    }
    return;
  }

  // Don't respond to the error if it was suppressed with a '@'
  if (error_reporting() == 0) {
    return;
  }

  // Don't respond to warning caused by ourselves.
  if (preg_match('#Cannot modify header information - headers already sent by \\([^\\)]*[/\\\\]devel[/\\\\]#', $message)) {
    return;
  }
  if ($error_level & error_reporting()) {

    // Only write each distinct NOTICE message once, as repeats do not give any
    // further information and can choke the page output.
    if ($error_level == E_NOTICE) {
      static $written = array();
      if (!empty($written[$line][$filename][$message])) {
        return;
      }
      $written[$line][$filename][$message] = TRUE;
    }
    require_once DRUPAL_ROOT . '/includes/errors.inc';
    $types = drupal_error_levels();
    $type = $types[$error_level];
    $backtrace = debug_backtrace();
    $variables = array(
      '%error' => $type[0],
      '%message' => $message,
      '%function' => $backtrace[1]['function'] . '()',
      '%file' => $filename,
      '%line' => $line,
    );
    $msg = t('%error: %message in %function (line %line of %file).', $variables);

    // Show message if error_level is ERROR_REPORTING_DISPLAY_SOME or higher.
    // (This is Drupal's error_level, which is different from $error_level,
    // and we purposely ignore the difference between _SOME and _ALL,
    // see #970688!)
    if (variable_get('error_level', 1) >= 1) {
      $error_handlers = devel_get_handlers();
      if (!empty($error_handlers[DEVEL_ERROR_HANDLER_STANDARD])) {
        drupal_set_message($msg, $type[1] <= WATCHDOG_ERROR ? 'error' : 'warning');
      }
      if (!empty($error_handlers[DEVEL_ERROR_HANDLER_BACKTRACE_KRUMO])) {
        print $msg . " =&gt;\n";
        ddebug_backtrace(FALSE, 1);
      }
      if (!empty($error_handlers[DEVEL_ERROR_HANDLER_BACKTRACE_DPM])) {
        dpm(ddebug_backtrace(TRUE, 1), $msg, 'warning');
      }
    }
    $watchdog = 'watchdog';
    $watchdog('php', $msg, NULL, $type[1]);
  }
}

/**
 * Implements hook_permission().
 */
function devel_permission() {
  return array(
    'access devel information' => array(
      'description' => t('View developer output like variable printouts, query log, etc.'),
      'title' => t('Access developer information'),
      'restrict access' => TRUE,
    ),
    'execute php code' => array(
      'title' => t('Execute PHP code'),
      'description' => t('Run arbitrary PHP from a block.'),
      'restrict access' => TRUE,
    ),
    'switch users' => array(
      'title' => t('Switch users'),
      'description' => t('Become any user on the site with just a click.'),
      'restrict access' => TRUE,
    ),
  );
}

/**
 * Implements hook_block_info().
 */
function devel_block_info() {
  $blocks['execute_php'] = array(
    'info' => t('Execute PHP'),
    'cache' => DRUPAL_NO_CACHE,
  );
  $blocks['switch_user'] = array(
    'info' => t('Switch user'),
    'cache' => DRUPAL_NO_CACHE,
  );
  return $blocks;
}

/**
 * Implements hook_block_configure().
 */
function devel_block_configure($delta) {
  if ($delta == 'switch_user') {
    $form['list_size'] = array(
      '#type' => 'textfield',
      '#title' => t('Number of users to display in the list'),
      '#default_value' => variable_get('devel_switch_user_list_size', 10),
      '#size' => '3',
      '#maxlength' => '4',
    );
    $form['include_anon'] = array(
      '#type' => 'checkbox',
      '#title' => t('Include %anonymous', array(
        '%anonymous' => format_username(drupal_anonymous_user()),
      )),
      '#default_value' => variable_get('devel_switch_user_include_anon', FALSE),
    );
    $form['show_form'] = array(
      '#type' => 'checkbox',
      '#title' => t('Allow entering any user name'),
      '#default_value' => variable_get('devel_switch_user_show_form', TRUE),
    );
    return $form;
  }
}

/**
 * Implements hook_block_save().
 */
function devel_block_save($delta, $edit = array()) {
  if ($delta == 'switch_user') {
    variable_set('devel_switch_user_list_size', $edit['list_size']);
    variable_set('devel_switch_user_include_anon', $edit['include_anon']);
    variable_set('devel_switch_user_show_form', $edit['show_form']);
  }
}

/**
 * Implements hook_block_view().
 */
function devel_block_view($delta) {
  $block = array();
  switch ($delta) {
    case 'switch_user':
      $block = devel_block_switch_user();
      break;
    case 'execute_php':
      if (user_access('execute php code')) {
        $block['content'] = drupal_get_form('devel_execute_block_form');
      }
      break;
  }
  return $block;
}

/**
 * Provides the Switch user block.
 *
 * @return array
 *   A render array containing the Switch user block.
 */
function devel_block_switch_user() {
  $links = devel_switch_user_list();
  if (!empty($links) || user_access('switch users')) {
    drupal_add_css(drupal_get_path('module', 'devel') . '/devel.css');
    $block['subject'] = t('Switch user');
    $build['devel_links'] = array(
      '#theme' => 'links',
      '#links' => $links,
    );
    if (variable_get('devel_switch_user_show_form', TRUE)) {
      $build['devel_form'] = drupal_get_form('devel_switch_user_form');
    }
    $block['content'] = $build;
    return $block;
  }
}

/**
 * Provides the Switch user list.
 *
 * @return array
 *   An array of links, each of which can be used to switch to a user.
 */
function devel_switch_user_list() {
  global $user;
  $links = array();
  if (user_access('switch users')) {
    $list_size = variable_get('devel_switch_user_list_size', 10);
    if ($include_anon = variable_get('devel_switch_user_include_anon', FALSE)) {
      --$list_size;
    }
    $dest = drupal_get_destination();

    // Try to find at least $list_size users that can switch.
    // Inactive users are omitted from all of the following db selects.
    $roles = user_roles(TRUE, 'switch users');
    $query = db_select('users', 'u');
    $query
      ->addField('u', 'uid');
    $query
      ->addField('u', 'access');
    $query
      ->distinct();
    $query
      ->condition('u.uid', 0, '>');
    $query
      ->condition('u.status', 0, '>');
    $query
      ->orderBy('u.access', 'DESC');
    $query
      ->range(0, $list_size);
    if (!isset($roles[DRUPAL_AUTHENTICATED_RID])) {
      $query
        ->leftJoin('users_roles', 'r', 'u.uid = r.uid');
      $or_condition = db_or();
      $or_condition
        ->condition('u.uid', 1);
      if (!empty($roles)) {
        $or_condition
          ->condition('r.rid', array_keys($roles), 'IN');
      }
      $query
        ->condition($or_condition);
    }
    $uids = $query
      ->execute()
      ->fetchCol();
    $accounts = user_load_multiple($uids);
    foreach ($accounts as $account) {
      $path = 'devel/switch/' . $account->name;
      $links[$account->uid] = array(
        'title' => drupal_placeholder(format_username($account)),
        'href' => $path,
        'query' => $dest + array(
          'token' => drupal_get_token($path),
        ),
        'attributes' => array(
          'title' => t('This user can switch back.'),
        ),
        'html' => TRUE,
        'last_access' => $account->access,
      );
    }
    $num_links = count($links);
    if ($num_links < $list_size) {

      // If we don't have enough, add distinct uids until we hit $list_size.
      $uids = db_query_range('SELECT uid FROM {users} WHERE uid > 0 AND uid NOT IN (:uids) AND status > 0 ORDER BY access DESC', 0, $list_size - $num_links, array(
        ':uids' => array_keys($links),
      ))
        ->fetchCol();
      $accounts = user_load_multiple($uids);
      foreach ($accounts as $account) {
        $path = 'devel/switch/' . $account->name;
        $links[$account->uid] = array(
          'title' => format_username($account),
          'href' => $path,
          'query' => array(
            'token' => drupal_get_token($path),
          ),
          'attributes' => array(
            'title' => t('Caution: this user will be unable to switch back.'),
          ),
          'last_access' => $account->access,
        );
      }
      uasort($links, '_devel_switch_user_list_cmp');
    }
    if ($include_anon) {
      $path = 'devel/switch';
      $link = array(
        'title' => format_username(drupal_anonymous_user()),
        'href' => $path,
        'query' => $dest + array(
          'token' => drupal_get_token($path),
        ),
        'attributes' => array(
          'title' => t('Caution: the anonymous user will be unable to switch back.'),
        ),
      );
      if (user_access('switch users', drupal_anonymous_user())) {
        $link['title'] = drupal_placeholder($link['title']);
        $link['attributes'] = array(
          'title' => t('This user can switch back.'),
        );
        $link['html'] = TRUE;
      }
      $links[] = $link;
    }
  }
  if (array_key_exists($user->uid, $links)) {
    $links[$user->uid]['title'] = '<strong>' . $links[$user->uid]['title'] . '</strong>';
  }
  return $links;
}

/**
 * Comparison helper function for uasort() in devel_switch_user_list().
 *
 * Sorts the Switch User links by the user's last access timestamp.
 */
function _devel_switch_user_list_cmp($a, $b) {
  return $b['last_access'] - $a['last_access'];
}

/**
 * Provides the Switch user form.
 */
function devel_switch_user_form() {
  $form['username'] = array(
    '#type' => 'textfield',
    '#description' => t('Enter username'),
    '#autocomplete_path' => 'user/autocomplete',
    '#maxlength' => USERNAME_MAX_LENGTH,
    '#size' => 16,
  );
  $form['actions'] = array(
    '#type' => 'actions',
  );
  $form['actions']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Switch'),
  );
  $form['#attributes'] = array(
    'class' => array(
      'clearfix',
    ),
  );
  return $form;
}

/**
 * Provides the devel docs form.
 */
function devel_doc_function_form() {
  $version = devel_get_core_version(VERSION);
  $form['function'] = array(
    '#type' => 'textfield',
    '#description' => t('Enter function name for api lookup'),
    '#size' => 16,
    '#maxlength' => 255,
  );
  $form['version'] = array(
    '#type' => 'value',
    '#value' => $version,
  );
  $form['actions'] = array(
    '#type' => 'actions',
  );
  $form['actions']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
  );
  return $form;
}

/**
 * Submit handler for the API lookup form.
 */
function devel_doc_function_form_submit($form, &$form_state) {
  $version = $form_state['values']['version'];
  $function = $form_state['values']['function'];
  $api = variable_get('devel_api_url', 'api.drupal.org');
  $form_state['redirect'] = "http://{$api}/api/function/{$function}/{$version}";
}

/**
 * Validate handler for the Switch user form.
 */
function devel_switch_user_form_validate($form, &$form_state) {
  if (!($account = user_load_by_name($form_state['values']['username']))) {
    form_set_error('username', t('Username not found'));
  }
}

/**
 * Submit handler for the Switch user form.
 */
function devel_switch_user_form_submit($form, &$form_state) {
  $path = 'devel/switch/' . $form_state['values']['username'];
  $form_state['redirect'] = array(
    $path,
    array(
      'query' => array(
        'destination' => '',
        'token' => drupal_get_token($path),
      ),
    ),
  );
}

/**
 * Implements hook_drupal_goto_alter().
 */
function devel_drupal_goto_alter($path, $options, $http_response_code) {
  global $user;
  if (isset($path) && !devel_silent()) {

    // The page we are leaving is a drupal_goto(). Present a redirection page
    // so that the developer can see the intermediate query log.
    // We don't want to load user module here, so keep function_exists() call.
    if (isset($user) && function_exists('user_access') && user_access('access devel information') && variable_get('devel_redirect_page', 0)) {
      $destination = function_exists('url') ? url($path, $options) : $path;
      $output = t_safe('<p>The user is being redirected to <a href="@destination">@destination</a>.</p>', array(
        '@destination' => $destination,
      ));
      drupal_deliver_page($output);

      // Don't allow the automatic redirect to happen.
      exit;
    }
    else {
      $GLOBALS['devel_redirecting'] = TRUE;
    }
  }
}

/**
 * Implements hook_library_alter().
 */
function devel_library_alter(&$libraries, $module) {

  // Use an uncompressed version of jQuery for debugging.
  if ($module === 'system' && variable_get('devel_use_uncompressed_jquery', FALSE) && isset($libraries['jquery'])) {

    // Make sure we're not changing the jQuery version used on the site.
    if (version_compare($libraries['jquery']['version'], '1.4.4', '=')) {
      $libraries['jquery']['js'] = array(
        drupal_get_path('module', 'devel') . '/jquery-1.4.4-uncompressed.js' => array(
          'weight' => JS_LIBRARY - 20,
        ),
      );
    }
    else {
      if (!devel_silent() && user_access('access devel information')) {
        drupal_set_message(t('jQuery could not be replaced with an uncompressed version of 1.4.4, because jQuery @version is running on the site.', array(
          '@version' => $libraries['jquery']['version'],
        )));
      }
    }
  }
}

/**
 * Runs on shutdown to clean up and display developer information.
 *
 * devel_boot() registers this function as a shutdown function.
 * The bulk of the work is done in devel_shutdown_real().
 */
function devel_shutdown() {

  // Register the real shutdown function so it runs after other shutdown
  // functions.
  drupal_register_shutdown_function('devel_shutdown_real');
}

/**
 * Implements hook_page_alter().
 */
function devel_page_alter($page) {
  if (variable_get('devel_page_alter', FALSE) && user_access('access devel information')) {
    dpm($page, 'page');
  }
}

/**
 * Implements hook_ajax_render_alter().
 *
 * Disables our footer stuff based on ajax response.
 *
 * AJAX render reponses sometimes are sent as text/html. We have to catch them
 * here and disable our footer stuff.
 */
function devel_ajax_render_alter() {
  $GLOBALS['devel_shutdown'] = FALSE;
}

/**
 * Runs on shutdown to display developer information in the footer.
 *
 * devel_shutdown() registers this function as a shutdown function.
 */
function devel_shutdown_real() {
  global $user;
  $output = $txt = '';

  // Set $GLOBALS['devel_shutdown'] = FALSE in order to supress the
  // devel footer for a page.  Not necessary if your page outputs any
  // of the Content-type http headers tested below (e.g. text/xml,
  // text/javascript, etc).  This is advised where applicable.
  if (!devel_silent() && !isset($GLOBALS['devel_shutdown']) && !isset($GLOBALS['devel_redirecting'])) {

    // Try not to break non html pages.
    if (function_exists('drupal_get_http_header')) {
      $header = drupal_get_http_header('content-type');
      if ($header) {
        $formats = array(
          'xml',
          'javascript',
          'json',
          'plain',
          'image',
          'application',
          'csv',
          'x-comma-separated-values',
        );
        foreach ($formats as $format) {
          if (strstr($header, $format)) {
            return;
          }
        }
      }
    }
    if (isset($user) && user_access('access devel information')) {
      $queries = devel_query_enabled() ? Database::getLog('devel', 'default') : NULL;
      if (!empty($queries)) {

        // Remove caller args to avoid recursion.
        foreach ($queries as &$query) {
          unset($query['caller']['args']);
        }
      }
      $output .= devel_shutdown_summary($queries);
      $output .= devel_shutdown_query($queries);
    }
    if ($output) {

      // TODO: gzip this text if we are sending a gzip page.
      // See drupal_page_header().
      // For some reason, this is not actually printing for cached pages even
      // though it gets executed and $output looks good.
      print $output;
    }
  }
}

/**
 * Returns the rendered shutdown summary.
 *
 * @return string
 */
function devel_shutdown_summary($queries) {
  $sum = 0;
  $output = '';
  list($counts, $query_summary) = devel_query_summary($queries);
  if (variable_get('devel_query_display', FALSE)) {

    // Query log on.
    $output .= $query_summary;
    $output .= t_safe(' Queries exceeding @threshold ms are <span class="marker">highlighted</span>.', array(
      '@threshold' => variable_get('devel_execution', 5),
    ));
  }
  if (variable_get('devel_timer', 0)) {
    $output .= devel_timer();
  }
  $output .= devel_shutdown_memory();
  if ($output) {
    return '<div class="dev-query">' . $output . '</div>';
  }
}

/**
 * Returns the rendered memory usage.
 *
 * @return string
 */
function devel_shutdown_memory() {
  global $memory_init;
  if (variable_get('devel_memory', FALSE)) {
    $memory_shutdown = memory_get_usage();
    $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),
    );
    $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>';

    // theme() may not be available. not t() either.
    return t_safe($msg, $args);
  }
}

/**
 * Returns the rendered query log.
 *
 * @return string
 */
function devel_shutdown_query($queries) {
  if (!empty($queries)) {
    if (function_exists('theme_get_registry') && theme_get_registry()) {

      // Safe to call theme('table).
      list($counts, $query_summary) = devel_query_summary($queries);
      $output = devel_query_table($queries, $counts);

      // Save all queries to a file in temp dir. Retrieved via AJAX.
      devel_query_put_contents($queries);
    }
    else {
      $output = '</div>' . dprint_r($queries, TRUE);
    }
    return $output;
  }
}

/**
 * Writes the variables information to a file.
 *
 * It will be retrieved on demand via AJAX.
 */
function devel_query_put_contents($queries) {
  $request_id = mt_rand(1, 1000000);
  $path = "temporary://devel_querylog";

  // Create the devel_querylog within the temp folder, if needed.
  file_prepare_directory($path, FILE_CREATE_DIRECTORY);

  // Occassionally wipe the querylog dir so that files don't accumulate.
  $range = variable_get('devel_query_random_range', 1000);
  if (mt_rand(1, $range) == 1) {
    devel_empty_dir($path);
  }
  $path .= "/{$request_id}.txt";
  $path = file_stream_wrapper_uri_normalize($path);

  // Save queries as a json array. Suppress errors due to recursion ()
  file_put_contents($path, @json_encode($queries));
  $settings['devel'] = array(
    // A random string that is sent to the browser.
    // It enables the AJAX to retrieve queries from this request.
    'request_id' => $request_id,
  );
  print '<script type="text/javascript">jQuery.extend(Drupal.settings, ' . json_encode($settings) . ");</script>\n";
}

/**
 * Returns whether query logging is enabled.
 *
 * @return boolean
 *   TRUE if the query log is enabled, FALSE otherwise.
 */
function devel_query_enabled() {
  return method_exists('Database', 'getLog') && variable_get('devel_query_display', FALSE);
}

/**
 * Returns the query summary.
 *
 * @return array
 *   An index array containing:
 *   0: The an associative array where the keys are the queries and the values
 *      are number of times that query was executes.
 *   1: The summary.
 *   2: An associative array of substitution variables to be applied in the
 *      previous element.
 */
function devel_query_summary($queries) {
  if (variable_get('devel_query_display', FALSE) && is_array($queries)) {
    $sum = 0;
    foreach ($queries as $query) {
      $text[] = $query['query'];
      $sum += $query['time'];
    }
    $counts = array_count_values($text);
    return array(
      $counts,
      t_safe('Executed @queries queries in @time ms.', array(
        '@queries' => count($queries),
        '@time' => round($sum * 1000, 2),
      )),
    );
  }
}

/**
 * Gets the t() function if available, uses strtr() as a fallback.
 */
function t_safe($string, $args) {

  // get_t() caused problems here with the theme registry after changing on
  // admin/build/modules. The theme_get_registry() call is needed!
  if (function_exists('t') && function_exists('theme_get_registry')) {
    theme_get_registry();
    return t($string, $args);
  }
  else {
    return strtr($string, $args);
  }
}

/**
 * Returns the core version.
 *
 * @param string
 *   The version string to parse.
 *
 * @return string
 *   MAJOR.MINOR for versions before 5, MAJOR for subsequent versions.
 */
function devel_get_core_version($version) {
  $version_parts = explode('.', $version);

  // Map from 4.7.10 -> 4.7
  if ($version_parts[0] < 5) {
    return $version_parts[0] . '.' . $version_parts[1];
  }
  else {
    return $version_parts[0];
  }
}

/**
 * Returns whether the optimizer is compatible.
 *
 * @return boolean
 *   TRUE if compatible, FALSE otherwise.
 */
function devel_is_compatible_optimizer() {

  // See http://drupal.org/node/126098.
  ob_start();
  phpinfo();
  $info = ob_get_contents();
  ob_end_clean();

  // Match the Zend Optimizer version in the phpinfo information.
  $found = preg_match('/Zend&nbsp;Optimizer&nbsp;v([0-9])\\.([0-9])\\.([0-9])/', $info, $matches);
  if ($matches) {
    $major = $matches[1];
    $minor = $matches[2];
    $build = $matches[3];
    if ($major >= 3) {
      if ($minor >= 3) {
        return TRUE;
      }
      elseif ($minor == 2 && $build >= 8) {
        return TRUE;
      }
      else {
        return FALSE;
      }
    }
    else {
      return FALSE;
    }
  }
  else {
    return TRUE;
  }
}

/**
 * Generates the execute block form.
 */
function devel_execute_block_form() {
  $form['execute'] = array(
    '#type' => 'fieldset',
    '#title' => t('Execute PHP Code'),
    '#collapsible' => TRUE,
    '#collapsed' => !isset($_SESSION['devel_execute_code']),
  );
  $form['#submit'] = array(
    'devel_execute_form_submit',
  );
  return array_merge_recursive($form, devel_execute_form());
}

/**
 * Generates the execute form.
 */
function devel_execute_form() {
  $form['execute']['code'] = array(
    '#type' => 'textarea',
    '#title' => t('PHP code to execute'),
    '#description' => t('Enter some code. Do not use <code>&lt;?php ?&gt;</code> tags.'),
    '#default_value' => isset($_SESSION['devel_execute_code']) ? $_SESSION['devel_execute_code'] : '',
    '#rows' => 20,
    '#attributes' => array(
      'spellcheck' => 'false',
    ),
  );
  $form['execute']['actions'] = array(
    '#type' => 'actions',
  );
  $form['execute']['actions']['op'] = array(
    '#type' => 'submit',
    '#value' => t('Execute'),
  );
  $form['#attached']['css'][] = drupal_get_path('module', 'devel') . '/devel.css';
  $form['#redirect'] = FALSE;
  if (isset($_SESSION['devel_execute_code'])) {
    unset($_SESSION['devel_execute_code']);
  }
  return $form;
}

/**
 * Processes PHP execute form submissions.
 */
function devel_execute_form_submit($form, &$form_state) {
  ob_start();
  print eval($form_state['values']['code']);
  $_SESSION['devel_execute_code'] = $form_state['values']['code'];
  dsm(ob_get_clean());
}

/**
 * Switches to a different user.
 *
 * We don't call session_save_session() because we really want to change users.
 * Usually unsafe!
 *
 * @param string $name
 *   The username to switch to, or NULL to log out.
 */
function devel_switch_user($name = NULL) {
  global $user;
  if ($user->uid) {
    module_invoke_all('user_logout', $user);
  }
  if (isset($name) && ($account = user_load_by_name($name))) {
    $old_uid = $user->uid;
    $user = $account;
    $user->timestamp = time() - 9999;
    if (!$old_uid) {

      // Switch from anonymous to authorized.
      drupal_session_regenerate();
    }
    $edit = array();
    user_module_invoke('login', $edit, $user);
  }
  elseif ($user->uid) {
    session_destroy();
  }
  drupal_goto();
}

/**
 * Prints an object using either Krumo (if installed) or devel_print_object().
 *
 * @param array|object $object
 *   An array or object to print.
 * @param string $prefix
 *   Prefix for output items.
 *
 * @return
 *   The results from krumo_ob() or devel_print_object().
 */
function kdevel_print_object($object, $prefix = NULL) {
  return has_krumo() ? krumo_ob($object) : devel_print_object($object, $prefix);
}

/**
 * Saves krumo html using output buffering.
 */
function krumo_ob($object) {
  ob_start();
  krumo($object);
  $output = ob_get_contents();
  ob_end_clean();
  return $output;
}

/**
 * Displays an object or array.
 *
 * @param array|object $object
 *   The object or array to display.
 * @param string $prefix
 *   Prefix for the output items (example "$node->", "$user->", "$").
 * @param boolean $header
 *   Set to FALSE to suppress the output of the h3 tag.
 *
 * @return string
 */
function devel_print_object($object, $prefix = NULL, $header = TRUE) {
  drupal_add_css(drupal_get_path('module', 'devel') . '/devel.css');
  $output = '<div class="devel-obj-output">';
  if ($header) {
    $output .= '<h3>' . t('Display of !type !obj', array(
      '!type' => str_replace(array(
        '$',
        '->',
      ), '', $prefix),
      '!obj' => gettype($object),
    )) . '</h3>';
  }
  $output .= _devel_print_object($object, $prefix);
  $output .= '</div>';
  return $output;
}

/**
 * Returns formatted listing for an array or object.
 *
 * Recursive (and therefore magical) function goes through an array or object
 * and returns a nicely formatted listing of its contents.
 *
 * @param array|object $obj
 *   Array or object to recurse through.
 * @param string $prefix
 *   Prefix for the output items (example "$node->", "$user->", "$").
 * @param string $parents
 *   Used by recursion.
 * @param boolean $object
 *   Used by recursion.
 *
 * @return string
 *   Formatted html.
 *
 * @todo
 *   Currently there are problems sending an array with a varname.
 */
function _devel_print_object($obj, $prefix = NULL, $parents = NULL, $object = FALSE) {
  static $root_type, $out_format;

  // TODO: support objects with references. See http://drupal.org/node/234581.
  if (isset($obj->view)) {
    return;
  }
  if (!isset($root_type)) {
    $root_type = gettype($obj);
    if ($root_type == 'object') {
      $object = TRUE;
    }
  }
  if (is_object($obj)) {
    $obj = (array) $obj;
  }
  if (is_array($obj)) {
    $output = "<dl>\n";
    foreach ($obj as $field => $value) {
      if ($field === 'devel_flag_reference') {
        continue;
      }
      if (!is_null($parents)) {
        if ($object) {
          $field = $parents . '->' . $field;
        }
        else {
          if (is_int($field)) {
            $field = $parents . '[' . $field . ']';
          }
          else {
            $field = $parents . '[\'' . $field . '\']';
          }
        }
      }
      $type = gettype($value);
      $show_summary = TRUE;
      $summary = NULL;
      if ($show_summary) {
        switch ($type) {
          case 'string':
          case 'float':
          case 'integer':
            if (strlen($value) == 0) {
              $summary = t("{empty}");
            }
            elseif (strlen($value) < 40) {
              $summary = htmlspecialchars($value);
            }
            else {
              $summary = format_plural(drupal_strlen($value), '1 character', '@count characters');
            }
            break;
          case 'array':
          case 'object':
            $summary = format_plural(count((array) $value), '1 element', '@count elements');
            break;
          case 'boolean':
            $summary = $value ? t('TRUE') : t('FALSE');
            break;
        }
      }
      if (!is_null($summary)) {
        $typesum = '(' . $type . ', <em>' . $summary . '</em>)';
      }
      else {
        $typesum = '(' . $type . ')';
      }
      $output .= '<span class="devel-attr">';
      $output .= "<dt><span class=\"field\">{$prefix}{$field}</span> {$typesum}</dt>\n";
      $output .= "<dd>\n";

      // Check for references.
      if (is_array($value) && isset($value['devel_flag_reference'])) {
        $value['devel_flag_reference'] = TRUE;
      }

      // Check for references to prevent errors from recursions.
      if (is_array($value) && isset($value['devel_flag_reference']) && !$value['devel_flag_reference']) {
        $value['devel_flag_reference'] = FALSE;
        $output .= _devel_print_object($value, $prefix, $field);
      }
      elseif (is_object($value)) {
        $value->devel_flag_reference = FALSE;
        $output .= _devel_print_object((array) $value, $prefix, $field, TRUE);
      }
      else {
        $value = is_bool($value) ? $value ? 'TRUE' : 'FALSE' : $value;
        $output .= htmlspecialchars(print_r($value, TRUE)) . "\n";
      }
      $output .= "</dd></span>\n";
    }
    $output .= "</dl>\n";
  }
  return $output;
}

/**
 * Shows all the queries for the page.
 *
 * Adds a table at the bottom of the page cataloguing data on all the database
 * queries that were made to generate the page.
 *
 * @return string
 *   Queries themed using devel_querylog.
 */
function devel_query_table($queries, $counts) {
  $version = devel_get_core_version(VERSION);
  $header = array(
    'ms',
    '#',
    'where',
    'ops',
    'query',
    'target',
  );
  $i = 0;
  $api = variable_get('devel_api_url', 'api.drupal.org');
  $conn = Database::getconnection();
  foreach ($queries as $query) {
    $function = !empty($query['caller']['class']) ? $query['caller']['class'] . '::' : '';
    $function .= $query['caller']['function'];
    $count = isset($counts[$query['query']]) ? $counts[$query['query']] : 0;
    $diff = round($query['time'] * 1000, 2);
    if ($diff > variable_get('devel_execution', 5)) {
      $cell[$i][] = array(
        'data' => $diff,
        'class' => 'marker',
      );
    }
    else {
      $cell[$i][] = $diff;
    }
    $cell[$i][] = $count;
    $cell[$i][] = l($function, "http://{$api}/api/function/{$function}/{$version}");
    $ops[] = l(t('P'), '', array(
      'attributes' => array(
        'title' => 'Show placeholders',
        'class' => array(
          'dev-placeholders',
        ),
        'qid' => $i,
      ),
    ));
    $ops[] = l(t('A'), '', array(
      'attributes' => array(
        'title' => 'Show arguments',
        'class' => array(
          'dev-arguments',
        ),
        'qid' => $i,
      ),
    ));

    // EXPLAIN only valid for select queries.
    if (strpos($query['query'], 'UPDATE') === FALSE && strpos($query['query'], 'INSERT') === FALSE && strpos($query['query'], 'DELETE') === FALSE) {
      $ops[] = l(t('E'), '', array(
        'attributes' => array(
          'title' => 'Show EXPLAIN',
          'class' => array(
            'dev-explain',
          ),
          'qid' => $i,
        ),
      ));
    }
    $cell[$i][] = implode(' ', $ops);

    // 3 divs for each variation of the query. Last 2 are hidden by default.
    if (variable_get('devel_show_query_args_first', FALSE)) {
      $placeholders = '<div class="dev-placeholders" style="display: none;">' . check_plain($query['query']) . "</div>\n";
      $quoted = array();
      foreach ($query['args'] as $key => $val) {
        $quoted[$key] = $conn
          ->quote($val);
      }
      $output = strtr($query['query'], $quoted);
      $args = '<div class="dev-arguments">' . $output . '</div>' . "\n";
    }
    else {
      $placeholders = '<div class="dev-placeholders">' . check_plain($query['query']) . "</div>\n";
      $args = '<div class="dev-arguments" style="display: none;"></div>' . "\n";
    }
    $explain = '<div class="dev-explain" style="display: none;"></div>' . "\n";
    $cell[$i][] = array(
      'id' => "devel-query-{$i}",
      'data' => $placeholders . $args . $explain,
    );
    $cell[$i][] = $query['target'];
    $i++;
    unset($diff, $count, $ops);
  }
  if (variable_get('devel_query_sort', DEVEL_QUERY_SORT_BY_SOURCE)) {
    usort($cell, '_devel_table_sort');
  }
  return theme('devel_querylog', array(
    'header' => $header,
    'rows' => $cell,
  ));
}

/**
 * Returns HTML for a  Devel querylog row.
 *
 * @param array $variables
 *   An associative array containing:
 *   - row: An array of cells in which each cell is either a string or an
 *     associative array containing:
 *     - data: The data to render.
 *     - Any attributes to be applied to the cell.
 *
 * @ingroup themeable
 */
function theme_devel_querylog_row($variables) {
  $row = $variables['row'];
  $i = 0;
  $output = '';
  foreach ($row as $cell) {
    $i++;
    if (is_array($cell)) {
      $data = !empty($cell['data']) ? $cell['data'] : '';
      unset($cell['data']);
      $attr = $cell;
    }
    else {
      $data = $cell;
      $attr = array();
    }
    if (!empty($attr['class'])) {
      $attr['class'] .= " cell cell-{$i}";
    }
    else {
      $attr['class'] = "cell cell-{$i}";
    }
    $attr = drupal_attributes($attr);
    $output .= "<div {$attr}>{$data}</div>";
  }
  return $output;
}

/**
 * Returns HTML for the Devel querylog.
 *
 * @param array $variables
 *   An associative array containing:
 *   - header: An array suitable for rendering with devel_querylog_row.
 *   - rows: An array of rows suitable for rendering with devel_querylog_row.
 *
 * @ingroup themeable
 */
function theme_devel_querylog($variables) {
  $header = $variables['header'];
  $rows = $variables['rows'];
  $output = '';
  if (!empty($header)) {
    $output .= "<div class='devel-querylog devel-querylog-header clear-block'>";
    $output .= theme('devel_querylog_row', array(
      'row' => $header,
    ));
    $output .= "</div>";
  }
  if (!empty($rows)) {
    $i = 0;
    foreach ($rows as $row) {
      $i++;
      $zebra = $i % 2 == 0 ? 'even' : 'odd';
      $output .= "<div class='devel-querylog devel-querylog-{$zebra} clear-block'>";
      $output .= theme('devel_querylog_row', array(
        'row' => $row,
      ));
      $output .= "</div>";
    }
  }
  return $output;
}

/**
 * Devel's table sort.
 */
function _devel_table_sort($a, $b) {
  $a = is_array($a[0]) ? $a[0]['data'] : $a[0];
  $b = is_array($b[0]) ? $b[0]['data'] : $b[0];
  if ($a < $b) {
    return 1;
  }
  if ($a > $b) {
    return -1;
  }
  return 0;
}

/**
 * Displays page execution time at the bottom of the page.
 *
 * @return string
 *   A message indicating how long it took to execute the page.
 */
function devel_timer() {
  $time = timer_read('page');
  return t_safe(' Page execution time was @time ms.', array(
    '@time' => $time,
  ));
}
if (!function_exists('dd')) {

  /**
   * An alias for drupal_debug().
   *
   * @see drupal_debug()
   */
  function dd($data, $label = NULL) {
    return drupal_debug($data, $label);
  }
}

/**
 * Logs a variable to a drupal_debug.txt in the site's temp directory.
 *
 * @param mixed $data
 *   The variable to log to the drupal_debug.txt log file.
 * @param string $label
 *   (optional) If set, a label to output before $data in the log file.
 *
 * @return void|false
 *   Empty if successful, FALSE if the log file could not be written.
 *
 * @see dd()
 * @see http://drupal.org/node/314112
 */
function drupal_debug($data, $label = NULL) {
  $out = ($label ? $label . ': ' : '') . print_r($data, TRUE) . "\n";

  // The temp directory does vary across multiple simpletest instances.
  $file = file_directory_temp() . '/drupal_debug.txt';
  if (file_put_contents($file, $out, FILE_APPEND) === FALSE) {
    drupal_set_message(t('Devel was unable to write to %file.', array(
      '%file' => $file,
    )), 'error');
    return FALSE;
  }
}

/**
 * Prints the arguments passed into the current function.
 */
function dargs($always = TRUE) {
  static $printed;
  if ($always || !$printed) {
    $bt = debug_backtrace();
    print kdevel_print_object($bt[1]['args']);
    $printed = TRUE;
  }
}

/**
 * Prints a SQL string from a DBTNG Select object.
 *
 * Includes quoted arguments.
 *
 * @param object $query
 *   An object that implements the SelectQueryInterface interface.
 * @param string $return
 *   Whether to return the string. Default is FALSE, meaning to print it
 *   and return $query instead.
 * @param string $name
 *   Optional name for identifying the output.
 *
 * @return object|string
 *   The $query object, or the query string if $return was TRUE.
 */
function dpq($query, $return = FALSE, $name = NULL) {
  if (user_access('access devel information')) {
    if (method_exists($query, 'preExecute')) {
      $query
        ->preExecute();
    }
    $sql = (string) $query;
    $quoted = array();
    $connection = Database::getConnection();
    foreach ((array) $query
      ->arguments() as $key => $val) {
      $quoted[$key] = $connection
        ->quote($val);
    }
    $sql = strtr($sql, $quoted);
    if ($return) {
      return $sql;
    }
    dpm($sql, $name);
  }
  return $return ? NULL : $query;
}

/**
 * Prints a variable to the 'message' area of the page.
 *
 * Uses drupal_set_message().
 *
 * @param $input
 *   An arbitrary value to output.
 * @param string $name
 *   Optional name for identifying the output.
 * @param string $type
 *   Optional message type for drupal_set_message(), defaults to 'status'.
 *
 * @return input
 *   The unaltered input value.
 *
 * @see drupal_set_message()
 */
function dpm($input, $name = NULL, $type = 'status') {
  if (user_access('access devel information')) {
    $export = kprint_r($input, TRUE, $name);
    drupal_set_message($export, $type);
  }
  return $input;
}

/**
 * Displays a drupal_var_export() variable to the 'message' area of the page.
 *
 * Uses drupal_set_message().
 *
 * @param $input
 *   An arbitrary value to output.
 * @param string $name
 *   Optional name for identifying the output.
 *
 * @return input
 *   The unaltered input value.
 *
 * @see drupal_set_message()
 * @see drupal_var_export()
 */
function dvm($input, $name = NULL) {
  if (user_access('access devel information')) {
    $export = dprint_r($input, TRUE, $name, 'drupal_var_export', FALSE);
    drupal_set_message($export);
  }
  return $input;
}

/**
 * Legacy function that was poorly named.
 *
 * @deprecated Use dpm() instead, since the 'p' maps to 'print_r'.
 *
 * @see dpm()
 */
function dsm($input, $name = NULL) {
  return dpm($input, $name);
}

/**
 * An alias for dprint_r().
 *
 * Saves carpal tunnel syndrome.
 *
 * @see dprint_r()
 */
function dpr($input, $return = FALSE, $name = NULL) {
  return dprint_r($input, $return, $name);
}

/**
 * An alias for kprint_r().
 *
 * Saves carpal tunnel syndrome.
 *
 * @see kprint_r()
 */
function kpr($input, $return = FALSE, $name = NULL) {
  return kprint_r($input, $return, $name);
}

/**
 * Like dpr(), but uses drupal_var_export() instead.
 *
 * @see dprint_r()
 * @see drupal_var_export()
 */
function dvr($input, $return = FALSE, $name = NULL) {
  return dprint_r($input, $return, $name, 'drupal_var_export', FALSE);
}

/**
 * Returns a message using Krumo.
 *
 * Uses dprint_r() as a fallback.
 *
 * @param mixed $input
 *   The thing to print.
 * @param boolean $return
 *   (optional) Indicates if the output should be returned.  The default is
 *   FALSE.
 * @param string $name
 *   (optional) The label to apply.
 * @param string $function
 *   (optional) The function to use for output in the case where dprint_r() is
 *   used.  The defualt is print_r().
 *
 * @return
 *   The output if $return is TRUE.
 */
function kprint_r($input, $return = FALSE, $name = NULL, $function = 'print_r') {

  // We do not want to krumo() strings and integers and such.
  if (merits_krumo($input)) {
    if (user_access('access devel information')) {
      return $return ? (isset($name) ? $name . ' => ' : '') . krumo_ob($input) : krumo($input);
    }
  }
  else {
    return dprint_r($input, $return, $name, $function);
  }
}

/**
 * Pretty-print a variable to the browser (no krumo).
 *
 * Displays only for users with proper permissions. If you want a string
 * returned instead of a print, use the 2nd param.
 *
 * @param mixed $input
 *   The input that should be printed or returned.
 * @param boolean $return
 *   (optional) Indicates of the result should be returned instead of printed.
 *   The default is to print it.
 * @param string $name
 *   (optional) The label to apply.
 * @param string $function
 *   (optional) The function to use for output.  The defualt is print_r().
 * @param boolean $check
 *   (optional) Indicates if the output should be run through check_plain().
 *   The default is TRUE.
 *
 * @return
 *   The formatted output if $return is TRUE.
 */
function dprint_r($input, $return = FALSE, $name = NULL, $function = 'print_r', $check = TRUE) {
  if (user_access('access devel information')) {
    if ($name) {
      $name .= ' => ';
    }
    if ($function == 'drupal_var_export') {
      include_once DRUPAL_ROOT . '/includes/utility.inc';
      $output = drupal_var_export($input);
    }
    else {
      ob_start();
      $function($input);
      $output = ob_get_clean();
    }
    if ($check) {
      $output = check_plain($output);
    }
    if (is_array($input) && count($input, COUNT_RECURSIVE) > DEVEL_MIN_TEXTAREA) {

      // Don't use fapi here because sometimes fapi will not be loaded.
      $printed_value = "<textarea rows=30 style=\"width: 100%;\">\n" . $name . $output . '</textarea>';
    }
    else {
      $printed_value = '<pre>' . $name . $output . '</pre>';
    }
    if ($return) {
      return $printed_value;
    }
    else {
      print $printed_value;
    }
  }
}

/**
 * Prints a renderable array element to the screen using kprint_r().
 *
 * #pre_render and/or #post_render pass-through callback for kprint_r().
 *
 * @todo Investigate appending to #suffix.
 * @todo Investigate label derived from #id, #title, #name, and #theme.
 */
function devel_render() {
  $args = func_get_args();

  // #pre_render and #post_render pass the rendered $element as last argument.
  kprint_r(end($args));

  // #pre_render and #post_render expect the first argument to be returned.
  return reset($args);
}

/**
 * Prints the function call stack.
 *
 * @param $return
 *   Pass TRUE to return the formatted backtrace rather than displaying it in
 *   the browser via kprint_r().
 * @param $pop
 *   How many items to pop from the top of the stack; useful when calling from
 *   an error handler.
 * @param $options
 *   Options to pass on to PHP's debug_backtrace(), depending on your PHP
 *   version.
 *
 * @return string|NULL
 *   The formatted backtrace, if requested, or NULL.
 *
 * @see http://php.net/manual/en/function.debug-backtrace.php
 */
function ddebug_backtrace($return = FALSE, $pop = 0, $options = TRUE) {
  if (user_access('access devel information')) {
    $backtrace = debug_backtrace($options);
    while ($pop-- > 0) {
      array_shift($backtrace);
    }
    $counter = count($backtrace);
    if (!empty($backtrace[$counter - 1]['file'])) {
      $path = $backtrace[$counter - 1]['file'];
      $path = substr($path, 0, strlen($path) - 10);
      $paths[$path] = strlen($path) + 1;
    }
    $paths[DRUPAL_ROOT] = strlen(DRUPAL_ROOT) + 1;
    $nbsp = " ";

    // Show message if error_level is ERROR_REPORTING_DISPLAY_SOME or higher.
    // (This is Drupal's error_level, which is different from $error_level,
    // and we purposely ignore the difference between _SOME and _ALL,
    // see #970688!)
    if (variable_get('error_level', 1) >= 1) {
      while (!empty($backtrace)) {
        $call = array();
        if (isset($backtrace[0]['file'])) {
          $call['file'] = $backtrace[0]['file'];
          foreach ($paths as $path => $len) {
            if (strpos($backtrace[0]['file'], $path) === 0) {
              $call['file'] = substr($backtrace[0]['file'], $len);
            }
          }
          $call['file'] .= ':' . $backtrace[0]['line'];
        }
        if (isset($backtrace[1])) {
          if (isset($backtrace[1]['class'])) {
            $function = $backtrace[1]['class'] . $backtrace[1]['type'] . $backtrace[1]['function'] . '()';
          }
          else {
            $function = $backtrace[1]['function'] . '()';
          }
          $backtrace[1] += array(
            'args' => array(),
          );
          $call['args'] = $backtrace[1]['args'];
        }
        else {
          $function = 'main()';
          $call['args'] = $_GET;
        }
        $nicetrace[($counter <= 10 ? $nbsp : '') . --$counter . ': ' . $function] = $call;
        array_shift($backtrace);
      }
      if ($return) {
        return $nicetrace;
      }
      kprint_r($nicetrace);
    }
  }
}

/**
 * Deletes all files in a dir.
 */
function devel_empty_dir($dir) {
  foreach (new DirectoryIterator($dir) as $file_info) {
    if ($file_info
      ->isFile()) {
      unlink($file_info
        ->getPathname());
    }
  }
}

/*
 * Migration-related functions.
 */

/**
 * Regenerates the data in node_comment_statistics table.
 *
 * Technique - http://www.artfulsoftware.com/infotree/queries.php?&bw=1280#101
 */
function devel_rebuild_node_comment_statistics() {

  // Empty table.
  db_truncate('node_comment_statistics')
    ->execute();

  // TODO: DBTNG. Ignore keyword is Mysql only? Is only used in the rare case
  // when two comments on the same node share same timestamp.
  $sql = "\n    INSERT IGNORE INTO {node_comment_statistics} (nid, cid, last_comment_timestamp, last_comment_name, last_comment_uid, comment_count) (\n      SELECT c.nid, c.cid, c.created, c.name, c.uid, c2.comment_count FROM {comment} c\n      JOIN (\n        SELECT c.nid, MAX(c.created) AS created, COUNT(*) AS comment_count FROM {comment} c WHERE status = 1 GROUP BY c.nid\n      ) AS c2 ON c.nid = c2.nid AND c.created = c2.created\n    )";
  db_query($sql, array(
    ':published' => COMMENT_PUBLISHED,
  ));

  // Insert records into the node_comment_statistics for nodes that are missing.
  $query = db_select('node', 'n');
  $query
    ->leftJoin('node_comment_statistics', 'ncs', 'ncs.nid = n.nid');
  $query
    ->addField('n', 'changed', 'last_comment_timestamp');
  $query
    ->addField('n', 'uid', 'last_comment_uid');
  $query
    ->addField('n', 'nid');
  $query
    ->addExpression('0', 'comment_count');
  $query
    ->addExpression('NULL', 'last_comment_name');
  $query
    ->isNull('ncs.comment_count');
  db_insert('node_comment_statistics', array(
    'return' => Database::RETURN_NULL,
  ))
    ->from($query)
    ->execute();
}

/**
 * Implements hook_form_alter().
 *
 * Adds mouse-over hints on the Permissions page to display language-independent
 * machine names and module base names.
 */
function devel_form_user_admin_permissions_alter(&$form, &$form_state) {
  if (user_access('access devel information') && variable_get('devel_raw_names', FALSE)) {
    foreach ($form['permission'] as $perm => $data) {
      if (is_numeric($perm)) {
        $form['permission'][$perm]['#markup'] = '<span title="' . $form['permission'][$perm]['#id'] . '">' . $form['permission'][$perm]['#markup'] . '</span>';
      }
      else {
        $form['permission'][$perm]['#markup'] = '<span title="' . check_plain($perm) . '">' . $form['permission'][$perm]['#markup'] . '</span>';
      }
    }
  }
}

/**
 * Implements hook_form_alter().
 *
 * Adds mouse-over hints on the Modules page to display module base names.
 */
function devel_form_system_modules_alter(&$form, &$form_state) {
  if (user_access('access devel information') && variable_get('devel_raw_names', FALSE) && isset($form['modules']) && is_array($form['modules'])) {
    foreach (element_children($form['modules']) as $key) {
      if (isset($form['modules'][$key]['name']['#markup'])) {
        $form['modules'][$key]['name']['#markup'] = '<span title="' . $key . '">' . $form['modules'][$key]['name']['#markup'] . '</span>';
      }
      elseif (is_array($form['modules'][$key])) {
        foreach (element_children($form['modules'][$key]) as $key2) {
          if (isset($form['modules'][$key][$key2]['name']['#markup'])) {
            $form['modules'][$key][$key2]['name']['#markup'] = '<span title="' . $key2 . '">' . $form['modules'][$key][$key2]['name']['#markup'] . '</span>';
          }
        }
      }
    }
  }
}

/**
 * Implements hook_query_TAG_alter() for the devel tag.
 *
 * Makes debugging EntityFieldQuery much easier.
 *
 * Example usage:
 * @code
 *   $q = new EntityFieldQuery;
 *   $q->entityCondition('entity_type', 'node')
 *     ->addTag('debug')
 *     ->execute();
 * @endcode
 *
 * @param QueryAlterableInterface $query
 */
function devel_query_debug_alter(QueryAlterableInterface $query) {
  if (!$query
    ->hasTag('debug-semaphore')) {
    $query
      ->addTag('debug-semaphore');
    dpq($query);
  }
}

Functions

Namesort descending Description
backtrace_error_handler Displays backtrace showing the route of calls to the current error.
dargs Prints the arguments passed into the current function.
dcp Wrapper for ChromePHP Class log method.
ddebug_backtrace Prints the function call stack.
devel_admin_paths Implements hook_admin_paths().
devel_ajax_render_alter Implements hook_ajax_render_alter().
devel_block_configure Implements hook_block_configure().
devel_block_info Implements hook_block_info().
devel_block_save Implements hook_block_save().
devel_block_switch_user Provides the Switch user block.
devel_block_view Implements hook_block_view().
devel_boot Implements hook_boot().
devel_doc_function_form Provides the devel docs form.
devel_doc_function_form_submit Submit handler for the API lookup form.
devel_drupal_goto_alter Implements hook_drupal_goto_alter().
devel_empty_dir Deletes all files in a dir.
devel_execute_block_form Generates the execute block form.
devel_execute_form Generates the execute form.
devel_execute_form_submit Processes PHP execute form submissions.
devel_form_system_modules_alter Implements hook_form_alter().
devel_form_user_admin_permissions_alter Implements hook_form_alter().
devel_get_core_version Returns the core version.
devel_get_handlers Gets error handlers.
devel_help Implements hook_help().
devel_init Implements hook_init().
devel_is_compatible_optimizer Returns whether the optimizer is compatible.
devel_library_alter Implements hook_library_alter().
devel_menu Implements hook_menu().
devel_menu_link_alter Implements hook_menu_link_alter().
devel_menu_need_destination Returns paths that need a destination.
devel_menu_need_token_protection Returns list of paths which need CSRF token protection.
devel_modules_installed Implements hook_modules_installed().
devel_page_alter Implements hook_page_alter().
devel_permission Implements hook_permission().
devel_print_object Displays an object or array.
devel_query_debug_alter Implements hook_query_TAG_alter() for the devel tag.
devel_query_enabled Returns whether query logging is enabled.
devel_query_put_contents Writes the variables information to a file.
devel_query_summary Returns the query summary.
devel_query_table Shows all the queries for the page.
devel_rebuild_node_comment_statistics Regenerates the data in node_comment_statistics table.
devel_render Prints a renderable array element to the screen using kprint_r().
devel_set_handler Sets a new error handler or restores the prior one.
devel_set_message Sets a message.
devel_shutdown Runs on shutdown to clean up and display developer information.
devel_shutdown_memory Returns the rendered memory usage.
devel_shutdown_query Returns the rendered query log.
devel_shutdown_real Runs on shutdown to display developer information in the footer.
devel_shutdown_summary Returns the rendered shutdown summary.
devel_silent Checks whether Devel may be active.
devel_switch_user Switches to a different user.
devel_switch_user_form Provides the Switch user form.
devel_switch_user_form_submit Submit handler for the Switch user form.
devel_switch_user_form_validate Validate handler for the Switch user form.
devel_switch_user_list Provides the Switch user list.
devel_theme Implements hook_theme().
devel_timer Displays page execution time at the bottom of the page.
devel_translated_menu_link_alter Implements hook_translated_menu_item_alter().
devel_watchdog Implements hook_watchdog().
dfb Calls the fb() function if it is found.
dfbt Calls dfb() to output a backtrace.
dpm Prints a variable to the 'message' area of the page.
dpq Prints a SQL string from a DBTNG Select object.
dpr An alias for dprint_r().
dprint_r Pretty-print a variable to the browser (no krumo).
drupal_debug Logs a variable to a drupal_debug.txt in the site's temp directory.
dsm Deprecated Legacy function that was poorly named.
dvm Displays a drupal_var_export() variable to the 'message' area of the page.
dvr Like dpr(), but uses drupal_var_export() instead.
has_krumo Determines if Krumo is available.
kdevel_print_object Prints an object using either Krumo (if installed) or devel_print_object().
kpr An alias for kprint_r().
kprint_r Returns a message using Krumo.
krumo_ob Saves krumo html using output buffering.
merits_krumo Decides whether or not to print a debug variable using krumo().
theme_devel_querylog Returns HTML for the Devel querylog.
theme_devel_querylog_row Returns HTML for a Devel querylog row.
t_safe Gets the t() function if available, uses strtr() as a fallback.
_devel_print_object Returns formatted listing for an array or object.
_devel_switch_user_access Menu item access callback: checks permission and token for Switch User.
_devel_switch_user_list_cmp Comparison helper function for uasort() in devel_switch_user_list().
_devel_table_sort Devel's table sort.

Constants