Skip to main content

PHP Xdebug


PHP Xdebug

in mDIS

ICDP-Internal mDIS Developer Guide, 'Debugging' section

Stepwise remote debugging with Xdebug 3

Stepwise debugging is relatively easy, when everything has been set up correctly. However, Setting Up your development environment can take a lot of time, because it is difficult to get everything exactly right. To complicate things, each developer's environment is configured differently, so be ready to adapt the instructions below to your development setup.

Simple is better

Keep things as simple as possible. This tutorial is not applicable for complicated setups.
If you want to debug an mDIS instance that

  • runs on a server protected by powerful firewalls or VPNs, and/or
  • has port forwarding disabled on the SSH server (or on some other layer of the network stack)
  • sits behind a proxy server or a reverse proxy server,
  • is virtualized or lives inside a container, or
  • is hosted by a cloud provider, or on a server that is not under your control,

then all these measures make step-debugging mDIS harder.

Config values for Xdebug 3 are described here.

Setting up Xdebug requires much experimentation and patience.

Debugging of web pages with PHP Xdebug and Visual Studio Code

You need to be an administrator on your machine. Install 4 pieces of software to do remote debugging, and create a config file:

On the server, you need:

  1. The Xdebugopen in new window PHP extension.
    • Admin rights to install and configure the extension.

On your development workstation, you need:

  1. The VS Code extension "xdebug.php-debug"open in new window
  2. The browser extension Xdebug helper for Chromeopen in new window or for Firefoxopen in new window
  3. A config file .vscode/launch.json at the top level of your workspace folder

All 4 pieces are free/gratis.

Xdebug is an extension for PHP to assist with debugging and development.

From the Xdebug Homepageopen in new window

  • It contains a single step debuggeropen in new window to use with IDEs. This is crucial for mDIS debugging
  • It upgrades PHP's var_dump() function
  • It adds stack traces for Notices, Warnings, Errors, and Exceptions
  • It features functionality for recording every function call and variable assignment to disk
  • (It contains a profiler, and provides code coverage functionality for use with PHPUnit) - not explained here.

Installation

Here's a step-by-step guide to Install Xdebug for use with Visual Studio Code

For remote debugging, there are two hosts that must communicate during a debugging session: (1) The mDIS Server and (2) the developer's workstation, where the mDIS codebase is installed.

Due to those measures mentioned above, it is quite possible that remote debugging might not work at all. In this case, you will need to ask your network administrator for help or try "local" debugging: install a copy of your instance on your workstation, then setup Xdebug (and related tools), and debug that cloned instance runnning on your "bare metal".

mDIS Server

On the mDIS Server, the Xdebug Installation Processopen in new window is straightforward. Do that first. Here are the commands for debian-based Linux distributions:

# Explanations see below after code block
sudo apt install php-xdebug
sudo phpenmod xdebug
sudo apachectl restart
sudo systemctl restart php7.4-fpm # or php8.1-fpm, etc.

Verify Xdebug Configuration on the Server

On the web server, a configuration file xdebug.ini should be created and stored in an appropriate location, e.g., where the other php-ini files are stored. On Linux, that path can be found with php --ini. The filename might be 20-xdebug.ini, or it might be prefixed with some other number.

Do a phpenmod xdebug and restart the web server. This will load the Xdebug extension into PHP.

Run php -i on the command line. Better yet, create a phpinfo() page and see the phpinfo() output.
Check the phpinfo() page for xdebug entries to make sure that the Xdebug extension is loaded and that the settings are correct.

In the output of PHPinfo (or php -i), the value for xdebug.mode must be set to debug. (Not "profile" or "coverage").

Ensure that your php.ini or Xdebug 3 configuration file on the server has the correct settings. Here's an example configuration:

# Typically, in your php.ini or Xdebug configuration file, you should have:
[Xdebug]
zend_extension=xdebug.so
xdebug.mode=debug
xdebug.start_with_request=yes
# xdebug.client_host=<IP Address> # this restricts debugging to <IP Address>. Security measure.
xdebug.client_port=9003
xdebug.log=/var/log/xdebug.log
xdebug.idekey=VSCODE

;;; default port is 9003 for Xdebug 3. 
;;; client settings must match this value.

If your web app runs on localhost/your own PC, then you are all set.

Key Points:

  • xdebug.client_host: This should point to your local machine. If you're using SSH remote port forwarding, ensure it's set to the correct IP.
  • xdebug.client_port: Match this with the port specified in your launch.json (commonly 9003 for Xdebug 3).
  • xdebug.idekey:
    • Ensure this matches the idekey in your VSCode configuration if specified.
    • Ensure this matches the IDEKEY entry in the settings of your xdebug Browser Extension.

Xdebug Troubleshooting

Port Issues

Docker container port forwarding

If your machine runs inside a Docker container, you need to add another port-forwarding rule to your Docker Container configuration. Redirect incoming traffic on port 9003 to the container's port 9003.

SSH Remote Port Forwarding

If your web application runs on a remote server and you have mounted the directory of the web app locally, you can establish a remote port forwarding tunnel to facilitate debugging with Xdebug. Execute the following command on your local machine:

ssh -R 9003:localhost:9003 youruser@mdishost.com

Explanation of the Command:

  • -R 9003:localhost:9003:
  • -R: Specifies remote port forwarding.
    • 9003 (first instance): The port on the remote server that SSH will listen to.
    • localhost:9003: The destination on your local machine where the traffic will be forwarded. Here, localhost refers to your local machine from the perspective of the SSH client.

What This Does:

  • Remote Listening: SSH will listen on port 9003 on the remote server (mdishost.com).
  • Forwarding Traffic: Any data sent to port 9003 on the remote server will be securely forwarded to port 9003 on your local machine.
  • Data Flow: This setup allows Xdebug running on the remote server to communicate with your IDE (e.g., VSCode) on your local machine by sending debugging data through the established SSH tunnel.
Key Points to Consider for SSH Remote Port Forwarding:
  • Using localhost vs. YourLocalIP:
    In most cases, using localhost is sufficient because the SSH client (your local machine) can be referenced directly. However, if your local machine has multiple network interfaces or if localhost does not resolve correctly in your environment, you might need to specify your local machine's IP address instead.

  • Firewall Port Opening If your client is protected by a software firewall (under your control), you might need to open port 9003 or whatever port you prefer to listen on for Xdebug. Alternatively, you might also need to disable the software firewall on your mDIS server or add a new rule for incoming traffic.

UFW on Ubuntu

With Ubuntu, try something like this:

sudo ufw status numbered
sudo ufw allow from 139.17.0.0/24 to any port 9003

Alternatively, set a more specific hostname and preferred port number, respectively.

Another expert command to try out during debugging is

tcpdump -vv -nn -i any dst port 9003

but it needs tcpdump installed.

Config file mismatches

If your value for xdebug.remote_host is set to localhost in the ini file, you sometimes cannot remote-debug when the value (in the .vscode/launch.json file) is set to 127.0.0.1 or ::1 (although it's semantically the same). Check the actual value in the output of your web server access log.

Xdebug Log Files and systemd's PrivateTmp:

Specified Logfile Location: If you have specified a logfile location for Xdebug, such as /var/tmp/xdebug.log, the log file will be created within the remote server's directory structure. However, due to systemd's PrivateTmp feature, the actual path may appear as:

/var/tmp/systemd-private-SOME_ID-apache2.service-.../tmp/xdebug.log

This occurs because systemd isolates temporary directories for enhanced security.

Both /var/tmp/xdebug.log and the one just mentioned may contain content such as:

sudo tail -f /var/tmp/systemd-private-9b637db8485f46859a97389f791b08b6-apache2.service-Ify9fj/tmp/xdebug.log
[1034122] Log closed at 2020-06-09 13:48:32
...
[1034122] Log opened at 2020-06-09 13:48:32
[1034122] I: Checking remote connect back address.
[1034122] I: Checking header 'HTTP_X_FORWARDED_FOR'.
[1034122] I: Checking header 'REMOTE_ADDR'.
[1034122] I: Remote address found, connecting to <IP>:9003.
[1034122] E: Time-out connecting to client (Waited: 200 ms). :-()
[1034122] Log closed at 2020-06-09 13:48:32
... And so on.
Debugger gets stuck

When the debugger cannot continue stepping through code during an interactive session:

  • Remove unneeded watch variables, especially those that intercept "native code" such as the mysqli....() functions.
  • Also, check your value of the "ignore" setting in launch.json.

mDIS Client

On the client, the PC workstation where the developers want to carry out the debugging session, some files need to be set up as well.

First, a copy of the codebase of mDIS that runs on the server must be present on the client. Both client and server should use the same codebase. To do so, simply clone the same git repository or mount the remote directory with SSHFS (or some other mechanism that is available to you).

In VS Code, there should be a workspace created. Add the local folder with the mDIS codebase to the workspace.
In the client directory, where the root folder of the mDIS codebase resides, there must be a subdirectory .vscode. If it is not there, create it.

Inside the directory .vscode, create a file launch.json with this content (which you need to edit in places):

// launch.json
{
    "version": "0.2.0",
    "configurations": [{
        "name": "php:xdebug",
        "type": "php",
        "request": "launch",
        "port": 9003,
        "stopOnEntry": false,
        "pathMappings": {
            "/var/www/dis": "${workspaceFolder}",
            "/app": "${workspaceFolder}/"
        },
        "ignore": [
            "**vVendor/**/*.php"
        ],
        "log": true, // Enable logging for troubleshooting
        "xdebugSettings": {
            "max_children": 100,
            "max_data": 10000,
            "max_depth": 5
        }
    }]
}

Adapt the pathMappings according to your needs.

Use Absolute Paths in `pathMappings`:

Sometimes using absolute paths can help in resolving path discrepancies.

Example:

"pathMappings": { "/var/www/dis/mdis-geolab/web": "/mnt/wb31/mdis-geolab/web" }

Ensure that /mnt/wb31/mdis-geolab/web accurately reflects your local project directory.

In the full launch.json example, two "pathMappings" are predefined for two different mDIS Servers which can be debugged individually (by a roaming developer). On mDIS Server 1, the PHP code resides in /var/www/dis, and on mDIS Server 2 (a Docker container), the PHP code resides in Docker volume /app. They are mapped to the active VSCode workspace folder. The left side of the colon character identifies a remote directory that enables the debugger to be able to step into web/index.php.
The ignore: directive ensures that third-party code is ignored during debugging.

Browser Extensions

Install the extension "xdebug-helper" for Google Chromeopen in new window or the equivalent extension "xdebug-helper for Firefox"open in new window.

In the respective configuration page ("Preferences") of the Browser Add-On, set the IDEKey Config Value to "VSCODE":

idekey

Enabling the debugging extension

The extension needs to be enabled in two separate locations.

First, enable the browser extension in the extension settings.
Then look for the icon of this extension in the address bar of the browser.

browserextension

Setting it to green means enabling it. The extension tells the browser to start or "switch on" a debugging session on the server and send some additional data with each HTTP request.

Do not have "Debug" enabled (set to green) when you are using the Adminer web-based SQL client (if the Adminer tool is reachable via the same URL). Xdebug will try to intercept the adminer.php script, and you do not want this. The code of adminer.php is minified and obfuscated, do not try to debug it.

Debugging

Launching a debugger

Start your debugging session by clicking on the "Debug" icon in the sidebar of the VSCode IDE (1). Then click on "Debug and run" (2). The debug configuration selected (3) should be php:xdebug.

xdebug

After clicking on "Debug and Run," see red (1) below; the DebugBar (2) should appear.

debugbar

To determine where Xdebug looks for on your development workstation, in the file .vscode/launch.json, set keys stopOnEntry to true and log to true temporarily.

  • stopOnEntry=true sets a breakpoint on the first line of code in the index.php file, so you can step through your code from the very beginning if the debugger is connected at all.
  • log=true writes verbose output into the debug pane of the VSCode IDE (and to the logfile on the server as well).

Then click on the "Continue" or "Step Over" icon in the Debugbar, see (2) in the figure above. If your pathMapping is wrong, a dialog box with an error message will appear and tell you where Xdebug looked for PHP files on your server and why it didn't find them, most likely because of an incorrect path mapping.

Happy debugging!

Hunting down bugs

There is no general strategy to find and remove bugs in a codebase. Every problem is different.

(TBC)