Skip to content
devel.module 53.5 KiB
Newer Older
// This module holds functions useful for Drupal development.
// 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', 2);

 * Implementation of 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 documention.') .'</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>';
Moshe Weitzman's avatar
Moshe Weitzman committed
    case 'devel/reinstall':
      return t('Warning - will delete your module tables and variables.');
 * Implementationation of hook_menu().
  // Note: we can't dynamically append destination to querystring. Do so at theme layer. Fix in D7?
  $items['devel/cache/clear'] = array(
    'title' => 'Empty 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'),
    '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'),
  );
  $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'),
Jeff Robbins's avatar
Jeff Robbins committed
  $items['devel/source'] = array(
    'title' => 'Display the PHP code of any file in your Drupal installation',
    'page callback' => 'devel_display_source',
    'access arguments' => array('display source code'),
    'type' => MENU_CALLBACK,
Jeff Robbins's avatar
Jeff Robbins committed
  );
Moshe Weitzman's avatar
Moshe Weitzman committed
  $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.',
Moshe Weitzman's avatar
Moshe Weitzman committed
    'page callback' => 'drupal_get_form',
    'page arguments' => array('devel_menu_rebuild'),
Moshe Weitzman's avatar
Moshe Weitzman committed
    'access arguments' => array('access devel information'),
  $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'),
  $items['devel/variable'] = array(
    'title' => 'Variable editor',
    'description' => 'Edit and delete site variables.',
    'page callback' => 'devel_variable_page',
    'access arguments' => array('access devel information'),
  // 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'),
  );
  $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'),
  );
  $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'),
Moshe Weitzman's avatar
Moshe Weitzman committed
  $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'),
    'title' => 'Hook_elements()',
    'description' => 'View the active form/render elements for this site.',
    'page callback' => 'devel_elements_page',
    'access arguments' => array('access devel information'),
  $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,
  );
  $items['devel/session'] = array(
    'title' => 'Session viewer',
    'description' => 'List the contents of $_SESSION.',
    'page callback' => 'devel_session',
    'access arguments' => array('access devel information'),
  );
  $items['devel/switch'] = array(
    'title' => 'Switch user',
    'page callback' => 'devel_switch_user',
    'access arguments' => array('switch users'),
    'type' => MENU_CALLBACK,
  $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'),
    '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'),
  $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(
    'description' =>  'Helper functions, pages, and blocks to assist Drupal developers. The devel blocks can be managed via the <a href="' . url('admin/structure/block') . '">block administration</a> page.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('devel_admin_settings'),
    'access arguments' => array('administer site configuration'),
  $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="' . url('admin/structure/block') . '">block administration</a> page.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('devel_admin_settings'),
    'access arguments' => array('administer site configuration'),
  );

  $items['node/%node/devel'] = array(
    'title' => 'Devel',
    'page callback' => 'devel_load_object',
    'page arguments' => array(1, 'node'),
    'access arguments' => array('access devel information'),
    'type' => MENU_LOCAL_TASK,
  $items['node/%node/devel/load'] = array(
    'title' => 'Load',
    'page callback' => 'devel_load_object',
    'page arguments' => array(1, 'node'),
    'access arguments' => array('access devel information'),
Moshe Weitzman's avatar
Moshe Weitzman committed
  $items['node/%node/devel/render'] = array(
    'page callback' => 'devel_render_object',
    'page arguments' => array('node', 1),
    'access arguments' => array('access devel information'),
  $items['comment/%comment/devel'] = array(
    'title' => 'Devel',
    'page callback' => 'devel_load_object',
    'page arguments' => array(1, 'comment'),
    'access arguments' => array('access devel information'),
    'type' => MENU_LOCAL_TASK,
  $items['comment/%comment/devel/load'] = array(
    'title' => 'Load',
    'page callback' => 'devel_load_object',
    'page arguments' => array(1, 'comment'),
    'access arguments' => array('access devel information'),
    'type' => MENU_DEFAULT_LOCAL_TASK,
  $items['comment/%comment/devel/render'] = array(
    'page callback' => 'devel_render_object',
    'page arguments' => array('comment', 1),
    'access arguments' => array('access devel information'),
    'type' => MENU_LOCAL_TASK,
  $items['user/%user/devel'] = array(
    'title' => 'Devel',
    'page callback' => 'devel_load_object',
    'page arguments' => array(1, 'user'),
    'access arguments' => array('access devel information'),
    'type' => MENU_LOCAL_TASK,
  $items['user/%user/devel/load'] = array(
    'title' => 'Load',
    'page callback' => 'devel_load_object',
    'page arguments' => array(1, 'user'),
    'access arguments' => array('access devel information'),
Moshe Weitzman's avatar
Moshe Weitzman committed
  $items['user/%user/devel/render'] = array(
    'page callback' => 'devel_render_object',
    'page arguments' => array('user', 1),
    'access arguments' => array('access devel information'),
  $items['taxonomy/term/%taxonomy_term/devel'] = array(
    'title' => 'Devel',
    'page callback' => 'devel_load_object',
    'page arguments' => array(2, 'term'),
    'access arguments' => array('access devel information'),
  $items['taxonomy/term/%taxonomy_term/devel/load'] = array(
    'title' => 'Load',
    'page callback' => 'devel_load_object',
    'page arguments' => array(2, 'term'),
    'access arguments' => array('access devel information'),
  $items['taxonomy/term/%taxonomy_term/devel/render'] = array(
    'page callback' => 'devel_render_term',
    'page arguments' => array(2),
    'access arguments' => array('access devel information'),
    'type' => MENU_LOCAL_TASK,
function devel_menu_need_destination() {
  return array('devel/cache/clear', 'devel/reinstall', 'devel/menu/reset', 'devel/variable', 'admin/reports/status/run-cron');
}

/**
 * An implementation of hook_menu_link_alter(). Flag this link as needing alter at display time.
Moshe Weitzman's avatar
Moshe Weitzman committed
 * 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()) || $item['link_path'] == 'devel/menu/item') {
    $item['options']['alter'] = TRUE;
  }
}

 * An implementation of hook_translated_menu_item_alter(). Append dynamic
 * querystring 'destination' to several of our own menu items.
 **/
function devel_translated_menu_link_alter(&$item) {
  if (in_array($item['href'], devel_menu_need_destination())) {
    $item['localized_options']['query'] = drupal_get_destination();
  }
  elseif ($item['href'] == 'devel/menu/item') {
    $item['localized_options']['query'] = array('path' => $_GET['q']);
  }
/**
 * Implementation of hook_theme()
 */
      'variables' => array('header' => array(), 'rows' => array()),
      'variables' => array('row' => array()),
 * Implementation of hook_init().
function devel_init() {
  if (!devel_silent()) {
    if (user_access('access devel information')) {
      devel_set_handler(variable_get('devel_error_handler', DEVEL_ERROR_HANDLER_STANDARD));
      // We want to include the class early so that anyone may call krumo() as needed. See http://krumo.sourceforge.net/
      // See http://www.firephp.org/HQ/Install.htm
      $path = NULL;
      if (@include_once('fb.php')) {
        // FirePHPCore is in include_path. Probably a PEAR installation.
        $path = '';
      }
      elseif (module_exists('libraries')) {
        // Support Libraries API - http://drupal.org/project/libraries
        $path = libraries_get_path('FirePHPCore') . '/lib/FirePHPCore/';
        $path = './'. drupal_get_path('module', 'devel') .'/FirePHPCore/lib/FirePHPCore/';
      if (file_exists($path . 'fb.php')) {
        include_once $path . 'fb.php';
      // 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'))));
function devel_set_message($msg, $type = NULL) {
  $function  = function_exists('drush_log') ? 'drush_log' : 'drupal_set_message';
  $function($msg, $type);
Moshe Weitzman's avatar
Moshe Weitzman committed
// Return boolean. No need for cache here.
function has_krumo() {
  // see README.txt or just download from http://krumo.sourceforge.net/
Moshe Weitzman's avatar
Moshe Weitzman committed
  @include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'devel') .'/krumo/class.krumo.php';
  return function_exists('krumo') && !drupal_is_cli();
/**
 * Decide whether or not to print a debug variable using krumo().
 *
 * @param $input
 * @return boolean
 */
function merits_krumo($input) {
  return (is_object($input) || is_array($input)) && has_krumo() && variable_get('devel_krumo_skin', '') != 'disabled';
/**
 * Calls the http://www.firephp.org/ fb() function if it is found.
 *
 * @return void
 */
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);
}

/**
 * Implements hook_watchdog().
 */
function devel_watchdog(array $log_entry) {
  if (class_exists('FirePHP') && !drupal_is_cli()) {
      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);
}
function devel_set_handler($handler) {
  switch ($handler) {
    case DEVEL_ERROR_HANDLER_STANDARD:
      // do nothing
      break;
    case DEVEL_ERROR_HANDLER_BACKTRACE:
        set_error_handler('backtrace_error_handler');
      }
      break;
    case DEVEL_ERROR_HANDLER_NONE:
      restore_error_handler();
      break;
  }
}

function devel_silent() {
  // isset($_GET['q']) is needed when calling the front page. q is not set.
  // Don't interfere with private files/images.
    function_exists('drupal_is_cli') && drupal_is_cli() ||
Moshe Weitzman's avatar
Moshe Weitzman committed
    (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')
function devel_xhprof_enable() {
  if (extension_loaded('xhprof') && variable_get('devel_xhprof_enabled', FALSE)) {
    if ($path = variable_get('devel_xhprof_directory', '')) {
      include_once $path . '/xhprof_lib/utils/xhprof_lib.php';
      include_once $path . '/xhprof_lib/utils/xhprof_runs.php';
      // @todo: consider a variable per-flag instead.
      xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY);
    }
  }
}

/**
 * Implementation of hook_boot(). Runs even for cached pages.
 */
function devel_boot() {
  // Initialize XHProf.
  devel_xhprof_enable();
  if (!devel_silent()) {
    if (variable_get('dev_mem', 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');
function backtrace_error_handler($errno, $message, $filename, $line) {
  // Don't respond to the error if it was suppressed with a '@'
  if (error_reporting() == 0) return;
  if ($errno & (E_ALL ^ E_NOTICE ^ E_DEPRECATED)) {
    // We can't use the PHP E_* constants here as not all versions of PHP have all
    // the constants defined, so for consistency, we just use the numeric equivelant.
    $types = array(
      1 => 'error',
      2 => 'warning',
      4 => 'parse error',
      8 => 'notice',
      16 => 'core error',
      32 => 'core warning',
      64 => 'compile error',
      128 => 'compile warning',
      256 => 'user error',
      512 => 'user warning',
      1024 => 'user notice',
      2048 => 'strict warning',
      4096 => 'recoverable error',
      8192 => 'deprecated',
      16384 => 'user deprecated',
    );
    $entry = $types[$errno] .': '. $message .' in '. $filename .' on line '. $line .'.';

    if (variable_get('error_level', 1) == 1) {
      $backtrace = debug_backtrace();
      foreach ($backtrace as $call) {
        $nicetrace[$call['function']] = $call;
    watchdog('php', '%message in %file on line %line.', array('%error' => $types[$errno], '%message' => $message, '%file' => $filename, '%line' => $line), WATCHDOG_ERROR);
Moshe Weitzman's avatar
Moshe Weitzman committed
 * Implement hook_permission().
Moshe Weitzman's avatar
Moshe Weitzman committed
function devel_permission() {
Moshe Weitzman's avatar
Moshe Weitzman committed
    'access devel information' => array(
      'description' => t('View developer output like variable printouts, query log, etc.'),
Moshe Weitzman's avatar
Moshe Weitzman committed
      'title' => t('Access developer information'),
    ),
    'execute php code' => array(
      'title' => t('Execute PHP code'),
      'description' => t('Run arbitrary PHP from a block. Danger!'),
    ),
    'switch users' => array(
      'title' => t('Switch users'),
      'description' => t('Become any user on the site with just a click. Danger!'),
    ),
    'display source code' => array(
      'title' => t('Display source code'),
      'description' => t('View the site\'s php source code. Danger!'),
    ),
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,
  );
 * Implementation of hook_block_configure().
  if ($delta == 'switch_user') {
      '#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',
    );
      '#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),
    );
  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']);
}

function devel_block_view($delta) {
  $block = array();
  switch ($delta) {
        $block['content'] = drupal_get_form('devel_execute_block_form');
function devel_block_switch_user() {
  $links = devel_switch_user_list();
  if (!empty($links) || user_access('switch users')) {
    $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;
  }
}

function devel_switch_user_list() {
  $links = array();
  if (user_access('switch users')) {
    $list_size = variable_get('devel_switch_user_list_size', 10);
    if ($include_anon = ($user->uid && 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.
    $roles = user_roles(TRUE, 'switch users');
    if (isset($roles[DRUPAL_AUTHENTICATED_RID])) {
      // If authenticated users have this permission, just grab
      // the last $list_size users, since there won't be records in
      // {user_roles} and every user on the system can switch.
      $accounts = db_query_range("SELECT DISTINCT u.uid, u.name, u.access FROM {users} u WHERE u.uid > 0 ORDER BY u.access DESC", 0, $list_size);
    }
    else {
      $where = array('u.uid = 1');
      if (count($roles)) {
        $where[] = 'r.rid IN ('. implode(',', array_keys($roles)) .')';
      }
      $where_sql = implode(' OR ', $where);
      $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 ORDER BY u.access DESC", 0, $list_size);
    foreach ($accounts as $account) {
      $links[$account->uid] = array(
        'title' => drupal_placeholder($account->name),
        'href' => 'devel/switch/'. $account->name,
        'query' => $dest,
        'attributes' => array('title' => t('This user can switch back.')),
        'html' => TRUE,
    }
    $num_links = count($links);
    if ($num_links < $list_size) {
      // If we don't have enough, add distinct uids until we hit $list_size.
      $accounts = db_query_range('SELECT uid, name, access FROM {users} WHERE uid > 0 AND uid NOT IN ('. implode(',', array_keys($links)) .') ORDER BY access DESC', 0, $list_size - $num_links);
      foreach ($accounts as $account) {
        $links[$account->uid] = array(
          'title' => $account->name ? $account->name : 'anon',
          'href' => 'devel/switch/'. $account->name,
          'attributes' => array('title' => t('Caution: this user will be unable to switch back.')),
      uasort($links, '_devel_switch_user_list_cmp');
        'title' => check_plain(format_username(drupal_anonymous_user())),
        'href' => 'devel/switch',
        'query' => $dest,
        '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;
    }
/**
 * 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'];
}

function devel_switch_user_form() {
  $form['username'] = array(
    '#type' => 'textfield',
    '#description' => t('Enter username'),
    '#autocomplete_path' => 'user/autocomplete',
    '#maxlength' => USERNAME_MAX_LENGTH,
    '#type' => 'submit',
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['submit_button'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
  );
  return $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";
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'));
function devel_switch_user_form_submit($form, &$form_state) {
  $form_state['redirect'] = 'devel/switch/'. $form_state['values']['username'];
// An implementation of hook_drupal_goto_alter().
function devel_drupal_goto_alter($path, $options, $http_response_code) {
    // 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));
      $GLOBALS['devel_redirecting'] = TRUE;
Jeff Robbins's avatar
Jeff Robbins committed
 * See devel_start() which registers this function as a shutdown function.
  // Register the real shutdown function so it runs later than other shutdown functions.
  drupal_register_shutdown_function('devel_shutdown_real');

  // Do this now so that we don't get cut off by drush_shutdown().
  global $devel_run_id;
  $devel_run_id = variable_get('devel_xhprof_enabled', FALSE) ? devel_shutdown_xhprof(): NULL;
Moshe Weitzman's avatar
Moshe Weitzman committed
  if ($devel_run_id && function_exists('drush_log')) {
    drush_log('xhprof link: ' . devel_xhprof_link($devel_run_id, 'url'), 'notice');
function devel_page_alter($page) {
  if (variable_get('devel_page_alter', FALSE) && user_access('access devel information')) {
    dpm($page, 'page');
  }
}
// AJAX render reponses sometimers are sent as text/html so we have to catch them here
// and disable our footer stuff.
function devel_ajax_render_alter() {
  $GLOBALS['devel_shutdown'] = FALSE;
}

/**
 * See devel_shutdown() which registers this function as a shutdown function. Displays developer information in the footer.
 */
function devel_shutdown_real() {
  // 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 is advised where applicable.
  if (!devel_silent() && !isset($GLOBALS['devel_shutdown']) && !isset($GLOBALS['devel_redirecting'])) {
    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)) {
    if (isset($user) && user_access('access devel information')) {
      $queries = Database::getLog('devel', 'default');
      $output .= devel_shutdown_summary($queries);
      $output .= devel_shutdown_query($queries);
      // 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.
function devel_shutdown_summary($queries) {
  list($counts, $query_summary) = devel_query_summary($queries);
  if (variable_get('devel_query_display', FALSE)) {
    $output .= t_safe(' Queries exceeding @threshold ms are <span class="marker">highlighted</span>.', array('@threshold' => variable_get('devel_execution', 5)));
  if (variable_get('dev_timer', 0)) {
    $output .= devel_timer();
  }

  if (variable_get('devel_xhprof_enabled', FALSE)) {
    $output .= ' ' . devel_xhprof_link($GLOBALS['devel_run_id']);
  $output .= devel_shutdown_memory();

  if ($output) {
    return '<div class="dev-query">' . $output . '</div>';
  }
  $namespace = variable_get('site_name', '');  // namespace for your application
  $xhprof_data = xhprof_disable();
  $xhprof_runs = new XHProfRuns_Default();
  return $xhprof_runs->save_run($xhprof_data, $namespace);
}

function devel_xhprof_link($run_id, $type = 'link') {
  // @todo: render results from within Drupal.
  $xhprof_url = variable_get('devel_xhprof_url', '');
  $namespace = variable_get('site_name', '');  // namespace for your application
    $url  = $xhprof_url . "/index.php?run=$run_id&source=$namespace";
    return $type == 'url' ? $url : t('<a href="@xhprof">XHProf output</a>. ', array('@xhprof' => $url));
function devel_shutdown_memory() {
  global $memory_init;

  if (variable_get('dev_mem', 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);
  }
}

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);
Moshe Weitzman's avatar
Moshe Weitzman committed
      $output = devel_query_table($queries, $counts);
      // Save all queries to a file in temp dir. Retrieved via AJAX.
      devel_query_put_contents($queries);
    }
    else {
Moshe Weitzman's avatar
Moshe Weitzman committed
      $output = '</div>' . dprint_r($queries, TRUE);
Moshe Weitzman's avatar
Moshe Weitzman committed
    return $output;
// Write the variables information to the 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.
  if (mt_rand(1, 1000) == 401) {