devel.module

Same filename in other branches
  1. 7.x-1.x devel.module
  2. 4.x devel.module

This module holds functions useful for Drupal development.

Please contribute!

Devel is allowed to use its own functions kpr(), dpm() and dpq() so disable the coding standard which gives warnings for using these. phpcs:disable Drupal.Functions.DiscouragedFunctions

File

./devel.module

View source
<?php


/**
 * @file
 * This module holds functions useful for Drupal development.
 *
 * Please contribute!
 *
 * Devel is allowed to use its own functions kpr(), dpm() and dpq() so disable
 * the coding standard which gives warnings for using these.
 * phpcs:disable Drupal.Functions.DiscouragedFunctions
 */
define('DEVEL_ERROR_HANDLER_NONE', 0);
define('DEVEL_ERROR_HANDLER_STANDARD', 1);
define('DEVEL_ERROR_HANDLER_BACKTRACE_KINT', 2);
define('DEVEL_ERROR_HANDLER_BACKTRACE_DPM', 4);
use Drupal\Core\Database\Query\AlterableInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Logger\RfcLogLevel;
use Drupal\Core\Menu\LocalTaskDefault;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
use Drupal\Core\Utility\Error;
use Drupal\devel\EntityTypeInfo;
use Drupal\devel\ToolbarHandler;

/**
 * Implements hook_help().
 */
function devel_help($route_name, RouteMatchInterface $route_match) {
    switch ($route_name) {
        case 'help.page.devel':
            $output = '<h3>' . t('About') . '</h3>';
            $output .= '<p>' . t('The Devel module provides a suite of modules containing fun for module developers and themers. For more information, see the <a href=":url">online documentation for the Devel module</a>.', [
                ':url' => 'https://www.drupal.org/docs/contributed-modules/devel',
            ]) . '</p>';
            $output .= '<h3>' . t('Uses') . '</h3>';
            $output .= '<dl>';
            $output .= '<dt>' . t('Inspecting Service Container') . '</dt>';
            $output .= '<dd>' . t('The module allows you to inspect Services and Parameters registered in the Service Container. You can see those informations on <a href=":url">Container info</a> page.', [
                ':url' => Url::fromRoute('devel.container_info.service')->toString(),
            ]) . '</dd>';
            $output .= '<dt>' . t('Inspecting Routes') . '</dt>';
            $output .= '<dd>' . t('The module allows you to inspect routes information, gathering all routing data from <em>.routing.yml</em> files and from classes which subscribe to the route build/alter events. You can see those informations on <a href=":url">Routes info</a> page.', [
                ':url' => Url::fromRoute('devel.route_info')->toString(),
            ]) . '</dd>';
            $output .= '<dt>' . t('Inspecting Events') . '</dt>';
            $output .= '<dd>' . t('The module allow you to inspect listeners registered in the event dispatcher. You can see those informations on <a href=":url">Events info</a> page.', [
                ':url' => Url::fromRoute('devel.event_info')->toString(),
            ]) . '</dd>';
            $output .= '</dl>';
            return $output;
        case 'devel.container_info.service':
        case 'devel.container_info.parameter':
            return '<p>' . t('Displays Services and Parameters registered in the Service Container. For more informations on the Service Container, see the <a href=":url">Symfony online documentation</a>.', [
                ':url' => 'http://symfony.com/doc/current/service_container.html',
            ]) . '</p>';
        case 'devel.route_info':
            return '<p>' . t('Displays registered routes for the site. For a complete overview of the routing system, see the <a href=":url">online documentation</a>.', [
                ':url' => 'https://www.drupal.org/docs/drupal-apis/routing-system',
            ]) . '</p>';
        case 'devel.event_info':
            return '<p>' . t('Displays events and listeners registered in the event dispatcher. For a complete overview of the event system, see the <a href=":url">Symfony online documentation</a>.', [
                ':url' => 'http://symfony.com/doc/current/components/event_dispatcher.html',
            ]) . '</p>';
        case 'devel.reinstall':
            $output = '<p>' . t('<strong>Warning</strong> - will delete your module tables and configuration.') . '</p>';
            $output .= '<p>' . t('Uninstall and then install the selected modules. <code>hook_uninstall()</code> and <code>hook_install()</code> will be executed and the schema version number will be set to the most recent update number.') . '</p>';
            return $output;
        case 'devel/session':
            return '<p>' . t('Here are the contents of your <code>$_SESSION</code> variable.') . '</p>';
        case 'devel.state_system_page':
            return '<p>' . t('This is a list of state variables and their values. For more information read online documentation of <a href=":documentation">State API in Drupal 8</a>.', [
                ':documentation' => "https://www.drupal.org/docs/develop/drupal-apis/state-api",
            ]) . '</p>';
        case 'devel.layout_info':
            return '<p>' . t('Displays layouts available to the site. For a complete overview of the layout system, see the <a href=":url">Layout API documentation</a>.', [
                ':url' => 'https://www.drupal.org/docs/drupal-apis/layout-api',
            ]) . '</p>';
    }
}

/**
 * Implements hook_entity_type_alter().
 */
function devel_entity_type_alter(array &$entity_types) {
    Drupal::service('class_resolver')->getInstanceFromDefinition(EntityTypeInfo::class)
        ->entityTypeAlter($entity_types);
}

/**
 * Implements hook_entity_operation().
 */
function devel_entity_operation(EntityInterface $entity) {
    return Drupal::service('class_resolver')->getInstanceFromDefinition(EntityTypeInfo::class)
        ->entityOperation($entity);
}

/**
 * Implements hook_toolbar().
 */
function devel_toolbar() {
    return Drupal::service('class_resolver')->getInstanceFromDefinition(ToolbarHandler::class)
        ->toolbar();
}

/**
 * Implements hook_menu_links_discovered_alter().
 */
function devel_menu_links_discovered_alter(&$links) {
    // Conditionally add the Layouts info menu link.
    if (Drupal::moduleHandler()->moduleExists('layout_discovery')) {
        $links['devel.layout_info'] = [
            'title' => new TranslatableMarkup('Layouts Info'),
            'route_name' => 'devel.layout_info',
            'description' => new TranslatableMarkup('Overview of layouts available to the site.'),
            'menu_name' => 'devel',
        ];
    }
}

/**
 * Implements hook_local_tasks_alter().
 */
function devel_local_tasks_alter(&$local_tasks) {
    if (Drupal::moduleHandler()->moduleExists('toolbar')) {
        $local_tasks['devel.toolbar.settings_form'] = [
            'title' => 'Toolbar Settings',
            'base_route' => 'devel.admin_settings',
            'route_name' => 'devel.toolbar.settings_form',
            'class' => LocalTaskDefault::class,
            'options' => [],
        ];
    }
}

/**
 * Sets message.
 */
function devel_set_message($msg, $type = NULL) {
    if (function_exists('drush_log')) {
        drush_log($msg, $type);
    }
    else {
        Drupal::messenger()->addMessage($msg, $type, TRUE);
    }
}

/**
 * Gets error handlers.
 */
function devel_get_handlers() {
    $error_handlers = Drupal::config('devel.settings')->get('error_handlers');
    if (!empty($error_handlers)) {
        unset($error_handlers[DEVEL_ERROR_HANDLER_NONE]);
    }
    return $error_handlers;
}

/**
 * Sets a new error handler or restores the prior one.
 */
function devel_set_handler($handlers) {
    if (empty($handlers)) {
        restore_error_handler();
    }
    elseif (count($handlers) == 1 && isset($handlers[DEVEL_ERROR_HANDLER_STANDARD])) {
        // Do nothing.
    }
    else {
        set_error_handler('backtrace_error_handler');
    }
}

/**
 * Displays backtrace showing the route of calls to the current error.
 *
 * @param int $error_level
 *   The level of the error raised.
 * @param string $message
 *   The error message.
 * @param string $filename
 *   (optional) The filename that the error was raised in.
 * @param int $line
 *   (optional) The line number the error was raised at.
 * @param array $context
 *   (optional) An array that points to the active symbol table at the point the
 *   error occurred.
 */
function backtrace_error_handler($error_level, $message, $filename = NULL, $line = NULL, ?array $context = NULL) {
    // Hide stack trace and parameters from unqualified users.
    if (!Drupal::currentUser()->hasPermission('access devel information')) {
        // Do what core does in bootstrap.inc and errors.inc.
        // (We need to duplicate the core code here rather than calling it
        // to avoid having the backtrace_error_handler() on top of the call stack.)
        if ($error_level & error_reporting()) {
            $types = drupal_error_levels();
            [
                $severity_msg,
                $severity_level,
            ] = $types[$error_level];
            $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
            $caller = Error::getLastCaller($backtrace);
            // We treat recoverable errors as fatal.
            _drupal_log_error([
                '%type' => isset($types[$error_level]) ? $severity_msg : 'Unknown error',
                '@message' => $message,
                '%function' => $caller['function'],
                '%file' => $caller['file'],
                '%line' => $caller['line'],
                'severity_level' => $severity_level,
                'backtrace' => $backtrace,
            ], $error_level == E_RECOVERABLE_ERROR);
        }
        return;
    }
    // Don't respond to the error if it was suppressed with a '@'.
    if (error_reporting() == 0) {
        return;
    }
    // Don't respond to warning caused by ourselves.
    if (preg_match('#Cannot modify header information - headers already sent by \\([^\\)]*[/\\\\]devel[/\\\\]#', $message)) {
        return;
    }
    if ($error_level & error_reporting()) {
        // Only write each distinct NOTICE message once, as repeats do not give any
        // further information and can choke the page output.
        if ($error_level == E_NOTICE) {
            static $written = [];
            if (!empty($written[$line][$filename][$message])) {
                return;
            }
            $written[$line][$filename][$message] = TRUE;
        }
        $types = drupal_error_levels();
        [
            $severity_msg,
            $severity_level,
        ] = $types[$error_level];
        $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
        $caller = Error::getLastCaller($backtrace);
        $variables = [
            '%type' => isset($types[$error_level]) ? $severity_msg : 'Unknown error',
            '@message' => $message,
            '%function' => $caller['function'],
            '%file' => $caller['file'],
            '%line' => $caller['line'],
        ];
        $msg = t('%type: @message in %function (line %line of %file).', $variables);
        // Show message if error_level is ERROR_REPORTING_DISPLAY_SOME or higher.
        // (This is Drupal's error_level, which is different from $error_level,
        // and we purposely ignore the difference between _SOME and _ALL,
        // see #970688!)
        if (Drupal::config('system.logging')->get('error_level') != 'hide') {
            $error_handlers = devel_get_handlers();
            if (!empty($error_handlers[DEVEL_ERROR_HANDLER_STANDARD])) {
                Drupal::messenger()->addMessage($msg, $severity_level <= RfcLogLevel::NOTICE ? MessengerInterface::TYPE_ERROR : MessengerInterface::TYPE_WARNING, TRUE);
            }
            if (!empty($error_handlers[DEVEL_ERROR_HANDLER_BACKTRACE_KINT])) {
                $input = ddebug_backtrace(return: TRUE, pop: 1);
                print Drupal::service('devel.dumper')->dumpOrExport(input: $input, name: $msg);
            }
            if (!empty($error_handlers[DEVEL_ERROR_HANDLER_BACKTRACE_DPM])) {
                $input = ddebug_backtrace(return: TRUE, pop: 1);
                Drupal::service('devel.dumper')->message(input: $input, name: $msg, type: MessengerInterface::TYPE_WARNING);
            }
        }
        Drupal::logger('php')->log($severity_level, $msg);
    }
}

/**
 * Implements hook_page_attachments_alter().
 */
function devel_page_attachments_alter(array &$attachments) : void {
    if (Drupal::currentUser()->hasPermission('access devel information') && Drupal::config('devel.settings')->get('page_alter')) {
        Drupal::service('devel.dumper')->message(input: $attachments, name: 'attachments');
    }
}

/**
 * Dumps information about a variable.
 *
 * Wrapper for DevelDumperManager::dump().
 *
 * @param mixed $input
 *   The variable to dump.
 * @param string $name
 *   (optional) The label to output before variable, defaults to NULL.
 * @param string $plugin_id
 *   (optional) The plugin ID, defaults to NULL.
 *
 * @see \Drupal\devel\DevelDumperManager::dump()
 */
function devel_dump($input, $name = NULL, $plugin_id = NULL) {
    Drupal::service('devel.dumper')->dump($input, $name, $plugin_id);
}

/**
 * Returns a string representation of a variable.
 *
 * Wrapper for DevelDumperManager::export().
 *
 * @param mixed $input
 *   The variable to dump.
 * @param string $name
 *   (optional) The label to output before variable, defaults to NULL.
 * @param string $plugin_id
 *   (optional) The plugin ID, defaults to NULL.
 *
 * @return string
 *   String representation of a variable.
 *
 * @see \Drupal\devel\DevelDumperManager::export()
 */
function devel_export($input, $name = NULL, $plugin_id = NULL) {
    return Drupal::service('devel.dumper')->export($input, $name, $plugin_id);
}

/**
 * Sets a message with a string representation of a variable.
 *
 * Wrapper for DevelDumperManager::message().
 *
 * @param mixed $input
 *   The variable to dump.
 * @param string|null $name
 *   (optional) The label to output before variable, defaults to NULL.
 * @param string $type
 *   (optional) The message's type. Defaults to 'status'.
 * @param string|null $plugin_id
 *   (optional) The plugin ID, defaults to NULL.
 *
 * @see \Drupal\devel\DevelDumperManager::message()
 */
function devel_message(mixed $input, ?string $name = NULL, string $type = MessengerInterface::TYPE_STATUS, ?string $plugin_id = NULL) : void {
    Drupal::service('devel.dumper')->message(input: $input, name: $name, type: $type, plugin_id: $plugin_id);
}

/**
 * Logs a variable to a drupal_debug.txt in the site's temp directory.
 *
 * Wrapper for DevelDumperManager::debug().
 *
 * @param mixed $input
 *   The variable to log to the drupal_debug.txt log file.
 * @param string $name
 *   (optional) If set, a label to output before $data in the log file.
 * @param string $plugin_id
 *   (optional) The plugin ID, defaults to NULL.
 *
 * @return null|false
 *   Empty if successful, FALSE if the log file could not be written.
 *
 * @see \Drupal\devel\DevelDumperManager::debug()
 */
function devel_debug($input, $name = NULL, $plugin_id = NULL) {
    return Drupal::service('devel.dumper')->debug($input, $name, $plugin_id);
}

/**
 * Wrapper for DevelDumperManager::dump().
 *
 * Calls the http://www.firephp.org/ fb() function if it is found.
 *
 * @see \Drupal\devel\DevelDumperManager::dump()
 */
function dfb() {
    $args = func_get_args();
    Drupal::service('devel.dumper')->dump($args, NULL, 'firephp');
}

/**
 * Wrapper for DevelDumperManager::dump().
 *
 * Calls dfb() to output a backtrace.
 *
 * @see \Drupal\devel\DevelDumperManager::dump()
 */
function dfbt($label) {
    Drupal::service('devel.dumper')->dump(FirePHP::TRACE, $label, 'firephp');
}
if (!function_exists('ddm')) {
    
    /**
     * Wrapper for DevelDumperManager::debug() to replace previous dd function.
     *
     * @see \Drupal\devel\DevelDumperManager::debug()
     */
    function ddm($data, $label = NULL) {
        return Drupal::service('devel.dumper')->debug($data, $label, 'default');
    }
}
if (!function_exists('kint')) {
    
    /**
     * Prints passed argument(s) using Kint debug tool.
     *
     * Wrapper for DevelDumperManager::dump().
     *
     * @see \Drupal\devel\DevelDumperManager::dump()
     */
    function kint() {
        $args = func_get_args();
        if (count($args) == 1) {
            // Pass a single argument directly, which works for any plug-in.
            $args = $args[0];
            $name = NULL;
        }
        else {
            // Pass an array marked with a special name. The kint plug-in expands the
            // arguments and prints each separately.
            $name = '__ARGS__';
        }
        Drupal::service('devel.dumper')->dump($args, $name, 'kint');
    }
}
if (!function_exists('ksm')) {
    
    /**
     * Prints passed argument(s) to the 'message' area of the page.
     *
     * Wrapper for DevelDumperManager::message().
     *
     * @see \Drupal\devel\DevelDumperManager::message()
     */
    function ksm() : void {
        $args = func_get_args();
        if (count($args) == 1) {
            // Pass a single argument directly, which works for any plug-in.
            $args = $args[0];
            $name = NULL;
        }
        else {
            // Pass an array marked with a special name. The kint plug-in expands the
            // arguments and prints each separately.
            $name = '__ARGS__';
        }
        Drupal::service('devel.dumper')->message(input: $args, name: $name, plugin_id: 'kint');
    }
}

/**
 * Wrapper for DevelDumperManager::message().
 *
 * Prints a variable to the 'message' area of the page.
 *
 * Uses Drupal\Core\Messenger\MessengerInterface::addMessage()
 *
 * @param mixed $input
 *   An arbitrary value to output.
 * @param string|null $name
 *   Optional name for identifying the output.
 * @param string $type
 *   Optional message type see MessengerInterface, defaults to TYPE_STATUS.
 * @param bool $load_dependencies
 *   Optional load dependencies if an entity is passed.
 *
 * @return mixed
 *   The unaltered input value.
 *
 * @see \Drupal\devel\DevelDumperManager::message()
 */
function dpm(mixed $input, ?string $name = NULL, string $type = MessengerInterface::TYPE_STATUS, bool $load_dependencies = FALSE) : mixed {
    Drupal::service('devel.dumper')->message(input: $input, name: $name, type: $type, load_references: $load_dependencies);
    return $input;
}

/**
 * Wrapper for DevelDumperManager::message().
 *
 * Displays a Variable::export() variable to the 'message' area of the page.
 *
 * Uses Drupal\Core\Messenger\MessengerInterface::addMessage()
 *
 * @param mixed $input
 *   An arbitrary value to output.
 * @param string|null $name
 *   Optional name for identifying the output.
 * @param bool $load_dependencies
 *   Optional load dependencies if an entity is passed.
 *
 * @return mixed
 *   The unaltered input value.
 *
 * @see \Drupal\devel\DevelDumperManager::message()
 */
function dvm(mixed $input, ?string $name = NULL, bool $load_dependencies = FALSE) : mixed {
    Drupal::service('devel.dumper')->message(input: $input, name: $name, plugin_id: 'drupal_variable', load_references: $load_dependencies);
    return $input;
}

/**
 * An alias for dpm(), for historic reasons.
 */
function dsm($input, $name = NULL, $load_dependencies = FALSE) {
    Drupal::service('devel.dumper')->message(input: $input, name: $name, load_references: $load_dependencies);
}

/**
 * Wrapper for DevelDumperManager::dumpOrExport().
 *
 * An alias for the 'devel.dumper' service. Saves carpal tunnel syndrome.
 *
 * @see \Drupal\devel\DevelDumperManager::dumpOrExport()
 */
function dpr($input, $export = FALSE, $name = NULL) {
    return Drupal::service('devel.dumper')->dumpOrExport(input: $input, name: $name, export: $export, plugin_id: 'default');
}

/**
 * Wrapper for DevelDumperManager::dumpOrExport().
 *
 * An alias for devel_dump(). Saves carpal tunnel syndrome.
 *
 * @see \Drupal\devel\DevelDumperManager::dumpOrExport()
 */
function kpr($input, $export = FALSE, $name = NULL) {
    return Drupal::service('devel.dumper')->dumpOrExport(input: $input, name: $name, export: $export);
}

/**
 * Wrapper for DevelDumperManager::dumpOrExport().
 *
 * Like dpr(), but uses Variable::export() instead.
 *
 * @see \Drupal\devel\DevelDumperManager::dumpOrExport()
 */
function dvr($input, $export = FALSE, $name = NULL) {
    return Drupal::service('devel.dumper')->dumpOrExport(input: $input, name: $name, export: $export, plugin_id: 'drupal_variable');
}

/**
 * Prints the arguments passed into the current function.
 */
function dargs($always = TRUE) {
    static $printed;
    if ($always || !$printed) {
        $bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
        print Drupal::service('devel.dumper')->dumpOrExport(input: $bt[1]['args']);
        $printed = TRUE;
    }
}

/**
 * Prints a SQL string from a DBTNG Select object. Includes quoted arguments.
 *
 * @param object $query
 *   An object that implements the SelectInterface interface.
 * @param bool $return
 *   Whether to return the string. Default is FALSE, meaning to print it
 *   and return $query instead.
 * @param string $name
 *   Optional name for identifying the output.
 *
 * @return object|string
 *   The $query object, or the query string if $return was TRUE.
 */
function dpq($query, $return = FALSE, $name = NULL) {
    if (Drupal::currentUser()->hasPermission('access devel information')) {
        if (method_exists($query, 'preExecute')) {
            $query->preExecute();
        }
        $sql = (string) $query;
        $quoted = [];
        $database = Drupal::database();
        foreach ((array) $query->arguments() as $key => $val) {
            $quoted[$key] = is_null($val) ? 'NULL' : $database->quote($val);
        }
        $sql = strtr($sql, $quoted);
        if ($return) {
            return $sql;
        }
        Drupal::service('devel.dumper')->message(input: $sql, name: $name);
    }
    return $return ? NULL : $query;
}

/**
 * Prints a renderable array element to the screen using kprint_r().
 *
 * #pre_render and/or #post_render pass-through callback for kprint_r().
 *
 * @todo Investigate appending to #suffix.
 * @todo Investigate label derived from #id, #title, #name, and #theme.
 */
function devel_render() {
    $args = func_get_args();
    // #pre_render and #post_render pass the rendered $element as last argument.
    Drupal::service('devel.dumper')->dumpOrExport(input: end($args), export: FALSE);
    // #pre_render and #post_render expect the first argument to be returned.
    return reset($args);
}

/**
 * Prints the function call stack.
 *
 * @param bool $return
 *   Pass TRUE to return the formatted backtrace rather than displaying it in
 *   the browser via kprint_r().
 * @param int $pop
 *   How many items to pop from the top of the stack; useful when calling from
 *   an error handler.
 * @param int $options
 *   Options (treated as a bit mask) to pass on to PHP's debug_backtrace().
 *
 * @return array|null
 *   The formatted backtrace, if requested, or NULL.
 *
 * @see http://php.net/manual/en/function.debug-backtrace.php
 */
function ddebug_backtrace(bool $return = FALSE, int $pop = 0, int $options = DEBUG_BACKTRACE_PROVIDE_OBJECT) : ?array {
    if (Drupal::currentUser()->hasPermission('access devel information') === FALSE) {
        return NULL;
    }
    $backtrace = debug_backtrace($options);
    while ($pop-- > 0) {
        array_shift($backtrace);
    }
    $counter = count($backtrace);
    $path = $backtrace[$counter - 1]['file'];
    $path = substr($path, 0, strlen($path) - 10);
    $paths[$path] = strlen($path) + 1;
    $paths[DRUPAL_ROOT] = strlen(DRUPAL_ROOT) + 1;
    $nbsp = " ";
    // Show message if error_level is ERROR_REPORTING_DISPLAY_SOME or higher.
    // (This is Drupal's error_level, which is different from $error_level,
    // and we purposely ignore the difference between _SOME and _ALL,
    // see #970688!)
    if (Drupal::config('system.logging')->get('error_level') === ERROR_REPORTING_HIDE) {
        return NULL;
    }
    $nicetrace = [];
    while (!empty($backtrace)) {
        $call = [];
        if (isset($backtrace[0]['file'])) {
            $call['file'] = $backtrace[0]['file'];
            foreach ($paths as $path => $len) {
                if (strpos($backtrace[0]['file'], $path) === 0) {
                    $call['file'] = substr($backtrace[0]['file'], $len);
                }
            }
            $call['file'] .= ':' . $backtrace[0]['line'];
        }
        if (isset($backtrace[1])) {
            if (isset($backtrace[1]['class'])) {
                $function = $backtrace[1]['class'] . $backtrace[1]['type'] . $backtrace[1]['function'] . '()';
            }
            else {
                $function = $backtrace[1]['function'] . '()';
            }
            $backtrace[1] += [
                'args' => [],
            ];
            foreach ($backtrace[1]['args'] as $key => $value) {
                $call['args'][$key] = $value;
            }
        }
        else {
            $function = 'main()';
            $requestStack = Drupal::service('request_stack');
            $call['args'] = $requestStack->getCurrentRequest()->query
                ->all();
        }
        $nicetrace[($counter <= 10 ? $nbsp : '') . --$counter . ': ' . $function] = $call;
        array_shift($backtrace);
    }
    if ($return) {
        return $nicetrace;
    }
    Drupal::service('devel.dumper')->dumpOrExport(input: $nicetrace, export: FALSE);
    return NULL;
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Adds mouse-over hints on the Permissions page to display
 * language-independent machine names and module base names.
 *
 * @see \Drupal\user\Form\UserPermissionsForm::buildForm()
 */
function devel_form_user_admin_permissions_alter(&$form, FormStateInterface $form_state) {
    if (Drupal::currentUser()->hasPermission('access devel information') && Drupal::config('devel.settings')->get('raw_names')) {
        foreach (Element::children($form['permissions']) as $key) {
            if (isset($form['permissions'][$key][0])) {
                $form['permissions'][$key][0]['#wrapper_attributes']['title'] = $key;
            }
            elseif (isset($form['permissions'][$key]['description'])) {
                $form['permissions'][$key]['description']['#wrapper_attributes']['title'] = $key;
            }
        }
    }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Adds mouse-over hints on the Modules page to display module base names.
 *
 * @see \Drupal\system\Form\ModulesListForm::buildForm()
 * @see theme_system_modules_details()
 */
function devel_form_system_modules_alter(&$form, FormStateInterface $form_state) {
    if (Drupal::currentUser()->hasPermission('access devel information') && Drupal::config('devel.settings')->get('raw_names') && isset($form['modules']) && is_array($form['modules'])) {
        foreach (Element::children($form['modules']) as $group) {
            if (is_array($form['modules'][$group])) {
                foreach (Element::children($form['modules'][$group]) as $key) {
                    if (isset($form['modules'][$group][$key]['name']['#markup'])) {
                        $form['modules'][$group][$key]['name']['#markup'] = '<span title="' . $key . '">' . $form['modules'][$group][$key]['name']['#markup'] . '</span>';
                    }
                }
            }
        }
    }
}

/**
 * Implements hook_query_TAG_alter().
 *
 * Makes debugging entity query much easier.
 *
 * Example usage:
 *
 * @code
 * $query = \Drupal::entityQuery('node');
 * $query->condition('status', NODE_PUBLISHED);
 * $query->addTag('debug');
 * $query->execute();
 * @endcode
 */
function devel_query_debug_alter(AlterableInterface $query) {
    if (!$query->hasTag('debug-semaphore')) {
        $query->addTag('debug-semaphore');
        // @phpstan-ignore-next-line
        dpq($query);
    }
}

Functions

Title Deprecated Summary
backtrace_error_handler Displays backtrace showing the route of calls to the current error.
dargs Prints the arguments passed into the current function.
ddebug_backtrace Prints the function call stack.
devel_debug Logs a variable to a drupal_debug.txt in the site's temp directory.
devel_dump Dumps information about a variable.
devel_entity_operation Implements hook_entity_operation().
devel_entity_type_alter Implements hook_entity_type_alter().
devel_export Returns a string representation of a variable.
devel_form_system_modules_alter Implements hook_form_FORM_ID_alter().
devel_form_user_admin_permissions_alter Implements hook_form_FORM_ID_alter().
devel_get_handlers Gets error handlers.
devel_help Implements hook_help().
devel_local_tasks_alter Implements hook_local_tasks_alter().
devel_menu_links_discovered_alter Implements hook_menu_links_discovered_alter().
devel_message Sets a message with a string representation of a variable.
devel_page_attachments_alter Implements hook_page_attachments_alter().
devel_query_debug_alter Implements hook_query_TAG_alter().
devel_render Prints a renderable array element to the screen using kprint_r().
devel_set_handler Sets a new error handler or restores the prior one.
devel_set_message Sets message.
devel_toolbar Implements hook_toolbar().
dfb Wrapper for DevelDumperManager::dump().
dfbt Wrapper for DevelDumperManager::dump().
dpm Wrapper for DevelDumperManager::message().
dpq Prints a SQL string from a DBTNG Select object. Includes quoted arguments.
dpr Wrapper for DevelDumperManager::dumpOrExport().
dsm An alias for dpm(), for historic reasons.
dvm Wrapper for DevelDumperManager::message().
dvr Wrapper for DevelDumperManager::dumpOrExport().
kpr Wrapper for DevelDumperManager::dumpOrExport().

Constants

Title Deprecated Summary
DEVEL_ERROR_HANDLER_BACKTRACE_DPM
DEVEL_ERROR_HANDLER_BACKTRACE_KINT
DEVEL_ERROR_HANDLER_NONE @file This module holds functions useful for Drupal development.
DEVEL_ERROR_HANDLER_STANDARD