Skip to content
devel.module 48 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'),
  $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 giben 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.
 * This is more robust that 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/.
      // Support Libraries API - http://drupal.org/project/libraries
      if (module_exists('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';
        include_once $path .'FirePHP.class.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'))));
// return boolean. no need for cache here.
function has_krumo() {
  // see README.txt or just download from http://krumo.sourceforge.net/
  @include_once './'. drupal_get_path('module', 'devel') .'/krumo/class.krumo.php';
Moshe Weitzman's avatar
Moshe Weitzman committed
  if (function_exists('krumo') && php_sapi_name() != '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')) {
    $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')) {
    switch ($log_entry['severity']) {
      case WATCHDOG_EMERG:
      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';
  }
  $watchdog = array(
    'type' => $log_entry['type'],
    'message' => decode_entities(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 on IIS when calling the front page. q is not set.
  // Don't interfere with private files/images.
    function_exists('drupal_is_cli') && drupal_is_cli() ||
    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' ||
Moshe Weitzman's avatar
Moshe Weitzman committed
      substr($_GET['q'], 0, strlen('batch')) == 'batch') ||
      substr($_GET['q'], 0, strlen('file/ajax')) == 'file/ajax'
}

/**
 * Implementation of hook_boot(). Runs even for cached pages.
 */
function devel_boot() {
  if (!devel_silent()) {
    devel_start();
// Kickoff our tricks. Put here all code which must run for cached pages too. Called from both devel_boot() and devel_init().
function devel_start() {
  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');
  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;
    // 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') {
    $form['devel_switch_user_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',
    );
    return $form;
  }
  if ($delta == 'switch_user') {
    variable_set('devel_switch_user_list_size', $edit['devel_switch_user_list_size']);
  }
}

function devel_block_view($delta) {
  $block = array();
  switch ($delta) {
      if (user_access('execute php code')) {
        $block['subject'] = t('Execute PHP');
        $block['content'] = drupal_get_form('devel_execute_form');
function devel_block_switch_user() {
  $links = devel_switch_user_list();
  if (!empty($links)) {
    $build['devel_links'] = array('#theme' => 'links', '#links' => $links);
    $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);
    $dest = drupal_get_destination();
    // Try to find at least $list_size users that can switch.
    $roles = user_roles(1, 'switch users');
    if (isset($roles[2])) {
      // 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.
      $users = db_query_range("SELECT DISTINCT u.uid, u.name, u.access FROM {users} u WHERE u.uid > 0 ORDER BY u.access DESC", 0, $list_size);
    }
    else {
      $where = array('u.uid = 1');
      if (count($roles)) {
        $where[] = 'r.rid IN ('. implode(',', array_keys($roles)) .')';
      }
      $where_sql = implode(' OR ', $where);
      $users = db_query_range("SELECT DISTINCT u.uid, u.name, u.access FROM {users} u LEFT JOIN {users_roles} r ON u.uid = r.uid WHERE $where_sql ORDER BY u.access DESC", 0, $list_size);
    foreach ($users as $user) {
        'title' => drupal_placeholder(array('text' => $user->name)),
        'href' => 'devel/switch/'. $user->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.
      $users = db_query_range('SELECT uid, name, access FROM {users} WHERE uid > 0 AND uid NOT IN ('. implode(',', array_keys($links)) .') ORDER BY access DESC', 0, $list_size - $num_links);
      foreach ($users as $user) {
        if (count($links) >= $list_size) {
          break;
        }
        $links[$user->uid] = array(
          'title' => $user->name ? $user->name : 'anon',
          'href' => 'devel/switch/'. $user->name,
          'query' => $dest,
          'attributes' => array('title' => t('Caution: this user will be unable switch back.')),
        );
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/$version/function/$function";
}

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.
  register_shutdown_function('devel_shutdown_real');
}

function devel_page_alter($page) {
  if (variable_get('devel_page_alter', FALSE) && user_access('access devel information')) {
    dpm($page, 'page');
  }
}
/**
 * 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 (!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) {
  $sum = 0;
  list($counts, $query_summary) = devel_query_summary($queries);
  $output .= '<div class="dev-query">';

  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();
  }

  $output .= devel_shutdown_memory();

  $output .= '</div>';

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) {
    devel_empty_dir($path);
  }

  $path .= "/$request_id.txt";
  $path = file_stream_wrapper_uri_normalize($path);
  // Save queries as a serialized array.
  file_put_contents($path, serialize($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";
}

  return method_exists('Database', 'getLog') && variable_get('devel_query_display', FALSE);
  if (variable_get('devel_query_display', FALSE) && is_array($queries)) {
    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))));
function t_safe($string, $args) {
  // get_t caused problems here with 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 {
    strtr($string, $args);
  }
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];
  }
  // Map from 5.5 -> 5 or 6.0-beta2 -> 6
  else {
    return $version_parts[0];
  }
// See http://drupal.org/node/126098
function devel_is_compatible_optimizer() {
   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;
  $form['code'] = array(
    '#type' => 'textarea',
    '#title' => t('PHP code to execute'),
    '#description' => t('Enter some code. Do not use <code>&lt;?php ?&gt;</code> tags.')