PHP Xdebug
in mDIS
Related Pages
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 is what takes 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.
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 there are a lot of security measures in place that make step-debugging mDIS harder. Setting up Xdebug requires much more experimentation and patience than described below.
It is also 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".
Config values for Xdebug 3 are described here. Xdebug 3.0.0 was released in late 2020 and works with PHP 7.4 and higher. Instructions for earlier versions of Xdebug are subtly different.
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:
- The Xdebug PHP extension.
- Admin rights to install and configure the extension.
On your development workstation, you need:
- The VS Code extension "xdebug.php-debug"
- The browser extension Xdebug helper for Chrome or for Firefox
- 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 Homepage
- It contains a single step debugger 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.
mDIS Server
On the mDIS Server, the Xdebug Installation Process 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 yourlaunch.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 yourxdebug
Browser Extension.
- Ensure this matches the
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 Chrome or the equivalent extension "xdebug-helper for Firefox".
In the respective configuration page ("Preferences") of the Browser Add-On, set the IDEKey Config Value to "VSCODE":
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.
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
.
After clicking on "Debug and Run," see red (1) below; the DebugBar (2) should appear.
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 theindex.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)