はじめに
PHPでのXDebugによるデバッグ環境構築は、セットアップの手数が多く慣れていないと苦戦してしまうことが多いです。
いつも試行錯誤しながら何となく動作させていたのですが、苦手意識を無くすため時間をとってステップを追って仕組みを理解しました。
同じような方も多いかと思いますので備忘録として検証手順を書いておきます。もし間違えている部分があったらコメント欄でやさしく教えてください。
環境
検証は、下記環境で実施しました。
PHPのコンテナはapacheを使っていますがphp-fpmでも基本は同じです。
ホストOS
- macOS Monterey
- Docker Desktop for Mac: 4.3.2
PhpStorm
- 2021.3.2
- Build #PS-213.6777.58, built on January 31, 2022
Dockerイメージ
- php:8.0-apache
localの作業ディレクトリ
- ~/project/2022-xdebug-docker-php
XDebug
- XDebug3
- XDebug2ではない!!
- XDebug2は、XDebug3とconfファイルの書き方が違うので注意!!!
バージョン
細かなバージョンは下記の通りです
% sw_vers
ProductName: macOS
ProductVersion: 12.3
BuildVersion: 21E230
% docker --version
Docker version 20.10.11, build dea9396
docker compose exec web php --version
PHP 8.0.2 (cli) (built: Feb 9 2021 19:12:13) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.2, Copyright (c) Zend Technologies
with Xdebug v3.1.4, Copyright (c) 2002-2022, by Derick Rethans
デバッグ対象のPHP環境を作る
フォルダ構成
検証に使ったプロジェクトは下記の構成です。
全部で4ファイルだけです。
├── docker
│ └── php
│ ├── Dockerfile
│ └── docker-php-ext-xdebug.ini
├── docker-compose.yml
└── src
└── index.php
ファイルの中身
各ファイルの中身は下記の通りです。
docker/php/Dockerfile
Dockerfileではxdebugのインストール・有効化と、xdebugの設定ファイルをコピーしています。
FROM php:8.0-apache
RUN pecl install xdebug && docker-php-ext-enable xdebug
COPY docker-php-ext-xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
WORKDIR /var/www
xdebugのインストールで設定が空のdocker-php-ext-xdebug.ini
が作成されるのですが、COPYで作成されたファイルを上書きしています。
docker/php/docker-php-ext-xdebug.ini
xdebug.iniではXDebug3.x向けの設定をします(XDebug2ではないです!)
zend_extension=xdebug
# for XDebug3
[xdebug]
xdebug.client_host=host.docker.internal
xdebug.client_port=9003
xdebug.mode=debug
#xdebug.idekey = PHPSTORM
xdebug.start_with_request=yes
xdebug.idekey
はなくても良いのですが、設定する人が多いようなのでコメントアウトの形で残してあります。xdebug.start_with_request=yes
は開発中は設定不要ですが、検証やXDebug設定のトラブルシューティング時にはyesにしておくことで問題の切り分けがとても楽になります。ですので、本当に必要な設定は3項目だけです。各設定項目の意味は後述します。
docker-compose.yml
先ほど作ったDockerfileのディレクトリを指定します。
version: "3.7"
services:
web:
build: ./docker/php
volumes:
- ./src:/var/www/html:cached
ports:
- "8080:80"
ポートは80 -> 8080にマッピングし直しているので、macOSからはhttps://localhost:8080/でアクセスできます。
src/index.php
Hello Worldのメッセージと現在時刻を表示します。ステップ実行の確認をするため、あえて変数に値を入れています。
<?php
echo "Hello World!" . PHP_EOL;
$t = date(DATE_ATOM);
echo $t . PHP_EOL;
xdebug.ini設定項目の意味
ここでxdebug.ini
で設定した項目の意味を説明しておきます。
xdebug.client_host=host.docker.internal
PHPStormが起動しているホスト名を指定します。Docker内から見たmacOSのホスト名のことです。Dockerはhost.docker.internal
の名前でmacOSへアクセスできるようIPを自動で設定してくれるので、これを利用しています。
xdebug.client_port=9003
PHPStormのデバッガが待ち受けしているポート番号です。何番でも良いのですがデフォルト値は9003で、PHPStormもインストールしたタイミングで9003をlistenするよう初期設定されているのでこれを踏襲します。
xdebug.mode=debug
これによってステップ実行を可能にする複数の設定を一括でOnに設定できます。
この1行で、以下の設定を行なったのと同じ意味になります。
xdebug.remote_enable=1
xdebug.default_enable=0
xdebug.profiler_enable=0
xdebug.auto_trace=0
xdebug.coverage_enable=0
xdebug.idekey = PHPSTORM
XDebugがどのIDEでデバッグできるかを識別するキーです。PHPStormの設定画面でフィルタ条件として指定することができます。ですが、Dockerのような一人でサーバを独り占めできる場合は設定なしでも大丈夫です。
xdebug.start_with_request=yes
yesにするとスクリプトを起動した直後に、最初のステップでブレークポイントがかかったような状態になります。
リクエストのたびにステップ実行が始まってしまうので、通常の開発ではno
(or 未指定)に指定した方が使いやすいです。ですが、最初のデバッガ設定では、一時的にyesにしておくことで疎通確認が非常に楽になります。
Dockerの起動
Dockerイメージのビルドと起動を行います。
ビルド
# コマンド
docker-compose build
# 実行結果
...
Building web
[+] Building 0.2s (9/9) FINISHED
起動
# コマンド
docker-compose up -d
# 実行結果
Creating network "2022-xdebug-docker-php_default" with the default driver
Creating 2022-xdebug-docker-php_web_1 ... done
動作確認
コンテナの起動の確認
コンテナ内のポートがホスト側から8080で見えるようマッピングされていることを確認します。
% docker-compose ps
Name Command State Ports
--------------------------------------------------------------------------------------------
2022-xdebug-docker-php_web_1 docker-php-entrypoint apac ... Up 0.0.0.0:8080->80/tcp
% docker-compose images
Container Repository Tag Image Id Size
--------------------------------------------------------------------------------------------
2022-xdebug-docker-php_web_1 2022-xdebug-docker-php_web latest 361078af6bd9 419.9 MB
ブラウザからのアクセス
macOS側からindex.phpページが見えることを確認します。
% curl http://localhost:8080/
Hello World!
2022-04-09T03:52:09+00:00
Chromeからもアクセスできることを確認しておきます。
XDebug3の設定確認
コンテナの中に入って、XDebug3の設定を確認しておきます。
コンテナ内でshellを起動します。
docker compose exec web bash
XDebugが入っていることとバージョンを確認します。
with Xdebug
が表示されること- バージョンが
v2.x
ではなくv3.x
であること
# コマンド
php --version
# 結果
PHP 8.0.2 (cli) (built: Feb 9 2021 19:12:13) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.2, Copyright (c) Zend Technologies
with Xdebug v3.1.4, Copyright (c) 2002-2022, by Derick Rethans
xdebug.ini
で指定した全てのパラメータが反映されていることを確認します。
# コマンド
php -i | fgrep xdebug.client_host
php -i | fgrep xdebug.client_host
php -i | fgrep xdebug.mode
php -i | fgrep xdebug.idekey
php -i | fgrep xdebug.start_with_request
# 結果
xdebug.client_host => host.docker.internal => host.docker.internal
xdebug.client_host => host.docker.internal => host.docker.internal
xdebug.mode => debug => debug
xdebug.idekey => PHPSTORM => PHPSTORM
xdebug.start_with_request => yes => yes
cliでのコマンド実行してみる
コンテナ内からphpファイルを直接実行できることを確認します。これは必須の確認事項ではありませんが、チェックしておくと設定でトラブルが起きた時に切り分けしやすいです。
# コマンド
php /var/www/html/index.php
# 結果
Xdebug: [Step Debug] Could not connect to debugging client. Tried: host.docker.internal:9003 (through xdebug.client_host/xdebug.client_port) :-(
Hello World!
2022-04-09T04:01:46+00:00
Xdebug: [Step Debug] Could not connect to debugging client
のエラーが出ているが気にしなくて良いです。これはxdebug.start_with_request = yes
にしていると、自動でサーバにアクセスされるのが理由です。xdebug.iniから該当の行をコメントアウトすれば表示されなくなります。
XDebug3への疎通確認
ここで、XDebugがホスト側のmacOSに接続できることを確認します。
検証手順を理解する上でいくつか前提知識が必要になるため、ここでまとめて書いておきます。
前提知識
-
XDebugではPHPスクリプトが動いているコンテナから、PHPStormが動いているIDE側へ接続が行われる
- ポートはデフォルトで
9003
が使われる - PHPStorm側では、電話のアイコンをクリックすることで待ち受けが開始される
- ポートはデフォルトで
-
疎通確認に便利な
nc
というcliコマンドがあるnc -l 9003
でport 9003の接続待ち受け(listen)ができるので、PHPStormの待ち受けを模倣できる
-
Dockerコンテナ内からは、ホストへ
host.docker.internal
のhostnameでアクセスできる -
Dockerコンテナ内にncコマンドが無い場合は、curlコマンドで代用できる
curl telnet://host.docker.internal:9003/
でtelnetがわりになる
今回はターミナルを2つ開いてncコマンドで疎通確認していきます。
疎通確認:Step1
待ち受け (mac)
macOSのターミナルで下記のコマンドを実行します。これでport9003に送られてきた文字列を確認できるようになります。
nc -l 9003
送信側 (Docker)
Dockerイメージで下記のコマンドを実行します。
入力待ちになっているので、何か文字を入力してEnterを押すことで、待ち受け側のmacOSへ文字列を送信できます。 動作を確認できたらCtrl-C
で終了します。
curl telnet://host.docker.internal:9003/
補足: Docker内にncコマンドがある場合はnc host.docker.internal 9003
でも送信できます
XDebugの設定ができているか、疎通確認する
XDebugはデバッグOnな状態でスクリプトを実行すると、ホスト側にアクセスしに来ます。ここでホスト側というのはPhpStormが実行されているmacOSのことです。
(phpのiniファイルで、xdebug.start_with_request=yes
にしておきます)
ここでは、コンテナ内で何らかのphpを実行して、ホスト側の9003へアクセスがくることを確認します。
待ち受け側 (macOS)
先ほどと同じく9003で待ち受けます。
nc -l 9003
送信側(Docker)
下記のコマンドでphpスクリプトを実行します。
php /var/www/html/index.php
待ち受け側でのデータ受信
すると、mac側に以下のような文字が出力されます。
<?xml version="1.0" encoding="iso-8859-1"?>
<init xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug"
fileuri="file:///var/www/html/index.php"
language="PHP"
xdebug:language_version="8.0.2"
protocol_version="1.0"
appid="69"
idekey="PHPSTORM">
<engine version="3.1.4"><![CDATA[Xdebug]]></engine>
<author><![CDATA[Derick Rethans]]></author>
<url><![CDATA[https://xdebug.org]]></url>
<copyright><![CDATA[Copyright (c) 2002-2022 by Derick Rethans]]></copyright>
</init>
受信テキストの意味
何が出力されているかは理解できなくて良いのですが、以下の情報がXDebug側から渡されることを知っていると後の検証ステップが理解しやすいです。
- fileuriで
/var/www/html/index.php
が実行されたことが通知されている - idekeyで"PHPSTORM"が渡されている
この時、phpのスクリプトは実行が終了しないはずです。
これはxdebug.start_with_request=yes
しているせいで、スクリプトの移動直後で自動でbreakpointが指定されたような状態になためです。 今回は疎通確認なので、ホスト側のnc
コマンドをCtrl-C
で終了させてしまい、phpスクリプトを実行させればよいです。
送信側(Chrome)
次は、macOSのChromeからhttp://localhost/
にアクセスしてみます。
この場合もアクセスされたタイミングで、待ち受け側の9003へ同じような文字列が出力されます。
PhpStormとXDebugの疎通
PhpStormで9003ポートを待ち受ける
まだPhpStormの設定は行なってませんが、この状態9003でので待ち受け自体は行えます。
nc
コマンドをCtrl-Cで終了した後、PphStopの電話アイコンをクリックして9003ポートを待ち受けます。(PhpStormの待ち受けポートのデフォルト値は9003です)
php /var/www/html/index.php
でスクリプトを実行すると、ステップ実行が始まります。
ですが、パスのマッピングを行なっていないので、ブレークポイントを指定してもスクリプトの実行を止めることができません。これは、先ほど見た
fileuri="file:///var/www/html/index.php"に相当するファイルが、macOS上でどのディレクトリにあるかを特定できないためです。
パスのマッピングを行う
ブレークポイントの指定を有効にするため、PHPStormでパスのマッピング設定を指定します。
この段階でのPphStormパスマッピングは、Preference > Servers
の1画面だけで指定できます。
Serverの設定
-
ショートカット
cmd+,
で、Preferenceを開きます。 -
Preference > Serversを選択します。
-
+
アイコンで追加します- 名前を
localhost
- この名前は重要で、cliから実行する時にこの文字列を
PHP_IDE_CONFIG
で指定します。
- この名前は重要で、cliから実行する時にこの文字列を
- ホストをlocalhostにします
- こちらもmacOSのchromeで入力するホスト名を指定します。
- ポートを設定します
- 今回は、docker-composerで8080経由でアクセス可能にしているので、8080を指定します。
- コンテナ上のポートではなく、macOSのchromeから指定するポート番号を指定します。
- 今回はhttpなので80にする。SSLを使って開発環境を作っている時は443にする。
- Use path mappingにチェックを入れます
- ローカル側のディレクトリと、Docker側のパスが一致するするようパスを指定します。
- 名前を
これだけでステップ実行が可能になります。
動作確認
ブレークポイントを設定して、再度php /var/www/html/index.php
でスクリプトを実行すると、ブレークポイントで処理が止まることが確認できます。
Chromeからhttp://localhost/
で実行した時も同様にブレークポイントを指定できます。
ここまでのおさらい(中間)
ここまでで、サーバのterminalやブラウザからのトリガーでPHPスクリプトを起動した場合は、ステップ実行できるようになりました。
よくある勘違いについて、ここで一度おさらいしておきます。
-
他で実行されたPHPスクリプトをXDebugでデバッグするだけ、Dockerの設定はしなくても良いです
- PHPStormのIDEから直接バッチのプログラムは、PHPUnitを実行させたい時などは追加で設定が必要です。
-
XDebugを使うだけなら、DebuggerでRemote Debugの設定はしなくて良いです
- 特定のIDE Keyによるフィルタが必要な時は設定が必要です。
- 共有の開発サーバで開発していた時代はこの設定が必要でしたが、個人のPC上で各自Dockerコンテナを立ち上げるような場合はIDE Keyのフィルタは必要ないでしょう。
(つづく...)
試したができません。
web:
build: ./docker/php
ここは
web:
build: ./php
こうではないですか?
神。ありがとうございます。こういう記事を求めていました。
当方ホストOSも仮想化ソフトもIDEもついでにXdebugのバージョンも違いますが
仕組みを理解できれば解決できるものですね。大変助かりました。