Debugging Drupal
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..
You 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:
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');
This
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.
There 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.
There 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
and select one. The choices are:- Symfony var-dumper
- Default
- kint
The “Symfony var-dumper” has the smallest footprint. The output looks like this:
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.
The last option (kint) looks like this:
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 also: Using Kint, Taming Kint's output, why-is-ksm-so-strange.
Devel submodules
The Devel ecosystem currently contains the following modules:
- Devel: Various debugging functions, blocks, and pages for developers.
- Devel Generate: Generates dummy users, nodes, and taxonomy terms.
- Devel Node Access: Helps you debug access control issues.
- Web Profiler: Provides detailed technical information about each request execution.
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:
- Preview your theme populated with articles, with comments and users.
- Test permissions and workflow with different user roles.
- Demo a site to a client.
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 module development.
These are:
dpm()
: Given an object or an array, this function will output a div structure in the message area at the top of your page that will show you the components of these composite types.ddm(mixed)
:dpr()
: prints arrays as plain text.ddebug_backtrace()
: prints the function call stack.
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);
It
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.
If you run this to output the variable to the browser in a function that runs before Drupal has bootstrapped (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.
This does not work inside PHPUnit tests..
Sources:
In 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.xPretty-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.
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.
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.
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);
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();
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/pareviewshXdebug
Xdebug is the standard debugger for PHP.
- DO: Xdebug debugger.
- Home page: xdebug.org
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:
- Download and install the PHP version of NetBeans.
- Install the php5-xdebug-package using Ubuntu’s built-in package manager.
Create a project in NetBeans
- Choose File → New Project…
- Select PHP Application from Existing Sources.
- Select the root folder of your Drupal project and choose a name for your project. In most cases it’s easiest to let NetBeans store the metadata in the Drupal root directory since you can just tell your VCS to ignore this folder.
- Choose Run as Local Web Site and enter the local URL you use to access the site
- Go to Tools → Options
- On the PHP tab uncheck the box “Stop at the First Line” to make sure the debugger only stops if you have set one or more breakpoints.
Start debugging
- Open the file you wish to debug
- Add one or more breakpoints by clicking on a line number in the margin or by pressing CTRL+F8.
- Choose Debug → Debug project or press
Ctrl+F5
- Choose Server side PHP and click Debug. Check the box “Do not show again” if you don’t want to be presented with this dialog every time you start the debugger.
The Call Stack tab shows the current position in the 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:
Key | Action |
---|---|
F5 | Continue |
F8 | Step over |
F7 | Step into |
CTRL+F7 | Step out |
F4 | Run 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:
- Get the latest copy of Webgrind.
- Install it in a local directory and create a virtual host for it, so you can access it at http://webgrind/.
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.
Final words
Links to other resources:
- Drupal Debugging
- Quick-and-Dirty Debugging
- Resources and links on debugging, tracing and profiling Drupal
Obsolete debugging tools:
- Drupal For Firebug.
- Variable dumper krumo was in Devel for Drupal 7.
Last update: 2021-02-02 [gh].