はじめに:なぜ、あなたのXdebugは「沈黙」するのか?
テックリードの私たちが新しいプロジェクトに参属したとき、最もフラストレーションが溜まる瞬間の一つが、「ブレークポイントを設定したのに、なぜかIDEがデバッグを捉えてくれない(スルーされる)」という現象だ。
「あれ、`php.ini`の設定は合っているはずなのに……」「Docker環境だからルーティングがおかしいのか?」
画面には無慈悲にHTTP 500やタイムアウトが表示され、手探りで`var_dump`を埋め込む原始的なデバッグに戻っていく——。この悪夢のような時間を、私たちは幾度となく経験してきたはずだ。
原因不明の接続エラーやハングアップに直面したとき、多くのエンジニアはIDE側の設定画面ばかりを疑う。しかし、真の解決の鍵は、PHP拡張モジュールであるX自身が吐き出す内部ログ(`xdebug.log`)の深淵にある。
今回は、Xdebugの通信レイヤーで何が起きているのかを完全に可視化し、接続拒否やハングアップの根本原因を秒速で特定するための実践的アプローチを伝授する。表面的な設定のコピペではなく、通信プロトコルの内部挙動から逆算したプロのトラブルシューティング術をマスターしよう。
—
1. Xdebug内部ログ(xdebug.log)の全貌と有効化の作法
Xdebugは、IDE(PhpStormやVS Codeなど)とDBGp(DEBUG Protocols)という独自の通信プロトコルを用いてTCP/IPソケット通信を行っている。この通信の確立過程やエラー、パケットの往来を記録するのが `xdebug.log` である。
まずは、このログ出力を有効化し、すべての挙動を丸裸にするための設定を施す。
実践的な `php.ini` 設定ベストプラクティス
開発環境(Dockerコンテナ内等)における `php.ini` または `xdebug.ini` の推奨設定は以下の通りだ。単にログを有効にするだけでなく、トラブルシューティングに必要な詳細度(LogLevel)を引き上げるのがポイントである。
[xdebug]
; モジュールのモードをデバッグに設定
xdebug.mode = debug
; リクエスト発信時に自動でデバッグを開始(CLIやAPIテスト時に極めて有効)
xdebug.start_with_request = yes
; IDEが待ち受けているホスト側のIP(Dockerの場合は host.docker.internal やホストマシンのIPを指定)
xdebug.client_host = “host.docker.internal”
; IDE側との通信ポート(VS Codeは9003、PhpStormもデフォルト9003)
xdebug.client_port = 9003
; 【最重要】Xdebug自体の動作ログの出力先パス
xlog.log = “/var/log/xdebug/xdebug.log”
; 【最重要】ログの冗長レベルを「7(Connection)」に設定し、ハンドシェイクの全貌を記録する
; 0: Critical, 1: Error, 3: Warning, 5: Communication, 7: Connection (推奨), 10: Debug
xdebug.log_level = 7
> アーキテクトの知見:なぜ `log_level = 7` なのか?
> デフォルトのログレベルでは、致命的なエラーしか出力されないため、「なぜ接続が切断されたのか」の前兆(タイムアウトやハンドシェイクの不一致)が分からない。レベルを `7`(または詳細を追いたい場合は `10`)に設定することで、IDEとの間で交わされるTCPソケットのネゴシエーションがすべてテキストとして流し込まれるようになる。
—
2. ログから深刻なバグの兆候を見抜く:通信断絶の3大パターン
`xdebug.log` を有効化したら、コンテナのログやファイル末尾を `tail -f` で監視しながらブラウザからリクエストを飛ばしてみよう。
ここからは、ログに出現する「深刻なバグの兆候」と、そこから読み解くべきインフラ・コードの不備をパターン別に解説する。
パターンA:【Connection Refused】(接続拒否)
ログに出力される兆候:
[311] Log opened at 2026-03-30 08:00:00
[311] E: Creating socket for ‘host.docker.internal:9003’.
[311] W: Creating socket: Connection refused (111)
[311] Critical: Couldn’t connect to client ‘host.docker.internal:9003’.
- 原因の特定:
Xdebugはクライアント(IDE)へ接続を試みたものの、宛先で誰も待ち受けていない状態。
- 裏側の動き:
PHPのプロセスは実行されているが、IDE側の「リスニングモード(電話の受話器マーク)」がオフになっているか、Docker環境においてホスト側のポート(9003)が正しくフォワードされていない、あるいはファイアウォール(UFWやiptables)がブロックしている。
- 対策:
IDEのリスニングアイコンがアクティブか確認する。Dockerの場合、`host.docker.internal` が正しくホストを指しているか、または `xdebug.client_host` にホストの物理IP(例: `172.17.0.1`)を直接ハードコードして検証する。
パターンB:【Connection Timeout】(ハングアップ・無応答)
ログに出力される兆候:
[412] Log opened at 2026-03-30 08:05:00
[412] I: Connecting to client, expected IP: ‘172.18.0.1’, port: ‘9003’.
[412] I: Connected to client.
[412] W: Time-out connecting to client. (or hanging without further logs)
- 原因の特定:
TCPのソケット接続自体は成功(`Connected to client`)しているが、その後のDBGpプロトコルによる初期ハンドシェイク(XMLメッセージのやり取り)の途中で、IDE側がフリーズしている、あるいはブレークポイントの評価に時間がかかりすぎてタイムアウトしている。
- 裏側の動き:
巨大な配列やオブジェクトを持つセッション変数が存在し、XdebugがそれをシリアライズしてIDEに送信しようとした瞬間にメモリ上限やネットワークバッファが溢れているケースが多い。
- 対策:
`xdebug.connect_timeout_ms` の値をデフォルト(200msなど)から一時的に引き上げ(例: `2000`)、IDE側のパフォーマンスやガベージコレクションの状態を疑う。
パターンC:【ID Mismatch / Protocol Error】(バージョン不整合)
ログに出力される兆候:
[520] I: Time to launch debugger.
[520] E: IDE is talking a different protocol version. (DBGp/3.x required)
- 原因の特定:
PHP本体のXdebugのバージョン(例: Xdebug 3)と、IDE側のプラグインが期待しているプロトコルバージョンが乖離している。古いIDEプラグインをそのまま使用している場合に発生する。
- 対策:
IDEのプラグイン(PhpStormやVS CodeのPHP Debugなど)を最新版にアップデートし、Xdebug 3の仕様(ポート9003、`xdebug.mode=debug`)に完全に準拠しているか確認する。
—
3. チーム開発の生産性を爆上げする共有化ルール & 開発環境設定
個人のローカル環境だけでXdebugが動いても意味がない。チームメンバー全員が、OS(macOS, Linux, Windows/WSL2)やIDEの違いに依存せず、「ワンクリックでデバッグが起動する」状態をコードベースとして担保する必要がある。
ここでは、チーム開発で破綻しないためのベストプラクティス構成例を公開する。
1. Docker Compose によるインフラの標準化
環境起因の接続エラーを防ぐため、Docker環境でのXdebug設定は `docker-compose.yml` と環境変数ファイル(`.env`)で一元管理する。
`docker-compose.yml` のベストプラクティス片:
version: ‘3.8’
services:
app:
build:
context: .
dockerfile: docker/php/Dockerfile
volumes:
- .:/var/www/html
- ./docker/php/xdebug.ini:/usr/local/etc/php/conf.d/xdebug.ini:ro
environment:
- PHP_IDE_CONFIG=serverName=production-standard-server
extra_hosts:
# Linux環境でも host.docker.internal が確実にホストを指すようにする神設定
- “host.docker.internal:host-gateway”
2. VS Code 向けプロジェクト設定(`.vscode/launch.json`)
チームメンバーがVS Codeを使う場合、リポジトリに `.vscode/launch.json` を含めておくことで、設定の手間をゼロにする。
{
“version”: “0.2.0”,
“configurations”: [
{
“name”: “Listen for Xdebug (Docker & Local)”,
“type”: “php”,
“request”: “launch”,
“port”: 9003,
“pathMappings”: {
// コンテナ内の絶対パス と ローカルのプロジェクトパスを完全一致させる
“/var/www/html”: “${workspaceFolder}”
},
// ログの詳細化をIDE側でも有効にし、トラブルシューティングを容易にする
“log”: true,
“hostname”: “0.0.0.0”
}
]
}
—
4. 開発スピードを極限まで高めるキーボードショートカット & 神プラグイン
プロのテックリードとして、マウス操作でデバッグをコントロールしているようではスピードが落ちる。以下のショートカットとプラグインを指に覚え込ませ、脳とコードを直結させよう。
🚀 絶対に入れるべき神プラグイン(VS Code編)
1. PHP Debug (felixfbecker)
- 言わずと知れた標準にして最強のデバッガー。
2. PHP Intelephense (bmewburn)
- 高速な補完だけでなく、定義ジャンプとブレークポイントの親和性が非常に高い。
⚡ 開発スピードを加速させるキーボードショートカット(VS Code / PhpStorm共通思想)
| アクション | Windows / Linux | macOS | プロの実践的活用法 |
| :— | :— | :— | :— |
| ブレークポイントのトグル | `F9` | `F9` | 疑わしい行にカーソルを合わせ、手を止めずに即座にトグル。 |
| デバッグの開始 / 継続 (Continue) | `F5` | `F5` | ブレークポイントで止まった後、次のブレークポイントまで一気に飛ばす。 |
| ステップオーバー (Step Over) | `F10` | `F10` | 関数の中に入らず、現在のスコープの次の行へ(処理を高速に流す)。 |
| ステップイン (Step Into) | `F11` | `F11` | 関数・メソッドの内部に入り込み、変数の変化を細かく追う。 |
| 条件付きブレークポイント (Conditional) | `Ctrl + Shift + F9` | `Cmd + Shift + F9` | 【超重要】 ループ内の特定のID(例: `id == 500`)の時だけ止めることで、数千回のループ周回地獄から解放される。 |
> テックリードからの極意:
> 「無条件のブレークポイント」を何箇所も貼る開発者は二流だ。本当にバグがいる条件が分かっているなら、「条件付きブレークポイント」を使いこなせ。これだけでデバッグにかかる時間は1/10になり、認知負荷が劇的に軽減される。
—
おわりに:ログを制する者は、デバッグを制す
「動かない」という現象に直面したとき、感覚で設定ファイルを書き換える時代は終わった。
今回解説した `xdebug.log` の読み方、そして `log_level = 7` による通信の可視化手法を手にしていれば、IDEとPHPの間に何が起きているのかは100%論理的に説明がつくようになる。
接続拒否やハングアップに怯える時間はもう終わりだ。今すぐあなたの開発環境で `xdebug.log` を有効化し、コードの深層で起きている真実の通信をその目で確かめてみてほしい。あなたのチームの開発スピードは、今日この瞬間から確実に一段階上のステージへと引き上げられるはずだ。