Skip to content
devel.module 60.7 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>';
 * 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'),
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'),
  $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'),
    'menu_name' => 'devel',
  );
    '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/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',
  );
  $items['admin/config/development/devel'] = 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'),
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,
  $items['comment/%comment/devel/load'] = array(
    'title' => 'Dev load',
    'page callback' => 'devel_load_object',
    'page arguments' => array(1, 'comment'),
    'access callback' => 'user_access',
    'access arguments' => array('access devel information'),
    'type' => MENU_LOCAL_TASK,
    'weight' => 100,
  );
  $items['comment/%comment/devel/render'] = array(
    'title' => 'Dev render',
    'page callback' => 'devel_render_object',
    'page arguments' => array('comment', 1),
    'access callback' => 'user_access',
    'access arguments' => array('access devel information'),
    'type' => MENU_LOCAL_TASK,
    'weight' => 100,
  );
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,
  $items['taxonomy/term/%taxonomy_term/devel/load'] = array(
    'title' => 'Dev load',
    'page callback' => 'devel_load_object',
    'page arguments' => array(2, 'term'),
    'access callback' => 'user_access',
    'access arguments' => array('access devel information'),
    'type' => MENU_LOCAL_TASK,
    'weight' => 100,
  );
  $items['taxonomy/term/%taxonomy_term/devel/render'] = array(
    'title' => 'Dev render',
    'page callback' => 'devel_render_term',
    'page arguments' => array(2),
    'access callback' => 'user_access',
    'access arguments' => array('access devel information'),
    'type' => MENU_LOCAL_TASK,
    'weight' => 100,
  );
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) {
  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_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'));
  drupal_theme_rebuild();
Moshe Weitzman's avatar
Moshe Weitzman committed
  drupal_goto();
}

/**
 * Implementation of hook_theme()
 */
function devel_theme() { // &$cache, $type, $theme, $path
    '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
    // with doxygen
    // $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/
      
      // See http://www.firephp.org/.
      // Support Libraries API - http://drupal.org/project/libraries
      if (module_exists('libraries')) {
        $path = libraries_get_path('FirePHPCore') . '/lib/FirePHPCore/fb.php';
      }
      else {
        $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_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);
  }
}


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() ||
    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('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 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()) {
    //TODO: How best to include this?
    @include_once DRUPAL_ROOT . '/includes/database/log.inc';
    Database::startLog('devel');;
  }
  if (devel_code_coverage_enabled()) {
    xdebug_start_code_coverage();
  }
  // 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) {
      $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);
      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_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_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'];
 * 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));
      drupal_set_page_content($output);
      $page = element_info('page');
      print drupal_render_page($page);
      // Don't allow the automatic redirect to happen.
      drupal_page_footer();
      exit();
    }
      $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_verify_cli() {
  if (php_sapi_name() == 'cgi') {
    return (is_numeric($_SERVER['argc']) && $_SERVER['argc'] > 0);
  }
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_header')) {
      if ($headers = drupal_get_header()) {
        $formats = array('xml', 'javascript', 'json', 'plain', 'image', 'application', 'csv', 'x-comma-separated-values');
        foreach ($formats as $format) {
          if (strstr($headers['content-type'], $format)) {
            return;
          }
    if (isset($user) && user_access('access devel information')) {
      if (devel_query_enabled()) {
        $output .= devel_shutdown_query();
      if (variable_get('dev_mem', FALSE)) {
        $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 = '<div class="dev-memory-usage"><h3>Memory usage:</h3> Memory used at: devel_boot()=<strong>@memory_boot</strong> MB, devel_shutdown()=<strong>@memory_shutdown</strong> MB, PHP peak usage=<strong>@memory_peak</strong> MB.</div>';
        // theme() may not be available. not t() either.
        $output .= t_safe($msg, $args);
      // Code coverage reporting.
      if (devel_code_coverage_enabled()) {
        $mode = variable_get('devel_code_coverage', FALSE);
        $coverage = xdebug_get_code_coverage();
        if ($mode == 1) {
          $output .= dpr(array_keys($coverage), TRUE);
          $output .= dpr($coverage, TRUE);
      // 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.
  $queries = Database::getLog('devel', 'default');
  list($counts, $query_summary) = devel_query_summary($queries);
  // 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);
      $output .= $txt . '</div>' . dprint_r($queries, TRUE);
  return $output;
}

function devel_query_enabled() {
  return method_exists('Database', 'getLog') && variable_get('dev_query', FALSE);
}

function devel_code_coverage_enabled() {
  return function_exists('xdebug_get_code_coverage') && variable_get('devel_code_coverage', FALSE);
function devel_query_summary($queries) {
  if (variable_get('dev_query', 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 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];
  }
// 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;