'. t('This is a list of defined user functions that generated this current request lifecycle. Click on a function name to view its documention.') .'

'; case 'devel/session': return '

'. t('Here are the contents of your $_SESSION variable.') .'

'; case 'devel/variable': $api = variable_get('devel_api_url', 'api.drupal.org'); return '

'. t('This is a list of the variables and their values currently stored in variables table and the $conf array of your settings.php file. These variables are usually accessed with variable_get() and variable_set(). 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")) .'

'; case 'devel/reinstall': return t('Warning - will delete your module tables and variables.'); } } /** * Implementationation of hook_menu(). */ function devel_menu() { $items = array(); // 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'), 'file' => 'devel.pages.inc', 'menu_name' => 'devel', ); $items['devel/reference'] = array( 'title' => 'Function reference', 'description' => 'View a list of currently defined user functions with documentation links.', 'page callback' => 'devel_function_reference', 'access arguments' => array('access devel information'), 'file' => 'devel.pages.inc', 'menu_name' => 'devel', ); $items['devel/reinstall'] = array( 'title' => 'Reinstall modules', 'page callback' => 'drupal_get_form', 'page arguments' => array('devel_reinstall'), 'description' => 'Run hook_uninstall() and then hook_install() for a given module.', 'access arguments' => array('access devel information'), 'file' => 'devel.pages.inc', 'menu_name' => 'devel', ); $items['devel/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, 'file' => 'devel.pages.inc', 'menu_name' => 'devel', ); $items['devel/menu/reset'] = array( 'title' => 'Rebuild menus', 'description' => 'Rebuild menu based on hook_menu() and revert any custom changes. All menu items return to their default settings.', 'page callback' => 'drupal_get_form', 'page arguments' => array('devel_menu_rebuild'), 'access arguments' => array('access devel information'), 'file' => 'devel.pages.inc', 'menu_name' => 'devel', ); $items['devel/menu/item'] = array( 'title' => 'Menu item', 'description' => 'Details about a given menu item.', 'page callback' => 'devel_menu_item', 'access arguments' => array('access devel information'), 'file' => 'devel.pages.inc', 'menu_name' => 'devel', ); $items['devel/variable'] = array( 'title' => 'Variable editor', 'description' => 'Edit and delete site variables.', 'page callback' => 'devel_variable_page', 'access arguments' => array('access devel information'), 'file' => 'devel.pages.inc', 'menu_name' => 'devel', ); // we don't want the abbreviated version provided by status report $items['devel/phpinfo'] = array( 'title' => 'PHPinfo()', 'description' => 'View your server\'s PHP configuration', 'page callback' => 'devel_phpinfo', 'access arguments' => array('access devel information'), 'file' => 'devel.pages.inc', 'menu_name' => 'devel', ); $items['devel/php'] = array( 'title' => 'Execute PHP Code', 'description' => 'Execute some PHP code', 'page callback' => 'drupal_get_form', 'page arguments' => array('devel_execute_form'), 'access arguments' => array('execute php code'), 'file' => 'devel.pages.inc', 'menu_name' => 'devel', ); $items['devel/theme/registry'] = array( 'title' => 'Theme registry', 'description' => 'View a list of available theme functions across the whole site.', 'page callback' => 'devel_theme_registry', 'access arguments' => array('access devel information'), 'file' => 'devel.pages.inc', 'menu_name' => 'devel', ); $items['devel/field/info'] = array( 'title' => 'Field info', 'description' => 'View fields information across the whole site.', 'page callback' => 'devel_field_info_page', 'access arguments' => array('access devel information'), 'file' => 'devel.pages.inc', 'menu_name' => 'devel', ); $items['devel/elements'] = array( 'title' => 'Hook_elements()', 'description' => 'View the active form/render elements for this site.', 'page callback' => 'devel_elements_page', 'access arguments' => array('access devel information'), 'file' => 'devel.pages.inc', 'menu_name' => 'devel', ); $items['devel/variable/edit/%'] = array( 'title' => 'Variable editor', 'page callback' => 'drupal_get_form', 'page arguments' => array('devel_variable_edit', 3), 'access arguments' => array('access devel information'), 'type' => MENU_CALLBACK, 'file' => 'devel.pages.inc', 'menu_name' => 'devel', ); $items['devel/session'] = array( 'title' => 'Session viewer', 'description' => 'List the contents of $_SESSION.', 'page callback' => 'devel_session', 'access arguments' => array('access devel information'), 'file' => 'devel.pages.inc', 'menu_name' => 'devel', ); $items['devel/switch'] = array( 'title' => 'Switch user', 'page callback' => 'devel_switch_user', 'access arguments' => array('switch users'), 'type' => MENU_CALLBACK, 'file' => 'devel.pages.inc', 'menu_name' => 'devel', ); $items['devel/explain'] = array( 'title' => 'Explain query', 'page callback' => 'devel_querylog_explain', 'description' => 'Run an EXPLAIN on a given query. Used by query log', 'access arguments' => array('access devel information'), 'file' => 'devel.pages.inc', 'type' => MENU_CALLBACK ); $items['devel/arguments'] = array( 'title' => 'Arguments query', 'page callback' => 'devel_querylog_arguments', 'description' => 'Return a giben query, with arguments instead of placeholders. Used by query log', 'access arguments' => array('access devel information'), 'file' => 'devel.pages.inc', 'type' => MENU_CALLBACK ); $items['devel/run-cron'] = array( 'title' => 'Run cron', 'page callback' => 'system_run_cron', 'access arguments' => array('administer site configuration'), 'file' => 'system.admin.inc', 'file path' => drupal_get_path('module', 'system'), 'menu_name' => 'devel', ); // Duplicate path in 2 different menus. See http://drupal.org/node/601788. $items['devel/settings'] = array( 'title' => 'Devel settings', 'description' => 'Helper functions, pages, and blocks to assist Drupal developers. The devel blocks can be managed via the block administration page.', 'page callback' => 'drupal_get_form', 'page arguments' => array('devel_admin_settings'), 'access arguments' => array('administer site configuration'), 'file' => 'devel.admin.inc', 'menu_name' => 'devel', ); $items['admin/config/development/devel'] = array( 'title' => 'Devel settings', 'description' => 'Helper functions, pages, and blocks to assist Drupal developers. The devel blocks can be managed via the block administration page.', 'page callback' => 'drupal_get_form', 'page arguments' => array('devel_admin_settings'), 'file' => 'devel.admin.inc', 'access arguments' => array('administer site configuration'), ); $items['node/%node/devel'] = array( 'title' => 'Devel', 'page callback' => 'devel_load_object', 'page arguments' => array(1, 'node'), 'access arguments' => array('access devel information'), 'type' => MENU_LOCAL_TASK, 'file' => 'devel.pages.inc', 'weight' => 100, ); $items['node/%node/devel/load'] = array( 'title' => 'Load', 'page callback' => 'devel_load_object', 'page arguments' => array(1, 'node'), 'access arguments' => array('access devel information'), 'file' => 'devel.pages.inc', 'type' => MENU_DEFAULT_LOCAL_TASK, ); $items['node/%node/devel/render'] = array( 'title' => 'Render', 'page callback' => 'devel_render_object', 'page arguments' => array('node', 1), 'access arguments' => array('access devel information'), 'file' => 'devel.pages.inc', 'type' => MENU_LOCAL_TASK, 'weight' => 100, ); $items['comment/%comment/devel'] = array( 'title' => 'Devel', 'page callback' => 'devel_load_object', 'page arguments' => array(1, 'comment'), 'access arguments' => array('access devel information'), 'type' => MENU_LOCAL_TASK, 'file' => 'devel.pages.inc', 'weight' => 100, ); $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, 'file' => 'devel.pages.inc', ); $items['comment/%comment/devel/render'] = array( 'title' => 'Render', 'page callback' => 'devel_render_object', 'page arguments' => array('comment', 1), 'access arguments' => array('access devel information'), 'type' => MENU_LOCAL_TASK, 'file' => 'devel.pages.inc', 'weight' => 100, ); $items['user/%user/devel'] = array( 'title' => 'Devel', 'page callback' => 'devel_load_object', 'page arguments' => array(1, 'user'), 'access arguments' => array('access devel information'), 'type' => MENU_LOCAL_TASK, 'file' => 'devel.pages.inc', 'weight' => 100, ); $items['user/%user/devel/load'] = array( 'title' => 'Load', 'page callback' => 'devel_load_object', 'page arguments' => array(1, 'user'), 'access arguments' => array('access devel information'), 'file' => 'devel.pages.inc', 'type' => MENU_DEFAULT_LOCAL_TASK, ); $items['user/%user/devel/render'] = array( 'title' => 'Render', 'page callback' => 'devel_render_object', 'page arguments' => array('user', 1), 'access arguments' => array('access devel information'), 'file' => 'devel.pages.inc', 'type' => MENU_LOCAL_TASK, 'weight' => 100, ); $items['taxonomy/term/%taxonomy_term/devel'] = array( 'title' => 'Devel', 'page callback' => 'devel_load_object', 'page arguments' => array(2, 'term'), 'access arguments' => array('access devel information'), 'file' => 'devel.pages.inc', 'type' => MENU_LOCAL_TASK, 'weight' => 100, ); $items['taxonomy/term/%taxonomy_term/devel/load'] = array( 'title' => 'Load', 'page callback' => 'devel_load_object', 'page arguments' => array(2, 'term'), 'access arguments' => array('access devel information'), 'file' => 'devel.pages.inc', 'type' => MENU_DEFAULT_LOCAL_TASK, ); $items['taxonomy/term/%taxonomy_term/devel/render'] = array( 'title' => 'Render', 'page callback' => 'devel_render_term', 'page arguments' => array(2), 'access arguments' => array('access devel information'), 'type' => MENU_LOCAL_TASK, 'file' => 'devel.pages.inc', 'weight' => 100, ); return $items; } 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() */ function devel_theme() { return array( 'devel_querylog' => array( 'variables' => array('header' => array(), 'rows' => array()), ), 'devel_querylog_row' => 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/ has_krumo(); // See http://www.firephp.org/. // Support Libraries API - http://drupal.org/project/libraries if (module_exists('libraries')) { $path = libraries_get_path('FirePHPCore') . '/lib/FirePHPCore/'; } else { $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 turn off 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'; if (function_exists('krumo') && php_sapi_name() != 'cli') { return TRUE; } else { return FALSE; } } /** * 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: if (has_krumo()) { 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. return 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' || 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()) { @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; if ($errno & (E_ALL ^ E_NOTICE)) { // 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; } krumo($nicetrace); } watchdog('php', '%message in %file on line %line.', array('%error' => $types[$errno], '%message' => $message, '%file' => $filename, '%line' => $line), WATCHDOG_ERROR); } } /** * Implement hook_permission(). */ function devel_permission() { return array( 'access devel information' => array( 'description' => t('View developer output like variable printouts, query log, etc.'), 'title' => t('Access developer information'), ), '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, ); return $blocks; } /** * Implementation of hook_block_configure(). */ function devel_block_configure($delta) { 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; } } function devel_block_save($delta, $edit = array()) { 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) { case 'switch_user': $block = devel_block_switch_user(); break; case 'execute_php': if (user_access('execute php code')) { $block['subject'] = t('Execute PHP'); $block['content'] = drupal_get_form('devel_execute_form'); } break; } return $block; } function devel_block_switch_user() { $links = devel_switch_user_list(); if (!empty($links)) { $block['subject'] = t('Switch user'); $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' => 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.')), ); } } } return $links; } function devel_switch_user_form() { $form['username'] = array( '#type' => 'textfield', '#description' => t('Enter username'), '#autocomplete_path' => 'user/autocomplete', '#maxlength' => USERNAME_MAX_LENGTH, '#size' => 16, ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Switch'), ); return $form; } function devel_doc_function_form() { $version = devel_get_core_version(VERSION); $form['function'] = array( '#type' => 'textfield', '#description' => t('Enter function name for api lookup'), '#size' => 16, '#maxlength' => 255, ); $form['version'] = array('#type' => 'value', '#value' => $version); $form['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) { global $user; if (isset($path) && !devel_silent()) { // The page we are leaving is a drupal_goto(). Present a redirection page // so that the developer can see the intermediate query log. // We don't want to load user module here, so keep function_exists() call. if (isset($user) && function_exists('user_access') && user_access('access devel information') && variable_get('devel_redirect_page', 0)) { $destination = function_exists('url') ? url($path, $options) : $path; $output = t_safe('

The user is being redirected to @destination.

', array('@destination' => $destination)); drupal_deliver_page($output); // Don't allow the automatic redirect to happen. exit(); } else { $GLOBALS['devel_redirecting'] = TRUE; } } } /** * See devel_start() which registers this function as a shutdown function. */ function devel_shutdown() { // 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() { global $user; $output = $txt = ''; // Set $GLOBALS['devel_shutdown'] = FALSE in order to supress the // devel footer for a page. Not necessary if your page outputs any // of the Content-type http headers tested below (e.g. text/xml, // text/javascript, etc). This is 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_http_header')) { $header = drupal_get_http_header('content-type'); if ($header) { $formats = array('xml', 'javascript', 'json', 'plain', 'image', 'application', 'csv', 'x-comma-separated-values'); foreach ($formats as $format) { if (strstr($header, $format)) { return; } } } } if (isset($user) && user_access('access devel information')) { $queries = Database::getLog('devel', 'default'); $output .= devel_shutdown_summary($queries); $output .= devel_shutdown_query($queries); } if ($output) { // TODO: gzip this text if we are sending a gzip page. see drupal_page_header(). // For some reason, this is not actually printing for cached pages even though it gets executed // and $output looks good. print $output; } } } function devel_shutdown_summary($queries) { $sum = 0; $output = ''; list($counts, $query_summary) = devel_query_summary($queries); $output .= '
'; if (variable_get('devel_query_display', FALSE)) { // Query log on. $output .= $query_summary; $output .= t_safe(' Queries exceeding @threshold ms are highlighted.', array('@threshold' => variable_get('devel_execution', 5))); } if (variable_get('dev_timer', 0)) { $output .= devel_timer(); } $output .= devel_shutdown_memory(); $output .= '
'; return $output; } 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 = ' Memory used at: devel_boot()=@memory_boot MB, devel_shutdown()=@memory_shutdown MB, PHP peak=@memory_peak MB.'; // 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); $output = devel_query_table($queries, $counts); // Save all queries to a file in temp dir. Retrieved via AJAX. devel_query_put_contents($queries); } else { $output = '' . dprint_r($queries, TRUE); } 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, 'queries' => $queries ); print '\n"; } function devel_query_enabled() { return method_exists('Database', 'getLog') && variable_get('devel_query_display', FALSE); } function devel_query_summary($queries) { if (variable_get('devel_query_display', FALSE) && is_array($queries)) { $sum = 0; foreach ($queries as $query) { $text[] = $query['query']; $sum += $query['time']; } $counts = array_count_values($text); return array($counts, t_safe('Executed @queries queries in @time ms.', array('@queries' => count($queries), '@time' => round($sum * 1000, 2)))); } } 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 Optimizer v([0-9])\.([0-9])\.([0-9])/', $info, $matches); if ($matches) { $major = $matches[1]; $minor = $matches[2]; $build = $matches[3]; if ($major >= 3) { if ($minor >= 3) { return TRUE; } elseif ($minor == 2 && $build >= 8) { return TRUE; } else { return FALSE; } } else { return FALSE; } } else { return TRUE; } } /** * Generates the execute block form. */ function devel_execute_form() { $form['code'] = array( '#type' => 'textarea', '#title' => t('PHP code to execute'), '#description' => t('Enter some code. Do not use <?php ?> tags.') ); $form['op'] = array('#type' => 'submit', '#value' => t('Execute')); $form['#redirect'] = FALSE; $form['#skip_duplicate_check'] = TRUE; return $form; } /** * Process PHP execute form submissions. */ function devel_execute_form_submit($form, &$form_state) { ob_start(); print eval($form_state['values']['code']); dsm(ob_get_clean()); } /** * Switch from original user to another user and back. * We don't call session_save_session() because we really want to change users. Usually unsafe! * * @param $name The username to switch to. */ function devel_switch_user($name = NULL) { global $user; static $orig_user = array(); if (isset($name)) { $user = user_load_by_name($name); } // Retrieve the initial user. Can be called multiple times. elseif (count($orig_user)) { $user = array_shift($orig_user); array_unshift($orig_user, $user); } // Store the initial user. else { $orig_user[] = $user; } drupal_goto(); } /** * Print an object or array using either Krumo (if installed) or devel_print_object() * * @param $object * array or object to print * @param $prefix * prefixing for output items */ function kdevel_print_object($object, $prefix = NULL) { return has_krumo() ? krumo_ob($object) : devel_print_object($object, $prefix); } // Save krumo htlm using output buffering. function krumo_ob($object) { ob_start(); krumo($object); $output = ob_get_contents(); ob_end_clean(); return $output; } /** * Display an object or array * * @param $object * the object or array * @param $prefix * prefix for the output items (example "$node->", "$user->", "$") * @param $header * set to FALSE to suppress the output of the h3 */ function devel_print_object($object, $prefix = NULL, $header = TRUE) { drupal_add_css(drupal_get_path('module', 'devel') .'/devel.css'); $output = '
'; if ($header) { $output .= '

'. t('Display of !type !obj', array('!type' => str_replace(array('$', '->'), '', $prefix), '!obj' => gettype($object))) .'

'; } $output .= _devel_print_object($object, $prefix); $output .= '
'; return $output; } /** * Recursive (and therefore magical) function goes through an array or object and * returns a nicely formatted listing of its contents. * * @param $obj * array or object to recurse through * @param $prefix * prefix for the output items (example "$node->", "$user->", "$") * @param $parents * used by recursion * @param $object * used by recursion * @return * fomatted html * * @todo * currently there are problems sending an array with a varname */ function _devel_print_object($obj, $prefix = NULL, $parents = NULL, $object = FALSE) { static $root_type, $out_format; // TODO: support objects with references. See http://drupal.org/node/234581. if (isset($obj->view)) { return; } if (!isset($root_type)) { $root_type = gettype($obj); if ($root_type == 'object') { $object = TRUE; } } if (is_object($obj)) { $obj = (array)$obj; } if (is_array($obj)) { $output = "
\n"; foreach ($obj as $field => $value) { if ($field == 'devel_flag_reference') { continue; } if (!is_null($parents)) { if ($object) { $field = $parents .'->'. $field; } else { if (is_int($field)) { $field = $parents .'['. $field .']'; } else { $field = $parents .'[\''. $field .'\']'; } } } $type = gettype($value); $show_summary = TRUE; $summary = NULL; if ($show_summary) { switch ($type) { case 'string' : case 'float' : case 'integer' : if (strlen($value) == 0) { $summary = t("{empty}"); } elseif (strlen($value) < 40) { $summary = htmlspecialchars($value); } else { $summary = format_plural(drupal_strlen($value), '1 character', '@count characters'); } break; case 'array' : case 'object' : $summary = format_plural(count((array)$value), '1 element', '@count elements'); break; case 'boolean' : $summary = $value ? t('TRUE') : t('FALSE'); break; } } if (!is_null($summary)) { $typesum = '('. $type .', '. $summary .')'; } else { $typesum = '('. $type .')'; } $output .= ''; $output .= "
{$prefix}{$field} $typesum
\n"; $output .= "
\n"; // Check for references. if (is_array($value) && isset($value['devel_flag_reference'])) { $value['devel_flag_reference'] = TRUE; } // Check for references to prevent errors from recursions. if (is_array($value) && isset($value['devel_flag_reference']) && !$value['devel_flag_reference']) { $value['devel_flag_reference'] = FALSE; $output .= _devel_print_object($value, $prefix, $field); } elseif (is_object($value)) { $value->devel_flag_reference = FALSE; $output .= _devel_print_object((array)$value, $prefix, $field, TRUE); } else { $value = is_bool($value) ? ($value ? 'TRUE' : 'FALSE') : $value; $output .= htmlspecialchars(print_r($value, TRUE)) ."\n"; } $output .= "
\n"; } $output .= "
\n"; } return $output; } /** * Adds a table at the bottom of the page cataloguing data on all the database queries that were made to * generate the page. */ function devel_query_table($queries, $counts) { $version = devel_get_core_version(VERSION); $header = array ('ms', '#', 'where', 'ops', 'query', 'target'); $i = 0; $api = variable_get('devel_api_url', 'api.drupal.org'); foreach ($queries as $query) { $function = !empty($query['caller']['class']) ? $query['caller']['class'] . '::' : ''; $function .= $query['caller']['function']; $count = isset($counts[$query['query']]) ? $counts[$query['query']] : 0; $diff = round($query['time'] * 1000, 2); if ($diff > variable_get('devel_execution', 5)) { $cell[$i][] = array ('data' => $diff, 'class' => 'marker'); } else { $cell[$i][] = $diff; } $cell[$i][] = $count; $cell[$i][] = l($function, "http://$api/api/$version/function/$function"); $ops[] = l('P', '', array('attributes' => array('title' => 'Show placeholders', 'class' => 'dev-placeholders', 'qid' => $i))); $ops[] = l('A', '', array('attributes' => array('title' => 'Show arguments', 'class' => 'dev-arguments', 'qid' => $i))); // EXPLAIN only valid for select queries. if (strpos($query['query'], 'UPDATE') === FALSE && strpos($query['query'], 'INSERT') === FALSE && strpos($query['query'], 'DELETE') === FALSE) { $ops[] = l('E', '', array('attributes' => array('title' => 'Show EXPLAIN', 'class' => 'dev-explain', 'qid' => $i))); } $cell[$i][] = implode(' ', $ops); // 3 divs for each variation of the query. Last 2 are hidden by default. $placeholders = '
' . check_plain($query['query']) . "
\n"; $args = '' . "\n"; $explain = '' . "\n"; $cell[$i][] = array( 'id' => "devel-query-$i", 'data' => $placeholders . $args . $explain, ); $cell[$i][] = $query['target']; $i++; unset($diff, $count, $ops); } if (variable_get('devel_query_sort', DEVEL_QUERY_SORT_BY_SOURCE)) { usort($cell, '_devel_table_sort'); } return theme('devel_querylog', array('header' => $header, 'rows' => $cell)); } function theme_devel_querylog_row($variables) { $row = $variables['row']; $i = 0; $output = ''; foreach ($row as $cell) { $i++; if (is_array($cell)) { $data = !empty($cell['data']) ? $cell['data'] : ''; unset($cell['data']); $attr = $cell; } else { $data = $cell; $attr = array(); } if (!empty($attr['class'])) { $attr['class'] .= " cell cell-$i"; } else { $attr['class'] = "cell cell-$i"; } $attr = drupal_attributes($attr); $output .= "
$data
"; } return $output; } function theme_devel_querylog($variables) { $header = $variables['header']; $rows = $variables['rows']; $output = ''; if (!empty($header)) { $output .= "
"; $output .= theme('devel_querylog_row', array('row' => $header)); $output .= "
"; } if (!empty($rows)) { $i = 0; foreach ($rows as $row) { $i++; $zebra = ($i % 2) == 0 ? 'even' : 'odd'; $output .= "
"; $output .= theme('devel_querylog_row', array('row' => $row)); $output .= "
"; } } return $output; } function _devel_table_sort($a, $b) { $a = is_array($a[0]) ? $a[0]['data'] : $a[0]; $b = is_array($b[0]) ? $b[0]['data'] : $b[0]; if ($a < $b) { return 1; } if ($a > $b) { return -1; } return 0; } /** * Displays page execution time at the bottom of the page. */ function devel_timer() { $time = timer_read('page'); return t_safe(' Page execution time was @time ms.', array('@time' => $time)); } // An alias for drupal_debug(). function dd($data, $label = NULL) { return drupal_debug($data, $label); } // Log any variable to a drupal_debug.log in the site's temp directory. // See http://drupal.org/node/314112 function drupal_debug($data, $label = NULL) { ob_start(); print_r($data); $string = ob_get_clean(); if ($label) { $out = $label .': '. $string; } else { $out = $string; } $out .= "\n"; // The temp directory does vary across multiple simpletest instances. $file = file_directory_path('temporary') . '/drupal_debug.txt'; if (file_put_contents($file, $out, FILE_APPEND) === FALSE) { drupal_set_message(t('The file could not be written.'), 'error'); return FALSE; } } /** * Prints the arguments passed into the current function */ function dargs($always = TRUE) { static $printed; if ($always || !$printed) { $bt = debug_backtrace(); print kdevel_print_object($bt[1]['args']); $printed = TRUE; } } /** * Print a variable to the 'message' area of the page. Uses drupal_set_message() */ function dpm($input, $name = NULL) { if (user_access('access devel information')) { $export = kprint_r($input, TRUE, $name); drupal_set_message($export); } } /** * Var_dump() a variable to the 'message' area of the page. Uses drupal_set_message() */ function dvm($input, $name = NULL) { if (user_access('access devel information')) { $export = dprint_r($input, TRUE, $name, 'var_dump', FALSE); drupal_set_message($export); } } // legacy function that was poorly named. use dpm() instead, since the 'p' maps to 'print_r' function dsm($input, $name = NULL) { dpm($input, $name); } /** * An alias for dprint_r(). Saves carpal tunnel syndrome. */ function dpr($input, $return = FALSE, $name = NULL) { return dprint_r($input, $return, $name); } /** * An alias for kprint_r(). Saves carpal tunnel syndrome. */ function kpr($input, $return = FALSE, $name = NULL) { return kprint_r($input, $return, $name); } /** * Like dpr, but uses var_dump() instead */ function dvr($input, $return = FALSE, $name = NULL) { return dprint_r($input, $return, $name, 'var_dump', FALSE); } function kprint_r($input, $return = FALSE, $name = NULL, $function = 'print_r') { // We do not want to krumo() strings and integers and such if (merits_krumo($input)) { if (user_access('access devel information')) { return $return ? (isset($name) ? $name .' => ' : '') . krumo_ob($input) : krumo($input); } } else { return dprint_r($input, $return, $name, $function); } } /** * Pretty-print a variable to the browser (no krumo). * Displays only for users with proper permissions. If * you want a string returned instead of a print, use the 2nd param. */ function dprint_r($input, $return = FALSE, $name = NULL, $function = 'print_r', $check= TRUE) { if (user_access('access devel information')) { if ($name) { $name .= ' => '; } ob_start(); $function($input); $output = ob_get_clean(); if ($check) { $output = check_plain($output); } if (count($input, COUNT_RECURSIVE) > DEVEL_MIN_TEXTAREA) { // don't use fapi here because sometimes fapi will not be loaded $printed_value = "'; } else { $printed_value = '
'. $name . $output .'
'; } if ($return) { return $printed_value; } else { print $printed_value; } } } /** * Print the function call stack. */ function ddebug_backtrace() { if (user_access('access devel information')) { $trace = debug_backtrace(); array_shift($trace); foreach ($trace as $key => $value) { $rich_trace[$value['function']] = $value; } if (has_krumo()) { print krumo($rich_trace); } else { dprint_r($rich_trace); } } } // Delete all files in a dir. http://www.plus2net.com/php_tutorial/php-files-delete.php function devel_empty_dir($dir) { foreach (new DirectoryIterator($dir) as $fileInfo) { unlink($fileInfo->getPathname()); } } /* * migration related functions */ /** * Update node_comment_statistics table for nodes with comments. * TODO: if 2 comments have exact same timestamp, the function can get wrong uid and name fields. * Handles when comment timestamps have been manually set in admin * * @return void **/ function devel_rebuild_node_comment_statistics() { // Empty table db_delete('node_comment_statistics'); $sql = "INSERT INTO {node_comment_statistics} (nid, last_comment_timestamp, last_comment_name, last_comment_uid, comment_count) (SELECT nid, c.timestamp, name, uid, comment_count FROM {comments} c INNER JOIN (SELECT MAX(timestamp) AS timestamp, COUNT(*) AS comment_count FROM {comments} WHERE status=%d GROUP BY nid) AS c2 ON c.timestamp=c2.timestamp)"; db_query($sql, COMMENT_PUBLISHED); // Insert 0 count records into the node_comment_statistics for nodes that are missing. See comment_enable() db_query_temporary("SELECT n.nid, n.changed, n.uid FROM {node} n LEFT JOIN {node_comment_statistics} c ON n.nid = c.nid WHERE c.comment_count IS NULL", 'missing_nids'); db_query("INSERT INTO {node_comment_statistics} (nid, last_comment_timestamp, last_comment_name, last_comment_uid, comment_count) SELECT n.nid, n.changed, NULL, n.uid, 0 FROM missing_nids n"); }