[PHP] Xdebug のリモートデバッグ、理解していますか？

PHP のデバッグツールである [Xdebug](https://xdebug.org/) にはリモートデバッグという機能があります。

…というのは言うまでもなく、広く知られていると思います。

しかし、Xdebug の設定をなんとなくコピペで済ませたりしていないでしょうか？ 

## Xdebug のリモートデバッグ設定、理解していますか？
日本語のサイトを検索すると「 `xdebug.remote_enable=1` にして、`xdebug.remote_host` を xxx にして…」と設定だけが淡々と書かれた情報が多いです。

しかし、設定項目について、それぞれの意味を理解していないとトラブル時の対処が難しくなります。

## リモートデバッグとは何か？

リモートデバッグ (Remote Debugging) とは、**実行中の PHP とは別の場所にデバッグを制御するものが存在する** 方式です。

よくある環境だと、次のように図示できます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/10713/0d4e21a7-07fd-e825-e1cb-91397790e908.png)

**PHP 本体とは別の場所で動いてる方** がリモートです。
この図だと PHPStorm 側がリモートというわけです。

ここでは PHP 開発用のツールである [PHPStorm](https://www.jetbrains.com/phpstorm/) を例として出していますが、**Xdebug を動かすのに PHPStorm が必要というわけではありません。**
PHPStorm 以外にも Xdebug のリモートデバッグを行えるツールはたくさんあります。

## リモート・セッション
デバッグの制御を開始するには、PHP 本体側とリモートをつなぐ必要があります。
この一連の接続を「リモート・セッション」と呼びます。

## リモートデバッグを有効にする
**初期値ではリモートデバッグは無効になっています！**
したがって、次の設定値を `1` にする必要があります。

|設定|役割|初期値|
|:--|:--|:--|
| **`xdebug.remote_enable`** | `1` にするとリモートデバッグが有効になる | `0` |

## リモートへの接続設定
Xdebug 側からリモートにつなぐために、**どこにつなぐのか？** という設定が必要です。といっても、単純な TCP 接続なのでホストとポート番号のみです。

|設定|役割|初期値|
|:--|:--|:--|
| **`xdebug.remote_host`** | つなぎ先のホスト名 | `localhost` |
| **`xdebug.remote_port`** | つなぎ先のポート番号 | `9000` |

たとえば、PHP本体とリモート（PHPStorm等）が同じマシンにある場合は初期値のままで使える、ということになります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/10713/b048f1f8-df8b-e1e6-f003-5854782ef33d.png)


PHP を実行しているのが同じマシンではなく、Docker のコンテナ内にある！といった場合は `xdebug.remote_host` を変えればOKです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/10713/dc0ff83e-c5af-6b95-243a-337709e17f72.png)


## Xdebug のリモート・セッションはいつ始まっていつ終わるのか？
`xdebug.remote_enable=1` にすると、すぐにセッションが始まるわけではありません。

php-fpm のように、HTTPリクエストを受け付ける方式の場合 `XDEBUG_SESSION_START=xxx` というパラメータを付けてアクセスすると、はじめて Xdebug のリモート・セッションが ON になります。

`xxx` の部分は任意の名前で OK です。

▼ 実際に `XDEBUG_SESSION_START` ありなしで比べてみるとわかります
![xdebug_session_start.gif](https://qiita-image-store.s3.amazonaws.com/0/10713/1cf76304-9b7d-4f54-fa5b-dcad3218e2ad.gif)



しかし、常に自動で開始させることもできます。
`xdebug.remote_autostart=1` を設定するとパラメータがなくても自動的にセッションが始まるようになります。

|設定|役割|初期値|
|:--|:--|:--|
| **`xdebug.remote_autostart`** | `1` にすると常にセッションを自動開始する | `0` |

一度セッションが始まると、`XDEBUG_REMOTE_SESSION` という Cookie がセットされ、それが残っている限りはずっとセッションが続きます。

では、セッションはいつ終わるのでしょうか？

`XDEBUG_SESSION_STOP` というパラメータをつけてアクセスすると、セッションを止めることができます。また、Cookie の有効期限が切れるとセッションも切れます。ここの有効期限は `xdebug.remote_cookie_expire_time` で調整できます。

|設定|役割|初期値|
|:--|:--|:--|
| **`xdebug.remote_cookie_expire_time`** | XDebug のセッションを維持する Cookie の有効期限 | `3600` |


## Xdebug のセッション開始モード（ `req` と `jit` ）
基本、セッション開始は上記のやり方で即座に開始されます（ `req` モードといいます）が、これを **エラーが初めて起こったときに開始する** ように変更することもできます。

このエラーが起こって初めてセッションを開始するモードを **jitモード** といいます。

|設定|役割|初期値|
|:--|:--|:--|
| **`xdebug.remote_mode`** | `jit` にするとエラーが初めて起こったときにセッションを開始する | `req` |

**`jit` モードにすると、`XDEBUG_SESSION_START` パラメータは無視され**、エラー発生直後で必ずセッションがつながります。

▼ 試しにわざとエラーを起こしてみるとこのようになりました。`XDEBUG_SESSION_START` パラメータを与えていないのにデバッグ制御ができているのがわかります。
![jit-mode.gif](https://qiita-image-store.s3.amazonaws.com/0/10713/482098e8-eaa3-ea19-b548-3f7ff6d42850.gif)


## （応用編）複数のリモートをつなぐには？
ひとつの PHP に対して複数のリモートセッションをつなぐ方法もあります。`xdebug.remote_host` で決定するのではなく、**PHPにアクセスした側の IP アドレスに自動的につなぎにいってくれる** 機能があります。この機能は `xdebug.remote_connect_back=1` にすることで有効になります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/10713/5cd42c17-369e-20fe-1804-6fe37fccd8e6.png)


（※ただし、HTTP ヘッダから IP アドレスを取る仕組みなので CLI 実行の場合はうまく動きません）

|設定|役割|初期値|
|:--|:--|:--|
| **`xdebug.remote_connect_back`** | `1` にするとPHPにアクセスした側の IP アドレスに自動的につなぎにいってくれる | `0` |

この設定を ON にすると、`xdebug.remote_host` は無視されます。

## （応用編） 間にルーターなどがあり、`connect_back` が使えない場合
複数リモートを管理する中間サーバー (DBGp Proxy) を立てる必要があります。
ここについてはすでに Qiita にすばらしい記事があるので参照すると良いでしょう

[【PHP】リモートマシンのデバッグを、「複数人で」やる【Xdebug×DBGpProxy】](https://qiita.com/takudo/items/35d2fd3c6e9846715bf4)


## Xdebug リモートデバッグの設定値まとめ
この記事で紹介した `xdebug.remote_...` の設定値を振り返ってみます。

|設定|役割|初期値|
|:--|:--|:--|
| **`xdebug.remote_enable`** | `1` にするとリモートデバッグが有効になる | `0` |
| **`xdebug.remote_host`** | つなぎ先のホスト名 | `localhost` |
| **`xdebug.remote_port`** | つなぎ先のポート番号 | `9000` |
| **`xdebug.remote_autostart`** | `1` にすると常にセッションを自動開始する | `0` |
| **`xdebug.remote_cookie_expire_time`** | XDebug のセッションを維持する Cookie の有効期限 | `3600` |
| **`xdebug.remote_mode`** | `jit` にするとエラーが初めて起こったときにセッションを開始する | `req` |
| **`xdebug.remote_connect_back`** | `1` にするとPHPにアクセスした側の IP アドレスに自動的につなぐようになる。よって `xdebug.remote_host` は無視される | `0` |



## 参考になるサイト
公式ドキュメントがやはりいちばん網羅的でわかりやすいです。英語ですが。

- [Remote Debugging - Xdebug: Documentation](https://xdebug.org/docs/remote)