What is Debugging?

Debugging is part of reverse engineering in classical software development.

In doing so, we gain deeper insight into the processes and workflows both as developers of a self-developed application and with an application from another person. This offers the option to understand application workflows more easily and eliminate possible errors more quickly, since the user can, for example, always view current details on available variable assignments and resources.

Debugging can occur at the execution time of a job, a web request or user interaction, as well as during the run of a unit test.

When a developer sets a breakpoint, they inform the debugger at which point it should pause. The debugger stops execution at exactly that point. It is therefore a tool that represents the interface between the developer and the application to be executed and can thus also be understood as a control center.

What are the advantages compared to dumping?

Xdebug offers step-by-step debugging, stack tracing, performance profiling and code coverage analysis. Compared to classic output functions such as var_dump(), echo or – in the context of the Laravel PHP framework – dd(), the advantage lies primarily in flexibility. Simple output functions deliver only a single snapshot of the process state per call.

That is sufficient in many cases. But at the latest after a change, we have to restart the entire process – navigate to the point again and test the adjustment multiple times "blindly" until the result is correct. That costs time and noticeably slows down development.

Xdebug solves this problem. Developers check changes live and validate them directly – while the application continues running. Does the change take effect on the next step? Xdebug shows this immediately. In short: developers save time instead of validating each change individually.

This is also why the team at digital agency mindtwo GmbH manages to quickly deliver the desired results to customers. Unit tests automatically validate the work at the latest during the next deployment to the test instance. Thanks to Pest.

Feature Deep Dive

This is done using the IDE PhpStorm in a case example, based on the execution of a request to the PHP interpreter with a certain Pest (which in turn builds on PhpUnit) test case.

Step Debugging

At the beginning, breakpoints were already mentioned, which we can set ourselves in the IDE to pause the execution of the respective application at certain points.

With the following php.ini configuration, we can activate profiling (this manual step is explicitly not required when setting up with Herd, it is already "automagically" prepared/used in connection with PhpStorm and other editors): xdebug.mode = develop,debug.

It is also important that we can set not just one, but also multiple breakpoints directly. These can also be equipped with additional conditions for when they should actually pause execution.

It is therefore possible, for example, that a certain breakpoint only pauses execution once a certain equation returns the value true.

Since breakpoints can thus be used very versatilely and pause execution, it is always valuable that we think about when it really makes sense to set a breakpoint. "With great power comes great responsibility", and so on.

Once we have set a breakpoint, we can also define the further conditions:

  1. The normal dialog when clicking on such a set breakpoint:

    !Detail view of a set breakpoint in PhpStorm with options for "Enabled", "Suspend execution" and an input field for conditions

  2. The dialog when we click on "More" in the previous popover, where we can further customize the behavior of the breakpoint:

    !The settings menu for breakpoints in PhpStorm, showing additional functions such as log messages, stack traces and the "Remove once hit" option

    Breakpoints are a powerful debugging tool to jump "laser-focused" through an application and directly view the desired snapshots of a selected location without any hassle.

    !An active debugging session in PhpStorm where code execution is paused at a breakpoint and available variables such as $this, $user and $event are displayed in the debug window

    Additionally, this screenshot also shows which control options are available, such as Continue, Step Over, Step Into and Step Out (Of), to navigate within an active debugging session.

    Of course, all clickable elements can also be used via keyboard shortcuts, with and without the installed IdeaVIM plugin. However, since every keyboard mapping is different, and the original documentation from JetBrains already addresses the default settings, it would be of little help to delve further into the details here – and so this remains a task that we may master ourselves.

Stack Traces

It is also possible via PhpStorm to use a stack trace or thread dump.

However, for this we first need to have one available and, which is also part of the Xdebug settings, so that it creates just that.

With the following php.ini configuration we can activate profiling:

xdebug.mode = develop,trace  
; 0=human readable, 1=machine readable, 2=HTML  
xdebug.trace_format = 1  
; %c=crc, %p=pid  
xdebug.trace_output_name = trace.%c.%p

The output directory of Xdebug should be /var/tmp by default.

The advantage of a stack trace is to quickly and specifically go through the same process flow and thus directly reach the same specific point in an application.

Files are, according to the above configuration, named in the following schema: trace.%c.%p.

This can be shared among DEV teams and time can be saved through its repeatable execution before we, for example, first work our way to the respective locations ourselves and prepare the required setup.

Furthermore, it is possible to jump directly to the respective location in the source code of the application once PhpStorm has executed the stack trace and "prepared" the application accordingly.

Here is a general example of its application, after recording when calling the index (/) of a Filament application (return view(...)):

!An Xdebug stack trace output in the terminal that logs in detail the temporal sequence and hierarchy of called PHP functions

Performance Profiling

Performance profiling writes the entire course of a request to the PHP interpreter with timings and memory usage.

With the following php.ini configuration we can activate profiling: xdebug.mode = develop,profile.

The output directory of Xdebug should be /var/tmp by default.

With tools like K/Qcachegrind it is possible to easily view the cachgrind.* files generated thereby.

To install on macOS, brew install qcachegrind can be used. But attention: Currently, calling is only possible via terminal, e.g. via qcachegrind.

Furthermore, the usability of the application on macOS is... interesting.

If the path to the file is appended when calling via terminal, the application freezes and is not responsive, even though the file apparently was loaded correctly.

Opening the application without prior passing of a file path does not lead to this malfunction. This also applies when we subsequently open a file that was previously extracted from a .gz archive.

However, further troubleshooting goes far beyond the scope of the blog article.

However, it is unfortunately generally recommended to use the mouse instead of the keyboard when navigating through the tool. It seems as if the tool is still stuck before any a11y usability initiative.

An example – after configuring it in Xdebug – can be seen below.

  1. Via the application on macOS:

    !The analysis of a cachegrind file in qcachegrind, which visualizes the memory consumption and call times of individual PHP methods for performance optimization

    It is easy to see how many calls and resources the call of individual methods of a request run through the PHP interpreter has caused in total.

  2. Finally via the file content itself:

version: 1  
creator: xdebug 3.4.0alpha2-dev (PHP 8.4.19)  
cmd: /Applications/Herd.app/Contents/Resources/valet/dump-loader.php  
part: 1  
positions: line

events: Time_(10ns) Memory_(bytes)

fl=(1) php:internal  
fn=(1) php::getenv  
1 75 0

fl=(2) xdebug://debug-eval  
fn=(2) {main}  
1 7642 32  
cfl=(1)  
cfn=(1)  
calls=1 0 0  
1 75 0

fl=(2)  
fn=(2)  
1 2658 0

fl=(2)  
fn=(2)  
1 46 0

fl=(2)  
fn=(2)  
1 33 0

fl=(3) /Applications/Herd.app/Contents/Resources/valet/HerdConfiguration.php  
fn=(3) require_once::/Applications/Herd.app/Contents/Resources/valet/HerdConfiguration.php  
1 150 0

fl=(1)  
fn=(4) php::array_key_exists  
48 5108 0

fl=(1)  
fn=(5) php::php_uname  
182 1371 32

fl=(1)  
fn=(6) php::strpos  
182 1329 0

fl=(3)  
fn=(7) BeyondCode\HerdConfiguration\HerdConfiguration→detectOperatingSystem  
181 115538 40  
cfl=(1)  
cfn=(5)  
calls=1 0 0  
182 1371 32  
cfl=(1)  
cfn=(6)  
calls=1 0 0  
182 1329 0

fl=(1)  
fn=(4)  
225 25 0

[…]

(This is heavily abbreviated and represents only about 3% of the entire file content and is also the reason why any cachegrind file is so large and the generation of such takes so long.)

These days, the approach of finding all this out via Xdebug is probably simply overshadowed by the possible use of Herd Dumps as well as Herd Profiler or the PHP Debug Bar or Laravel Debug Bar. Therefore, this can probably be partially ignored, since at least today's alternatives appear significantly more performant.

Times in Change through AI

In times of increased use of agentic AI, one might think that self-responsible debugging is taking a back seat. Some even go so far as to predict that developers, designers or musicians will soon no longer be needed. Numerous workflows and recommendations are circulating on the net in which users only need to give their commands to the AI and check the results. Occasionally, voices are even heard claiming that with the right skills or prompts, one can save the last step as well.

But is that really true?

We don't just see ourselves as software developers, but also to some extent as creative professionals. "Vibe Coding" supposedly opens the doors to software development for non-developers, but the results should be viewed with caution. Unchecked applications repeatedly lead to security vulnerabilities and are susceptible to bugs. That's why we are convinced that writing code belongs in the hands of experts – with or without AI. Generating generated code is simple. Understanding and validating it is the actual task.

Application

For clarification, an example in application using the development environment (IDE) PhpStorm. Note: There are of course other ways to use Xdebug.

Laravel Herd

Since we internally rely on Laravel Herd (Pro) via macOS, it is obvious to integrate Xdebug directly through it. Windows users have the same option. Linux users, however, still have to make do with manual configuration via the Laravel Valet port.

PhpStorm

Once Xdebug is activated via Laravel Herd, some steps must be completed in PhpStorm so that the debug signals are received correctly.

  1. It is also recommended to first take another look at the configuration guide in the Laravel Herd documentation, for example to avoid unwanted forced breakpoints from the outset.

  2. After that, it is advisable to double-check the PHP settings for the current project.

    The general PHP settings for the active project on the IDE side

    !The configuration menu in PhpStorm for PHP language level 8.4 and the CLI interpreter

    The PHP CLI interpreter settings

    !The configuration menu in PhpStorm for PHP language level 8.4 and the CLI interpreter

    The server settings for the active project

    !Detailed PHP interpreter settings with paths to the PHP executable and the configuration file (php.ini) as well as the active Xdebug version 3.4.0alpha2-dev

  3. In the next step, all that remains is to activate debug mode in the IDE, which can be located in the top menu of PhpStorm, depending on version, settings and design.

    !Detailed PHP interpreter settings with paths to the PHP executable and the configuration file (php.ini) as well as the active Xdebug version 3.4.0alpha2-dev

    The official JetBrains documentation offers a comprehensive guide for manual setup without Laravel Herd. It also describes in greater detail the actual manual process for setting up Xdebug without a convenience tool like Laravel Herd. However, the information regarding browser add-on/extension can be skipped depending on the situation.

Conclusion from Setup

Problems when setting up Xdebug are not uncommon. Not everything works as described here or elsewhere.

Sometimes the operating system interferes because of occupied ports, DNS problems or because Xdebug is not addressed correctly.

Setting up PHP projects professionally while relying on proven development workflows? The team at mindtwo supports you in this – from the development environment to deployment. Feel free to get in contact.