You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
709 lines
24 KiB
709 lines
24 KiB
<?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, ?string $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|null $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, ?string $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|null $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, ?string $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|null $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, ?string $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|null $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, ?string $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, ?string $label = NULL) { |
|
return Drupal::service('devel.dumper')->debug($data, $label, 'default'); |
|
} |
|
|
|
} |
|
|
|
/** |
|
* 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, ?string $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, ?string $name = NULL) { |
|
return Drupal::service('devel.dumper') |
|
->dumpOrExport(input: $input, name: $name, export: $export, plugin_id: 'default'); |
|
} |
|
|
|
/** |
|
* Wrapper for DevelDumperManager::dumpOrExport(). |
|
* |
|
* Like dpr(), but uses Variable::export() instead. |
|
* |
|
* @see \Drupal\devel\DevelDumperManager::dumpOrExport() |
|
*/ |
|
function dvr($input, $export = FALSE, ?string $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|null $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, ?string $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 = "\xC2\xA0"; |
|
|
|
// 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); |
|
} |
|
}
|
|
|