Debugging Drupal

by Gisle Hannemyr

This chapter gives a brief overview of some of the tools you can use to make Drupal development and debugging easier.

Table of contents

Drupal contributed extensions discussed in this chapter: Devel, Devel Node Access.

Introduction

[Some of it is legacy content dating back to Drupal 6, and can do with some updating.]

Acknowledgement: Morten Wulff has created a Devel Demo project that has been used to generate the Drupal 6 screenshots on this page.

This chapter covers (or should cover) the debugging tools and variable dumpers that is provided by PHP, Drupal core, the Devel contributed project and other sources..

See alsoYou my also want to read DO: Development tools.

Some old documentation about the combination of NetBeans and Xdebug has been retained until I can find the time to update it.

Run-time error reporting

While developing, some run-time errors may produce a blank page or a blank page with just the following error message. “The website encountered an unexpected error. Please try again later.” This is known to developers as the White Screen Of Death or WSOD. You may also experience that some task, such as installation of a custom module, has no effect.

To get more information about run-time errors, navigate to: Configuration » Development » Logging and errors to configure error messages to display. For development select “All messages”. For a production site, you want to set this to “None”.

Even if “All messages” are set, you may get a WSOD with no text. If you use Apache, latest PHP run-time messages can be seen in the Apache error log The default location is: /var/log/apache2/error.log.

You can also make use of the logger class provided by the Database logging core module when debugging a module:

\Drupal::logger('my_module')->notice('A notice.');
\Drupal::logger('my_module')->error('An error');

tipThis can be used just as one used the global watchdog() function in Drupal 7 and earlier. The Database logging module was previously called Watchdog.

To add a message to the list of messages to be displayed in the message area, in OO code use:

addMessage('A status', self::TYPE_STATUS, FALSE);
$this->messenger()->addMessage($this->t('A custom type.'), 'custom', TRUE);
$this->messenger()->addStatus('A status');
$this->messenger()->addWarning('A warning');
$this->messenger()->addError('An error');

In procedural code, you can use:

\Drupal::messenger()->addMessage('A message.');

In addition to the functions listed above, there is the Devel contributed project that provides functions that can be used for debugging.

The Drupal core API provides this function (debug()). Example:

debug($var, 'var');

Unlike debug output generated by the devel-functions output generated by the debug() fuction does not have any access controls, so it will always display, even for the anonymous user role.

See alsoThere is more about debugging run-time errors on the Drupal.org site. See, for instance, these notes posted on the developer's wiki: Show all errors while developing [Drupal 7], Fixing white screens step by step and Blank pages or WSOD. In addition to the WSOD, internal server errors (status code 500) may pose a challenge when debugging.

This pair of CLI commands may also be handy when debugging. The first shows the latest entries from the database log. The second truncates the log.

$ drush ws
$ drush wd-del all -y

PHP

The function print_r() converts any variable into plain text.

 print_r(mixed $value, bool $return = false): string|bool

See also php.net: Debugging in PHP.

Core

debug($data, $label = NULL, $print_r = TRUE);

See the API documentation for details.

Devel

The Devel project is a suite of modules that provides helper functions, admin pages, and additional development drush commands to use during development.

See alsoThere is excellent documentation available on Drupal.org. Start out by reading here. The page: dpm() and other Devel functions covers the same functions as this section. See also: Ivan Zugec: Replacement for print_r.

It is widely used for testing purposes because of its ability to generate a lot of content for nodes, comments, users and various content types and entities.

Page footers can be added for all pages with the help of its submodule called Web Profiler. It gives the site admins basic analytics about the caching abilities, database queries, resource utilization and much more.

[It can print a summary of all database queries for each page request. [D8?]]

[It comes with a block named “Development”. Enabling it gives direct access to a several useful functions. [D8?]]

Installing and configuring Devel

To install and enable it for Drupal do the following.

Drupal 7:

$ drush en devel -y

The Drupal 7 version of Devel comes with the krumo var-dumper to show an accordion view of objects and arrays.

Drupal 8 and later

$ composer require 'drupal/devel:^4.1'
$ composer require kint-php/kint
$ drush en devel -y
$ drush cr

The cache rebuild is necessary to make kint show up in “Devel settings”.

You may want to configure what variable dumper to use when displaying variables you do this by navigating to Configuration » Development » Devel settings and select one. The choices are:

The “Symfony var-dumper” has the smallest footprint. The output looks like this:

devel-dpm-symfony.png
Output from the dpm() function using rhe Symfony var-dumper.

The default (Doctrine) debugging tool provides no navigation, it just pretty-prints the whole data structure as plain text. This output is harder to read than the Symfony var-dumper or kint, but it let you copy and paste the data.

devel-dpm-default.png
Part of output from the dpm() function using the default variable dumper.

The last option (kint) looks like this:

devel-dpm-kint.png
Output from the dpm() function using kint as a variable dumper.

When displaying a printed variable, kint also displays a stack trace just below the output. Click on the “+” icon below the variable and it’ll expand a stack trace.

See alsoSee also: Using Kint, Taming Kint's output, why-is-ksm-so-strange.

Devel submodules

The Devel ecosystem currently contains the following modules:

Below is a brief description of each of these.

The Devel Generate submodule (available in all versions) will generate dummy content.

You may use Devel Generate to:

The Drupal 7 version contains the Devel Node Access (DNA) module to help you debug access control issues.

In Drupal 8 Devel Node Access is a separate project. To download:

$ composer require 'drupal/devel_node_access-devel_node_access:1.x-dev@dev'

It has not been updated since 2019 and is not fully ported.

The Web Profiler is a port of the Symfony WebProfilerBundle for Drupal.

It has a lot of useful options, including one where it tells you the names of routes.

Read more Joe Fender: Web Profiler in Drupal 8.

Devel helper functions

The basic Devel module provides a set of helper functions that is useful for debugging during mod­ule development.

These are:

To distinguish between different calls to the degel helper functions, you can pass a distinct string in the $name parameter.

Each of these, complete with examples, are discussed below:

dpm()

This function is a wrapper around the service “devel.dumper” to print a variable to the “message” area of the page (where it is less likely to break the layout of your page). You can use this function to keep track of one or more variables when you’re working on the code running your site.

See the API documentation for details.

dpm($input, $name = NULL);

technicalIt was previously called dsm(). A devel module focblock contains this note: "dsm() is a legacy function that was poorly named; use dpm() instead, since the 'p' maps to 'print_r'."

To display a query object constructed by means of db_select and its placeholder arguments, use the following construct:

// Build query object to fetch new nodes.
$query = db_select('node', 'n');
$query->fields('n', array('nid'));
…
dpm((string) $query);
dpm($query->arguments());

In addition to letting you look inside Drupal objects and arrays, the function dpm() can be combined with various PHP debugging functions. For instance to print all defined variables, or to backtrack function calls, use:

dpm(get_defined_vars());
dpm(debug_backtrace());

In Drupal 7, the dpm() function makes use of the Krumo print function kprint_r(). If the debugging information we want to capture exists outside of a page view, we may want to capture it and place it in the system log instead of displaying it directly. The following is an example of this:

$export = kprint_r($array, TRUE, 'object');
watchdog('mymodule', $export);
$export = print_r($array, TRUE);
\Drupal::logger('guidedcustom')->notice('mymodule' . $export);

To see the output from Devel helper functions, the current user need to have the permission “Access developer information” granted.

Devel has its own PHP debug window that can be used for extracting information about your configuration. To access it, go to the path /devel/php on your site.

For example, you may execute the following to see see all field information on your site:

// Show all fields.
dpm(field_info_fields());
// Show all field instances.
dpm(field_info_instances());

This is useful when debugging custom content types created with the Field API.

noteIf you run this to output the variable to the browser in a function that runs before Drupal has bootstrap­ped (e.g. a preprocess function), it will trigger this error: “RuntimeException: Failed to start the session because headers have already been sent". You get the dump, but the site crashes immediately afterwards.

Some claim that using die() after the call to dpm() may save some RAM. Example:

dpm($input, 'mymodule');
die('This may save some RAM');

ddm()

The function ddm() logs any variable to a file named “drupal_debug.txt” below the server’s temp directory (usually /tmp). It is not located directly in the temp directory, but in some automatically named subdirectory. Use find to locate it). All output from this function is appended to the log file, making it easy to see how the contents of a variable change as you modify your code.

You can use it like this:

ddm($variable, $label = NULL);

Which is equicalent to:

$ddumper = \Drupal::service('devel.dumper');
$ddumper->debug($variable, NULL, 'default');

If you’re using Mac OS X you can use the Logging Console to monitor the contents of the log file.

If you’re using a flavor of Linux you can use this command:

$tail -f …/drupal_debug.txt

The -f option, shows new lines appended to the file as it grows.

noteThis does not work inside PHPUnit tests..

Sources:

tipIn Drupal 7, this was named dd(). It had to be renamed for Symfony-based Drupal, since that function exists in Symfony.


Some Devel functions I have not explored yet

dvm()

The output looks identical to that of dpm().

See the API documentation for details.

dvm($input, $name = NULL);

dpr()

dpr($input, $return = FALSE, $name = NULL)

pretty prints arrays.

https://api.drupal.org/api/devel/devel.module/function/dpr/8.x-9.x

Pretty-prints a variable to the browser. The output is displayed in the page header, making this a good choice if your theme doesn't print the $messages variable.

dpr($input, $return = FALSE, $name = NULL);

Set the second parameter to TRUE if you want to return a string instead of printing it.

dpr() screenshot

dvr()

Uses var_dump() to pretty-print a variable to the browser. The output is displayed in the page header, making this a good choice if your theme doesn’t print the $messages variable.

dvr($input, $return = FALSE, $name = NULL)

Set the second parameter to TRUE if you want to return a string instead of printing it.

dvr() screenshot

kpr()

Pretty-prints a variable to the browser using [krumo???]. The output is displayed in the page header, making this a good choice if your theme doesn’t print the $messages variable.

kpr($input, $return = FALSE, $name = NULL)

Set the second parameter to TRUE if you want to return a string instead of printing it.

kpr() screenshot

dargs()

Prints the arguments passed into the current function. In this case, the arguments are ‘foo’, and ‘42’. You can use this function if you’re not completely sure of the arguments received by a specific Drupal hook or function in the code you’re working on.

dargs($always = TRUE);

dargs() screenshot

Set the paramter to FALSE to cancel printing if the paramters already has been printed.

See the API documentation for details.

ddebug_backtrace()

Prints the function call stack.

ddebug_backtrace();

ddebug_backtrace() screenshot

PAReview.sh

This is simple Bash script to some static code analysis of a Drupal project. It takes a path to a moduler theme project and checks that. The output is suitable for an attachment in the Project Applications issue queue.

Alternatively, it takes a Git repository URL as an argument, clones the code in a "pareview_temp" folder, and runs some checks.

To install, download the tarball and put it in a global location XXXX

https://www.drupal.org/project/pareviewsh

Xdebug

Xdebug is the standard debugger for PHP.

drupal-check

github: drupal-check.

Built on PHPStan (PHP Static Analysis Tool). this static analysis tool will check for correctness (e.g. using a class that doesn't exist), deprecation errors, and more. It takes the Drupal context into consideration.

$ composer require mglaman/drupal-check --dev
$ php vendor/bin/drupal-check [OPTIONS] [DIRS]

Here are specific instruction about using it to check for Drupal 9 readiness.


This part is salvaged from Morten Wulff's Drupal 6 page (now 404) and need to checked and updated. It may be oudated.

Outdated documentation

The sections below are probably outdated, but has been retianed until I can find the time to make them current.

XDebug & NetBeans

At the moment this guide is only available for Ubuntu. This part of the guide assumes that you have a running Ubuntu system with Apache, MySQL and PHP installed, and that they are configured to run Drupal locally.

Installation:

Create a project in NetBeans

Project: Type

Project: Source

Project: Run configuration

Project: PHP options

Start debugging

Debug: Breakpoint

Debug: Dialog

When the debugger is running you can use the Local Variables tab to display the contents of all the variables which are currently in scope as well as all superglobals.

Debug: Local variables

The Call Stack tab shows the current position in the call stack.

Debug: Call stack

You can add more tabs by going to the Window → Debugging menu in NetBeans.

Keyboard shortcuts

Use the following shortcuts to step through the code when running under the debugger:

KeyAction
F5Continue
F8Step over
F7Step into
CTRL+F7Step out
F4Run to cursor

Profiling with Xdebug

In addition to its basic use as a debugging tool, you can also use it to profile your code. This is a two-step process: First you use Xdebug to generate a cachegrind output file. Then, you use a tool like Webgrind to analyze the output file.

If you have Xdebug installed and configured, you can enable profiling by adding this line to your xdebug.ini file:

xdebug.profiler_enable_trigger=1

The xdebug.ini file can usually be found in /etc/php5/conf.d/.

To create the cachegrind file, add an ?XDEBUG_PROFILE query parameter when loading a page on your site:

drupal.local/?XDEBUG_PROFILE

This generates a cachegrind.out.<pid> file in your /tmp directory.

To analyze the output file you can install a tool like Webgrind on your local web server:

When you access the Webgrind frontpage, you can use the dropdown box at the top of the screen to select the cachegrind file you want to analyze.

After processing the file webgrind displays a list of all the functions which where called to build the current page. See the screenshots below for example output.

Overview

Detail

XDebug.ini

Final words

Links to other resources:

Obsolete debugging tools:


Last update: 2021-02-02 [gh].