Skip to content
devel.module 64.1 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>';
/**
 * Implementation 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/queries'] = array(
    'title' => 'Database queries',
    'page callback' => 'devel_queries',
    'access callback' => 'devel_menu_access_store_queries',
    'access arguments' => array(),
    'menu_name' => 'devel',
  );
  $items['devel/queries/empty'] = array(
    'title' => 'Empty database queries',
    'page callback' => 'devel_queries_empty',
    'access callback' => 'devel_menu_access_store_queries',
    'access arguments' => array(),
  $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(
Moshe Weitzman's avatar
Moshe Weitzman committed
    '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'),
Moshe Weitzman's avatar
Moshe Weitzman committed
  $items['devel/devel_themer'] = array(
    'title callback' => 'devel_menu_title_theme_developer',
    'title arguments' => array(NULL),
    'title' => 'foo',
    'description' => 'Quickly enable or disable theme developer module. Useful for removing HTML cruft added by that module.',
Moshe Weitzman's avatar
Moshe Weitzman committed
    'page callback' => 'devel_devel_themer_toggle',
    'access arguments' => array('access devel information'),
Moshe Weitzman's avatar
Moshe Weitzman committed
  );
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/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'),
    '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'),
    '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['admin/settings/devel'] = array(
    'description' =>  'Helper functions, pages, and blocks to assist Drupal developers. The devel blocks can be managed via the <a href="!block">block administration</a> page.', array('!block' => url('admin/build/block')),
    'page callback' => 'drupal_get_form',
    'page arguments' => array('devel_admin_settings'),
    'access arguments' => array('administer site configuration'),
Moshe Weitzman's avatar
Moshe Weitzman committed
  $items['node/%node/devel/load'] = array(
    'title' => 'Dev load',
    'page callback' => 'devel_load_object',
    'page arguments' => array(1, 'node'),
    'access callback' => 'user_access',
    'access arguments' => array('access devel information'),
    'type' => MENU_LOCAL_TASK,
Moshe Weitzman's avatar
Moshe Weitzman committed
  $items['node/%node/devel/render'] = array(
    'title' => 'Dev render',
    'page callback' => 'devel_render_object',
    'page arguments' => array('node', 1),
    'access callback' => 'user_access',
    'access arguments' => array('access devel information'),
    'type' => MENU_LOCAL_TASK,
Moshe Weitzman's avatar
Moshe Weitzman committed
  $items['user/%user/devel/load'] = array(
Moshe Weitzman's avatar
Moshe Weitzman committed
    'title' => 'Dev load',
    'page callback' => 'devel_load_object',
    'page arguments' => array(1, 'user'),
    'access callback' => 'user_access',
    'access arguments' => array('access devel information'),
    'type' => MENU_LOCAL_TASK,
Moshe Weitzman's avatar
Moshe Weitzman committed
  $items['user/%user/devel/render'] = array(
    'title' => 'Dev render',
    'page callback' => 'devel_render_object',
    'page arguments' => array('user', 1),
    'access callback' => 'user_access',
    'access arguments' => array('access devel information'),
    'type' => MENU_LOCAL_TASK,
function devel_menu_need_destination() {
  return array('devel/cache/clear', 'devel/devel_themer', 'devel/reinstall', 'devel/menu/reset', 'admin/og/og', '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, $menu) {
  if (in_array($item['link_path'], devel_menu_need_destination())) {
    $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();
  }
}

function devel_menu_access_store_queries() {
  return user_access('access devel information') && variable_get('devel_store_queries', 0);
}

function devel_menu_title_theme_developer() {
  if (module_exists('devel_themer')) {
    return t('Disable Theme developer');
Jeff Robbins's avatar
Jeff Robbins committed
  }
Moshe Weitzman's avatar
Moshe Weitzman committed
  else {
    return t('Enable Theme developer');
  }
}

function devel_devel_themer_toggle() {
  if (module_exists('devel_themer')) {
    module_disable(array('devel_themer'));
Moshe Weitzman's avatar
Moshe Weitzman committed
  }
    // Sanity check in case the devel_themer schema is not installed.
    include_once('./includes/install.inc');
    if (drupal_get_schema_versions('devel_themer') == FALSE) {
      drupal_install_modules(array('devel_themer'));
Moshe Weitzman's avatar
Moshe Weitzman committed
  drupal_goto();
}

/**
 * Implementation of hook_theme()
 */
function devel_theme() { // &$cache, $type, $theme, $path
  return array(
    'devel_variable_form' => array(
        'arguments' => array('form' => NULL)
    ),
    'devel_querylog' => array(
      'arguments' => array('header' => array(), 'rows' => array()),
    ),
    'devel_querylog_row' => array(
      'arguments' => array('row' => array()),
    ),
Jeff Robbins's avatar
Jeff Robbins committed
/**
 * Page callback to display syntax hilighted source code
 *
 * note: the path for this function is received via $_GET['path']
 *  example http://www.example.com/devel/source?file=modules/node/node.module
 *
 * @param $standalone
 *   Set to FALSE to place the code inside a Drupal page. Otherwise code displays on its own.
 */
function devel_display_source($standalone = TRUE) {
  $path = $_GET['file'];
  // take out the nasties
  $path = str_replace('../', '', $path);
  $output = devel_highlight_file($path, $standalone);
  if ($output) {
    if ($standalone) {
      print $output;
      exit();
    }
    return $output;
  }
  else {
    drupal_set_message(t('Invalid file path'), 'error');
    drupal_not_found();
  }
}

/**
 * Return PHP highlighted file
 *
 * @param $path
 *   path to the file
 *    *warning* there is NO VALIDATION in this function
 *    Beware of paths such as '../../../../../etc/apache/httpd.conf'
 *
 * @param $standalone
 *   should the returned HTML be wrapped in a full <html> page or will it be output by Drupal?
 */
function devel_highlight_file($path = NULL, $standalone = FALSE) {
  if (file_exists($path)) {
    $source = highlight_file($path, TRUE);
    // add anchor links before all functions so that we can link to a function within the source
    // ** commented out because regexes aren't working **
    //$source = preg_replace('!(\/\*\*.*?\*\/.*?)<br.*?function.*?#0000BB">(.*?)<\/span>!', '<a id="$2"></a> $0', $source);
Jeff Robbins's avatar
Jeff Robbins committed
    //$source = preg_replace('!(\/\*\*.*?\*\/).*?function.*?#0000BB">(.*?)<\/span>!', '<a id="$2"></a> $0', $source);
    if ($standalone) {
      $source = <<<EOT
<head><title>$path</title></head>
<body>$source</body>
EOT;
    }
    return $source;
  }
  else {
    return FALSE;
  }
}

 * 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/
      $path = './'. drupal_get_path('module', 'devel') .'/FirePHPCore/lib/FirePHPCore/fb.php';
      if (file_exists($path)) {
        include_once $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');
  if (variable_get('devel_rebuild_theme_registry', FALSE)) {
    drupal_rebuild_theme_registry();
    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/settings/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().
 *
 * @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);
  }
}


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.
    devel_verify_cli() ||
    isset($GLOBALS['devel_shutdown']) ||
    strstr($_SERVER['PHP_SELF'], 'update.php') ||
    (isset($_GET['q']) && (
      in_array($_GET['q'], array('upload/js', 'admin/content/node-settings/rebuild')) ||
      substr($_GET['q'], 0, strlen('system/files')) == 'system/files' ||
      substr($_GET['q'], 0, strlen('batch')) == 'batch')
    );
}

/**
 * Implementation of hook_boot(). Runs even for cached pages.
 */
function devel_boot() {
  if (!devel_silent()) {
    devel_start();
// kickoff all our tricks. put here all code which must run for ached pages too. called from both hook_boot and hook_init.
function devel_start() {
  if (variable_get('dev_mem', 0) && function_exists('memory_get_usage')) {
    global $memory_init;
    $memory_init = memory_get_usage();
  }

  // we need user_access() in the shutdown function. make sure it gets loaded
  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);
/**
 * Implementation of hook_perm().
 */
function devel_perm() {
Jeff Robbins's avatar
Jeff Robbins committed
  return array('access devel information', 'execute php code', 'switch users', 'display source code');
}

/**
 * Implementation of hook_block().
 */
function devel_block($op = 'list', $delta = 0, $edit = array()) {
  if ($op == 'list') {
    $blocks[0]['info'] = t('Switch user');
    $blocks[2]['info'] = t('Execute PHP');
    return $blocks;
  }
  elseif ($op == 'configure' && $delta == 0) {
    $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;
  }
  elseif ($op == 'save' && $delta == 0) {
    variable_set('devel_switch_user_list_size', $edit['devel_switch_user_list_size']);
  }
Moshe Weitzman's avatar
Moshe Weitzman committed
    $block = array();
    switch ($delta) {
      case 0:
        $block = devel_block_switch_user();
        // Deleted in favor of custom menu. do not reuse this index.
        break;
      case 2:
        if (user_access('execute php code')) {
          $block['subject'] = t('Execute PHP');
          $block['content'] = drupal_get_form('devel_execute_form');
    return $block;
  }
}
function devel_block_switch_user() {
  $links = devel_switch_user_list();
  if (!empty($links)) {
    $block['subject'] = t('Switch user');
    $block['content'] = theme('links', $links);
    $block['content'] .= drupal_get_form('devel_switch_user_form');
    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);
    }
    while ($user = db_fetch_object($users)) {
      $links[$user->uid] = array(
        'title' => theme('placeholder', $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);
      while (($user = db_fetch_object($users)) && count($links) < $list_size) {
        $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_phpinfo() {
  print phpinfo();
  exit;
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(array('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'];
 * TODO: i switched params as per http://drupal.org/node/144132#form-alter but needs work still
 * Implementation of hook_form_alter().
 */
function devel_form_alter(&$form, $form_state, $form_id, $key_in = NULL) {
  if (user_access('access devel information') && variable_get('devel_form_weights', 0)) {
    $children = element_children($form);
    if (empty($children)) {
      if (isset($form['#type']) && !in_array($form['#type'], array('value', 'hidden'))) {
        if (!isset($form['#title'])) {
          $form['#title'] = '';
        }
        $form['#title'] .= " (key=$key_in, weight=". (isset($form['#weight']) ? $form['#weight'] : 0) .')';
      }
    }
    else {
      foreach (element_children($form) as $key) {
        // We need to add the weight to fieldsets.
        if (element_children($form[$key])) { // Which are a container of others.
          if (!isset($form[$key]['#title'])) {
            $form[$key]['#title'] = '';
          }
          $form[$key]['#title'] .= " (key=$key, weight=". (isset($form[$key]['#weight']) ? $form[$key]['#weight'] : 0) .')';
        devel_form_alter($form[$key], $form_state, $form_id, $key);
  if (isset($destination) && !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)) {
      $output = t_safe('<p>The user is being redirected to <a href="@destination">@destination</a>.</p>', array('@destination' => $destination));
      // Don't allow the automatic redirect to happen.
      drupal_page_footer();
      exit();
    }
      $GLOBALS['devel_redirecting'] = TRUE;
Moshe Weitzman's avatar
Moshe Weitzman committed
// Borrowed from Drush.
function devel_verify_cli() {
  if (php_sapi_name() == 'cgi') {
    return (is_numeric($_SERVER['argc']) && $_SERVER['argc'] > 0);
  }
Moshe Weitzman's avatar
Moshe Weitzman committed
  return (php_sapi_name() == 'cli');
}


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

/**
 * 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'])) {
    // Try not to break non html pages.
    if (function_exists('drupal_get_headers')) {
      $headers = drupal_get_headers();
      $formats = array('xml', 'javascript', 'json', 'plain', 'image', 'application', 'csv', 'x-comma-separated-values');
      foreach ($formats as $format) {
        if (strstr($headers, $format)) {
          return;
        }
    if (isset($user) && user_access('access devel information')) {
      list($counts, $query_summary) = devel_query_summary();
      // Query log off, timer on.
      if (!variable_get('devel_query_display', 0) && variable_get('dev_timer', 0)) {
        $output .= '<div class="dev-timer">'. devel_timer() .' '. $query_summary .'</div>';
      // Query log on.
      $sum = 0;
      if (variable_get('devel_query_display', FALSE)) {
        $output .= '<div class="dev-query">';
        $output .= $query_summary;
        // calling theme() during shutdown is very bad if registry gets rebuilt like when making a change on  admin/build/modules
        // so we check for presence of theme registry before calling theme()
        if (function_exists('theme_get_registry') && theme_get_registry()) {
          $txt = t_safe(' Queries taking longer than @threshold ms and queries executed more than once, are <span class="marker">highlighted</span>.', array('@threshold' => variable_get('devel_execution', 5)));
          if (variable_get('dev_timer', 0)) {
            $txt .= devel_timer();
          }
          $output .= $txt;
          $output .= '</div>';
          $output .= devel_query_table($queries, $counts);
          ob_start();
          dprint_r($queries);
          $output .= ob_get_clean();

      if (variable_get('dev_mem', FALSE) && function_exists('memory_get_usage')) {
        $memory_shutdown = memory_get_usage();
        $args = array('@memory_init' => round($memory_init / 1024 / 1024, 2), '@memory_shutdown' => round($memory_shutdown / 1024 / 1024, 2));
        $msg = '<div class="dev-memory-usage"><h3>Memory usage:</h3> Memory used at: devel_init()=<strong>@memory_init</strong> MB, devel_shutdown()=<strong>@memory_shutdown</strong> MB.</div>';
        // theme() may not be available. not t() either.
        $output .= t_safe($msg, $args);
      // TODO: gzip this text if we are sending a gzip page. see drupal_page_header().
  devel_store_queries();
}

function devel_store_queries() {
  if (variable_get('devel_store_queries', 0) && rand(1, variable_get('devel_store_random', 1)) == 1) {
    global $active_db, $queries, $db_type;

    $qids = array();
    $values = array();
    $fields = array();
    // We need this for the devel_queries insert below.
    setlocale(LC_NUMERIC, 'C');
    foreach ($queries as $value) {
      list($function, $query) = explode("\n", $value[0]);
      $query = preg_replace(array("/'.*'/s", "/\d.*\.\d.*/", "/\d.*/"), array("S", "F", "D"), $query);
      $hash = md5($function . $query);
      if (!isset($qids[$hash])) {
        $qids[$hash] = db_result(devel_db_query("SELECT qid FROM {devel_queries} WHERE hash = '%s'", $hash));
        if (!$qids[$hash]) {
          devel_db_query("INSERT INTO {devel_queries} (query, function, hash) VALUES ('%s', '%s', '%s')", $query, $function, $hash);
          db_last_insert_id('devel_queries', 'qid');
      $fields[] = "(%d, '%f')";
      $values[] = $qids[$hash];
    }
    if (count($fields)) {
      devel_db_query('INSERT INTO {devel_times} (qid, time) VALUES '. implode(',', $fields), $values);
    }
  }
}

  global $queries;
  if (variable_get('dev_query', FALSE) && is_array($queries)) {
    foreach ($queries as $query) {
      $text[] = $query[0];
      $sum += $query[1];
    }
    $counts = array_count_values($text);
    return array($counts, t_safe('Executed @queries queries in @time milliseconds.', 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);
  }
 * Returns a list of all currently defined user functions in the current
 * request lifecycle, with links their documentation.
 */
function devel_function_reference() {
  $functions = get_defined_functions();
  $version = devel_get_core_version(VERSION);
  $ufunctions = $functions['user'];
  sort($ufunctions);
  $api = variable_get('devel_api_url', 'api.drupal.org');
  foreach ($ufunctions as $function) {
    $links[] = l($function, "http://$api/api/$version/function/$function");
  }
  return theme('item_list', $links);
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];
  }
function devel_db_query($query) {
  global $active_db;
  $args = func_get_args();
  array_shift($args);
  $query = db_prefix_tables($query);
  if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
    $args = $args[0];
  }
  _db_query_callback($args, TRUE);
  $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
  return db_query($query, $active_db);
// See http://drupal.org/node/126098
function devel_is_compatible_optimizer() {
   ob_start();
   phpinfo();
   $info = ob_get_contents();
   ob_end_clean();

   // Match the Zend Optimezer 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['queries'] = array('#type' => 'fieldset', '#title' => t('Query log'));
  $description = t("Collect query info. If disabled, no query log functionality will work.");
  if (!devel_is_compatible_optimizer()) {
    $description = t('You must disable or upgrade the php Zend Optimizer extension in order to enable this feature. The minimum required version is 3.2.8. Earlier versions of Zend Optimizer are <a href="!url">horribly buggy and segfault your Apache</a> ... ', array('!url' => url('http://drupal.org/node/126098'))) . $description;
  $form['queries']['dev_query'] = array('#type' => 'checkbox',
    '#title' => t('Collect query info'),
    '#default_value' => variable_get('dev_query', 0),
    '#disabled' => !devel_is_compatible_optimizer() ? TRUE : FALSE,
    '#description' => $description,
  );
  $form['queries']['devel_query_display'] = array('#type' => 'checkbox',
    '#title' => t('Display query log'),
    '#default_value' => variable_get('devel_query_display', 0),
    '#description' => t('Display a log of the database queries needed to generate the current page, and the execution time for each. Also, queries which are repeated during a single page view are summed in the # column, and printed in red since they are candidates for caching.'));
  $form['queries']['devel_query_sort'] = array('#type' => 'radios',
    '#title' => t('Sort query log'),
    '#default_value' =>   variable_get('devel_query_sort', DEVEL_QUERY_SORT_BY_SOURCE),
    '#options' => array(t('by source'), t('by duration')),
    '#description' => t('The query table can be sorted in the order that the queries were executed or by descending duration.'),
  );
  $form['queries']['devel_execution'] = array('#type' => 'textfield',
    '#title' => t('Slow query highlighting'),
    '#default_value' => variable_get('devel_execution', 5),
    '#size' => 4,
    '#maxlength' => 4,
    '#description' => t('Enter an integer in milliseconds. Any query which takes longer than this many milliseconds will be highlighted in the query log. This indicates a possibly inefficient query, or a candidate for caching.'),
  );
  $form['queries']['devel_store_queries'] = array('#type' => 'checkbox',
    '#title' => t('Store executed queries'),
    '#default_value' => variable_get('devel_store_queries', 0),
    '#description' => t('Store statistics about executed queries. See the devel_x tables.'));
  $form['queries']['devel_store_random'] = array('#type' => 'textfield',
    '#title' => t('Sampling interval'),
    '#default_value' => variable_get('devel_store_random', 1),
    '#size' => 4,
    '#description' => t('If storing query statistics, only store every nth page view. 1 means every page view, 2 every second, and so on.'));

  $form['devel_api_url'] = array('#type' => 'textfield',
    '#title' => t('API Site'),
    '#default_value' => variable_get('devel_api_url', 'api.drupal.org'),
    '#description' => t('The base URL for your developer documentation links. You might change this if you run <a href="!url">api.module</a> locally.', array('!url' => url('http://drupal.org/project/api'))));
  $form['dev_timer'] = array('#type' => 'checkbox',
    '#title' => t('Display page timer'),
    '#default_value' => variable_get('dev_timer', 0),
    '#description' => t('Display page execution time in the query log box.'),
  );
  $form['dev_mem'] = array('#type' => 'checkbox',
    '#title' => t('Display memory usage'),
    '#default_value' => variable_get('dev_mem', 0),
    '#description' => t('Display how much memory is used to generate the current page. This will show memory usage when devel_init() is called and when devel_exit() is called. PHP must have been compiled with the <em>--enable-memory-limit</em> configuration option for this feature to work.'),
  $form['devel_redirect_page'] = array('#type' => 'checkbox',
    '#title' => t('Display redirection page'),
    '#default_value' => variable_get('devel_redirect_page', 0),
    '#description' => t('When a module executes drupal_goto(), the query log and other developer information is lost. Enabling this setting presents an intermediate page to developers so that the log can be examined before continuing to the destination page.'),
  );
  $form['devel_form_weights'] = array('#type' => 'checkbox',
    '#title' => t('Display form element keys and weights'),
    '#default_value' => variable_get('devel_form_weights', 0),
    '#description' => t('Form element names are needed for performing themeing or altering a form. Their weights determine the position of the element. Enabling this setting will show these keys and weights beside each form item.'),
  $form['devel_error_handler'] = array('#type' => 'radios',
    '#title' => t('Error handler'),
    '#default_value' => variable_get('devel_error_handler', DEVEL_ERROR_HANDLER_STANDARD),
    '#options' => array(DEVEL_ERROR_HANDLER_NONE => t('None'), DEVEL_ERROR_HANDLER_STANDARD => t('Standard drupal')),
    '#description' => t('Choose an error handler for your site. <em>Backtrace</em> prints nice debug information when an error is noticed, and you <a href="@choose">choose to show errors on screen</a>. <strong>Backtrace requires the <a href="@krumo">krumo library</a></strong>. <em>None</em> is a good option when stepping through the site in your debugger.', array('@krumo' => url('http://krumo.sourceforge.net'), '@choose' => url('admin/settings/error-reporting'))),