PHP+PHPStorm+Docker+XDebugの仕組みを完全に理解した

カテゴリ: 未分類 | タグ: , , ,

はじめに

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で指定します。
    • ホストを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のフィルタは必要ないでしょう。

(つづく...)


Amazonでおトクに買い物する方法
AmazonチャージでポイントGET


Amazonは買いもの前にAmazonギフト券をチャージしてポイントをゲットしないと損!

こちらもおススメ

2 thoughts on “PHP+PHPStorm+Docker+XDebugの仕組みを完全に理解した

  1. 試したができません。

    web:
    build: ./docker/php

    ここは

    web:
    build: ./php

    こうではないですか?

  2. 神。ありがとうございます。こういう記事を求めていました。
    当方ホストOSも仮想化ソフトもIDEもついでにXdebugのバージョンも違いますが
    仕組みを理解できれば解決できるものですね。大変助かりました。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です