Xdebug
Debugging Tool
Xdebug
Overview
Xdebug is a PHP debugging and profiling extension. It provides step debugging, stack traces, and code coverage analysis, widely used as an essential tool for PHP development.
Details
Xdebug is the most important debugging and profiling extension for PHP development, with development started by Derick Rethans in 2002. It has become the de facto standard debugging tool for PHP developers, used by millions of developers worldwide. Key features include step debugging, function trace, profiling, code coverage analysis, and improved error reporting.
Xdebug 3 (released in 2020) introduced a mode-based configuration system that significantly improved performance and security. While previous versions had individual enabling settings for each feature, Xdebug 3 allows selective activation of only needed features through the xdebug.mode
setting, including debug (step debugging), develop (development support), coverage (coverage analysis), profile (profiling), and trace (function trace).
IDE integration has also made significant progress, working with major IDEs like PhpStorm, Visual Studio Code, Eclipse, and Sublime Text to provide intuitive GUI-based breakpoint setting, variable monitoring, step execution, and call stack analysis. The standardized debug communication through the DBGp protocol provides consistent debugging experience across various development environments.
Docker environment usage has been simplified, and container debugging that was difficult with pre-Xdebug 3 versions is now easily configured. Remote debugging functionality enables debugging in production-like environments. Development continues actively as of 2024, with support for the latest PHP 8.x features.
Pros and Cons
Pros
- De Facto Standard: Standard debugging tool in PHP development
- Rich Features: Step debugging, profiling, coverage analysis
- IDE Integration: Seamless integration with major IDEs
- Docker Support: Easy configuration in container environments
- Remote Debugging: Network-based debugging capabilities
- Detailed Information: Enhanced error messages and stack traces
- Open Source: Available for free use
- Active Development: Continuous improvement and PHP new version support
Cons
- Performance Impact: Execution speed degradation when debugging enabled
- Configuration Complexity: Complicated initial setup and IDE integration
- Memory Consumption: Increased memory usage with large applications
- Production Risk: Security risk if accidentally enabled in production
- Dependencies: Installation requirements as PHP extension
- Version Compatibility: Combination issues between PHP/IDE/Xdebug versions
- Learning Curve: Time required to learn effective usage
Key Links
- Xdebug Official Site
- Xdebug Official Documentation
- Xdebug GitHub Repository
- Xdebug Installation Guide
- Xdebug Step Debugging
- Xdebug and VS Code Setup
Usage Examples
Xdebug Installation
# Installation using PECL
pecl install xdebug
# Using Linux package manager
sudo apt-get install php-xdebug # Ubuntu/Debian
sudo yum install php-xdebug # CentOS/RHEL
# As development dependency with Composer
composer require --dev xdebug/xdebug
# Installation verification
php -m | grep xdebug
php --version
Basic Configuration in php.ini
; Xdebug 3 basic configuration
zend_extension=xdebug
; Mode setting (multiple modes allowed)
xdebug.mode=debug,develop,coverage,profile
; Step debugging configuration
xdebug.start_with_request=yes
xdebug.client_host=localhost
xdebug.client_port=9003
; IDE Key (optional)
xdebug.idekey=PHPSTORM
; Log configuration
xdebug.log=/tmp/xdebug.log
xdebug.log_level=7
; Output directory
xdebug.output_dir=/tmp/xdebug
VS Code Debug Configuration
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Listen for Xdebug",
"type": "php",
"request": "launch",
"port": 9003,
"pathMappings": {
"/var/www/html": "${workspaceFolder}"
}
},
{
"name": "Launch currently open script",
"type": "php",
"request": "launch",
"program": "${file}",
"cwd": "${fileDirname}",
"port": 0,
"runtimeArgs": [
"-dxdebug.start_with_request=yes"
],
"env": {
"XDEBUG_MODE": "debug,develop",
"XDEBUG_CONFIG": "client_port=${port}"
}
}
]
}
PhpStorm Debug Configuration
<?php
// PhpStorm configuration
// 1. Settings → Languages & Frameworks → PHP → Debug
// 2. Xdebug port: 9003
// 3. Settings → Languages & Frameworks → PHP → Servers
// 4. Server settings: Host=localhost, Port=80, Debugger=Xdebug
// Start debug session in browser
// Add ?XDEBUG_SESSION_START=PHPSTORM to URL
// Or use Xdebug helper extension
?>
Step Debugging Practical Examples
<?php
// PHP code for debugging
class UserService
{
private $database;
public function __construct($database)
{
$this->database = $database;
}
public function getUserById($id)
{
// Set breakpoint on this line
$sql = "SELECT * FROM users WHERE id = ?";
$stmt = $this->database->prepare($sql);
// Check variable values
$stmt->bind_param("i", $id);
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();
// Conditional debugging
if ($user === null) {
// Set breakpoint here
throw new Exception("User not found: " . $id);
}
return $user;
}
public function createUser($userData)
{
// Step-in/step-over tracking
$this->validateUserData($userData);
$sql = "INSERT INTO users (name, email, age) VALUES (?, ?, ?)";
$stmt = $this->database->prepare($sql);
// Array data inspection
$stmt->bind_param("ssi",
$userData['name'],
$userData['email'],
$userData['age']
);
return $stmt->execute();
}
private function validateUserData($userData)
{
// Local variable inspection in function
$required = ['name', 'email', 'age'];
foreach ($required as $field) {
if (!isset($userData[$field])) {
// Debug information on error
throw new InvalidArgumentException("Missing field: " . $field);
}
}
}
}
// Start debug session
$userService = new UserService($database);
// Start step execution here
try {
$user = $userService->getUserById(123);
// Variable monitoring
$newUser = [
'name' => 'John Doe',
'email' => '[email protected]',
'age' => 30
];
$userService->createUser($newUser);
} catch (Exception $e) {
// Debug on exception occurrence
error_log($e->getMessage());
}
?>
Profiling Configuration and Analysis
; Profiling-specific configuration
xdebug.mode=profile
xdebug.start_with_request=trigger
xdebug.trigger_value=profile
xdebug.output_dir=/tmp/xdebug/profiles
; Filter configuration (specific requests only)
xdebug.profiler_enable_trigger=1
<?php
// Code for profiling
function heavyFunction()
{
// Heavy processing simulation
for ($i = 0; $i < 1000000; $i++) {
$result = sqrt($i) * 2.5;
}
return $result;
}
function databaseQuery()
{
// Database processing simulation
sleep(0.1); // 100ms simulation
return "query result";
}
// URL: http://localhost/script.php?XDEBUG_PROFILE=profile
// Run profiling
$start = microtime(true);
$data = heavyFunction();
$dbResult = databaseQuery();
$end = microtime(true);
echo "Execution time: " . ($end - $start) . " seconds";
// Analyze /tmp/xdebug/profiles/*.out files with KCacheGrind
?>
Function Trace Configuration
; Function trace configuration
xdebug.mode=trace
xdebug.start_with_request=trigger
xdebug.trace_format=0
xdebug.trace_options=0
xdebug.collect_params=4
xdebug.collect_return=1
xdebug.show_mem_delta=1
<?php
// Functions for tracing
class Calculator
{
public function add($a, $b)
{
return $this->performOperation($a, $b, '+');
}
public function multiply($a, $b)
{
return $this->performOperation($a, $b, '*');
}
private function performOperation($a, $b, $operation)
{
switch ($operation) {
case '+':
return $a + $b;
case '*':
return $a * $b;
default:
throw new InvalidArgumentException("Unsupported operation");
}
}
}
// URL: http://localhost/calc.php?XDEBUG_TRACE=trace
$calc = new Calculator();
$result1 = $calc->add(10, 20);
$result2 = $calc->multiply(5, 6);
// Check detailed execution history in trace file
?>
Xdebug in Docker Environment
# Dockerfile Xdebug configuration
FROM php:8.2-apache
RUN pecl install xdebug \
&& docker-php-ext-enable xdebug
COPY xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
EXPOSE 80
# Docker xdebug.ini
zend_extension=xdebug
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_host=host.docker.internal
xdebug.client_port=9003
xdebug.idekey=PHPSTORM
; Log configuration
xdebug.log=/tmp/xdebug.log
# docker-compose.yml
version: '3.8'
services:
php-app:
build: .
ports:
- "80:80"
volumes:
- .:/var/www/html
environment:
- XDEBUG_MODE=debug
- XDEBUG_CONFIG=client_host=host.docker.internal
Code Coverage Measurement
<?php
// Start code coverage measurement
xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
// Code under test
function processUser($user) {
if (!isset($user['name'])) {
return false; // Is this line covered?
}
if ($user['age'] < 18) {
return 'minor'; // Is this line covered?
}
return 'adult';
}
// Execute test cases
$testCases = [
['name' => 'John', 'age' => 25],
['name' => 'Jane', 'age' => 16],
// ['age' => 30] // Don't test case missing name
];
foreach ($testCases as $user) {
$result = processUser($user);
echo "Result: $result\n";
}
// Get coverage data
$coverage = xdebug_get_code_coverage();
xdebug_stop_code_coverage();
// Generate coverage report
foreach ($coverage as $file => $lines) {
echo "File: $file\n";
foreach ($lines as $line => $count) {
if ($count === -1) {
echo " Line $line: Not executable\n";
} elseif ($count === -2) {
echo " Line $line: Dead code\n";
} elseif ($count === 0) {
echo " Line $line: Not covered\n";
} else {
echo " Line $line: Covered ($count times)\n";
}
}
}
?>
CLI Environment Debugging
# Xdebug debugging in command line
export XDEBUG_MODE=debug
export XDEBUG_SESSION=1
# Execute PHP script (after starting IDE listener)
php -dxdebug.start_with_request=yes script.php
# Execute profiling with specific configuration
php -dxdebug.mode=profile -dxdebug.start_with_request=yes script.php