|
|
Subscribe / Log in / New account

PHP Debugging using Xdebug

By John Coggeshall
August 14, 2020

While PHP does not come with a full toolkit for debugging and profiling, an open-source project has existed almost as long as PHP to provide both: Xdebug. Created and maintained by PHP core developer Derick Rethans, it offers remote debugging, stack traces, profiling, and more. It is a project that anyone doing PHP development would benefit from using.

The Xdebug GitHub page claims over 60 contributors to the project. Its most recent release, version 2.9.6, was made on May 29. The Xdebug compatibility page provides a table describing which versions of Xdebug are still supported, including which Xdebug version should be used for the PHP version in question. Presently, version 2.9 supports PHP 7.4 (with security support for PHP 7.1 and above). Xdebug uses its own open-source license based on the PHP project's permissive license.

Xdebug visual features

Simply installing the Xdebug extension offers a number of niceties as compared to unmodified PHP, take for example this simple PHP script that causes an E_NOTICE error (because $foo is an undefined variable):

    <?php
    // Settings just to make sure all errors are displayed
    error_reporting(E_ALL);
    ini_set('display_errors', true);

    // Turn off Xdebug (if enabled by default)
    xdebug_disable();

    echo "<h1>Without Xdebug</h1>";
    echo "<br/>The value is: " . $foo . "<br/>";

    xdebug_enable();
    echo "<h1>With Xdebug</h1>";
    echo "<br/>The value is: " . $foo . "<br/>";

Here is the output in the browser when this script is executed, both with and without Xdebug enabled:

[Xdebug error output]

Xdebug provides an enhancement to the var_dump() function, which is commonly used in development to dump the contents of a variable to the browser. Unmodified PHP will simply output the text without any HTML formatting (so it generally needs to be wrapped using the <pre> tag), where Xdebug provides an easier-to-read output:

[Xdebug var_dump() Output]

Xdebug step-debugging

Xdebug isn't only limited to HTML-formatting of PHP outputs; it also offers a fully-featured remote debugger. With Xdebug, PHP scripts running on a remote server can be debugged by communicating using the DBGp debugging protocol. DBGp is authored by Shane Caraveo and Rethans; it provides a common means to communicate remote debugging commands for any language (for example, here is a Python implementation of DBGp). An open-source protocol means that plenty of clients implement DBGp, making it likely that Xdebug is supported in a developer's favorite editor. This article will be using a DBGp-compatible Vim plugin Vdebug to debug the PHP script. For reference, here is the script to be debugged:

    <?php
    $total = 10;
    echo "<h1> First " . ($total + 2) ." Fibonacci numbers</h1>";

    $a = 0;
    $b = 1;

    $vals = [];

    echo "$a, $b, ";

    for($i = 0; $i < $total; $i++) {
        $d = $b + $a;
        $vals[] = $d;
        $a = $b;
        $b = $d;
    }

    echo implode(', ', $vals);

When activated, Xdebug will make a connection to a DBGp client specified in php.ini. By default, that host will be localhost, but it can be changed as necessary using the xdebug.remote_host configuration setting (the port can be set using xdebug.remote_port). To activate the debugger for a given PHP script, multiple options are available. First, start a debugging session in the client (F5 in Vdebug), followed by activating the Xdebug debugger on the server. With the client listening, activating Xdebug for a request in our situation means appending XDEBUG_SESSION_START=<session_id> to the URL as an HTTP GET parameter (session_id is arbitrary):

    http://localhost/step_debug.php?XDEBUG_SESSION_START=mysession
This request, which causes Xdebug to open a connection to the DBGp client specified in the xdebug.remote_host setting, triggers the Vdebug debugging interface on connection:

[Vdebug debugger interface]

Using Vdebug, the script can be stepped through (F2 for step over, F3 to step into), breakpoints can be set using :Breakpoint, and inline PHP code can be evaluated using :Vdebugeval. See the Vdebug documentation for a complete listing of available commands.

Profiling of PHP scripts

Along with debugging, Xdebug provides a mechanism to profile a PHP script's execution time by generating a Cachegrind-compatible file that can be visualized using the open-source KCachegrind application. To enable profiling of PHP scripts, we will need to set the xdebug.profiler_enable setting in the php.ini file to true, and provide a directory where Xdebug should put the generated profile files using xdebug.profiler_output_dir. Note that using the profiler can consume an extensive amount of disk space — over 500MB for complex applications. To only use the profiler for specific requests, you can set xdebug.profile_enable_trigger to true and adding XDEBUG_PROFILE as an HTTP variable to the request to be profiled:

    http://localhost/step_debugger.php?XDEBUG_PROFILE=1

Profiling the simple PHP scripts from our previous example wouldn't produce much useful information. To give a better demonstration of Xdebug's profiler capabilities, we installed a new stub Laravel framework project via Composer using the following command:

    $ composer create-project laravel/laravel profile-demo

This (significantly more complex) stub project was then profiled by visiting it in our browser and providing the XDEBUG_PROFILE HTTP variable. Here is the Cachegrind profile of that request, as viewed using KCachegrind:

[KCachegrind rendering of an Xdebug profile]

Other useful functionality

Along with debugger and profiler tooling, Xdebug provides a collection of additional PHP functions. Some of these functions, like xdebug_break(), are meant to be used to apply logic to features like debugger breakpoints. Others, like xdebug_call_class(), provide more detailed information on the call stack, which is handy in augmenting logs for analysis. The PHPUnit project takes advantage of Xdebug's code-coverage-analysis functions when available, enabling useful reports to measure how effective a code base's unit tests are.

The functions provided by Xdebug to developers allow for tight integration between Xdebug and the application. In the example below, we take our Fibonacci sequence code from above, introduce a non-fatal error, and add a few Xdebug functions to improve the way errors are displayed:

    <?php

    xdebug_start_error_collection(); // Capture non-fatal errors

    $total = 10;
    echo "<h1> First " . ($total + 2) . " Fibonacci numbers</h1>";

    // Let's remove this, which will cause an E_NOTICE error
    //$a = 0;
    $b = 1;

    $vals = [];

    echo "$a, $b, ";

    for($i = 0; $i < $total; $i++) {
        $d = $b + $a;
        $vals[] = $d;
        $a = $b;
        $b = $d;
    }

    echo implode(', ', $vals);

    xdebug_stop_error_collection();
    $errors = xdebug_get_collected_errors();

    if(!empty($errors)) {
        echo "<hr/>";
        echo "<h1>Non-Fatal Errors</h1>";
        foreach($errors as $error) {
            echo $error;
        }
    }

The above example produces a E_NOTICE error, which is normally displayed as soon as it is generated. In our case, that would be right in the middle of our script's output. In a more complicated script that generates complex HTML, it's possible for such a PHP error not even to be rendered in the browser (e.g. it happened in a <script> tag). Xdebug allows us to avoid this sort of problem by providing a means of first capturing these errors using xdebug_start_error_collection(), and then displaying them at a more opportune moment (e.g. after the output is finished) by using xdebug_stop_error_collection() and xdebug_get_collected_errors(). Here is the output of the script above, implementing these functions to defer errors until after our program has finished:

[Xdebug error capturing]

In closing

This article has provided an introduction to the most commonly used features of Xdebug. There is more to it, however, including features like garbage collection statistics, function traces, and DBGp proxies. Xdebug ably fills a gap by providing robust debugging and profiling that is missing from the PHP standard distribution.



to post comments

PHP Debugging using Xdebug

Posted Aug 15, 2020 16:08 UTC (Sat) by jkingweb (subscriber, #113039) [Link] (1 responses)

Doesn't PHP come with a debugger in the form of phpdbg? I realize Xdebug is vastly more popular, but the opening statement would seem to be inaccurate.

PHP Debugging using Xdebug

Posted Aug 15, 2020 21:33 UTC (Sat) by coogle (guest, #138507) [Link]

Thanks for pointing that out -- updated the article to be a more accurate statement.

PHP Debugging using Xdebug

Posted Aug 21, 2020 0:41 UTC (Fri) by flussence (guest, #85566) [Link]

It still kind of amazes me that this is more or less the state of the art in 2020. I don't mean that in a pejorative sense toward PHP either - a lot of languages have pretty miserable profiling/debugging workflows. KCacheGrind is vastly underappreciated as a frontend.

The only thing I wish Xdebug would add is a way to redirect all the HTML pretty-printing to a separate web UI so it doesn't get interspersed with regular output. It's possible to micromanage it as the article demonstrates, but it'd save so much time to just pop open an extra tab.


Copyright © 2020, Eklektix, Inc.
This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds