有馬総一郎のブログ

(彼氏の事情)

2026年02月07日 12:15:13 JST - 6 minute read - Linux

PHP初心者がVSCodeを使ってDockerを使ってデバッグ環境を構築する その3 (デバッグ編)

Dev Containersファイル作成後から。最後に、デバッグ構成ファイルを作成する。

launch.json

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Listen for Xdebug",
            "type": "php",
            "request": "launch",
            "port": 9003,
            "pathMappings": {
                "/var/www/html": "${workspaceFolder}"
            }
        }
    ]
}

以上でデバッグ環境が構築されている。

"pathMappings"については、Apache(サーバー)のドキュメントルートとプロジェクトのフォルダ構造をVSCodeに教えるための項目。デフォルトが"pathMappings": { "/var/www/html": "${workspaceFolder}" }なので、書く必要はないが理解しやすいように残している。

/var/www/html/virtual/test/public_html/testphp.coresv.comというディレクトリ構成ゆえに、変更が必要かと思いきや、ルートのphpも/var/www/html/virtual/test/public_html/testphp.coresv.com/index.phpにあるので、変更の必要はない。

分かればシンプルな話なのだけど、docker-compose.ymlの以下の項目。

services:
  app:
    volumes:
      # 親フォルダ(..)をコンテナ内の /var/www/html に同期する
      - ../:/var/www/html:cached

(.devcontainer/内の)docker-compose.ymlvolumes設定で、プロジェクトの親フォルダ(ワークスペース直下)をコンテナの/var/www/htmlに丸ごとマウントしている。そのため、コンテナ内のディレクトリ構造と、VSCode上のフォルダ構造が完全に一致していて、デフォルトのpathMappingsだけでパス解決はできている。

root@ffee28f3ebc9:/var/www/html# ls  .devcontainer
Dockerfile  devcontainer.json  docker-compose.yml  post-start.sh

次にDockerfileは、以下のとおりになっている。

ENV APACHE_DOCUMENT_ROOT /var/www/html/virtual/test/public_html/testphp.coresv.com
RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf
RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf

なので、ブラウザでURL http://testphp.coresv.com にアクセスすると、コンテナの/var/www/html/virtual/test/public_html/testphp.coresv.com/index.phpを見に行く。VSCodeでも${workspaceFolder}/virtual/test/public_html/testphp.coresv.com/index.phpと一致するので、問題なくデバッグできる。

もし、Apacheを使わない場合、php -S1を使ったビルトインの簡易webサーバを起動して、デバッグを行うするときは、調整が必要かも知れない。

以下、デバッグできなかった場合の探り方を書き残しておく。

デバッグできない!!!

最初、デバッグできなくて困った。

結論から言ってしまうと…渡されたソースが、構築した環境では致命的エラーとなるため、デバッグする前に処理が終了していた。

詳しく説明すると親クラスがいないextendsしていないのにparent::execute($sql, $bindArr);をしていて、当然親クラスがいないので、エラーとなる。

しかし、私はphpはインタプリタ型言語だから、エラー箇所を通らなければエラーは起きないと思っていた。また、現行機が問題なく動いているのだから、このソースで動くはずだ、と思い込んでいた。

しかし、PHP7.0以降、includeしたタイミングでチェックして、エラーを出すようになったらしい。

何故、デバッグできないかを探求する

まずは、止めたい場所にxdebug_break();を埋めこんでみる。でも止まらない。

Xdebugオプションとして-dxdebug.log=/tmp/xdebug.logを追加する。デバッグログファイルがない場合は、Xdebugが起動できてない。

ログの中身を見ると、Xdebug: [Step Debug] Could not connect to debugging client. Tried: localhost:9000 (through xdebug.client_host/xdebug.client_port).となっている。

接続できていない。ipやポートなどのズレ。-dxdebug.client_host=127.0.0.1-dxdebug.client_port=9003などのオプションを追加する。そして、launch.jsonの"port": 9003,と合わせる。この状態だとデバッグログを見ずともコンソールに出力されている。

次に、以下の内容がデバッグログに出力された。

[1354] [Step Debug] -> <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" command="run" transaction_id="13" status="stopping" reason="ok"></response>
[1354] [Step Debug] <- stop -i 14
[1354] [Step Debug] -> <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" command="stop" transaction_id="14" status="stopped" reason="ok"></response>
[1354] Log closed at 2026-02-01 09:13:58.167478

stoppingとあるので、ブレークポイントが無視されて、最後までプログラムが実行されている。file://を出力している箇所を探す。

<notify xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" name="error"><xdebug:message filename="file:///var/www/html/virtual/test/public_html/testphp.coresv.com/noParents.php" lineno="165" type="Fatal error"><![CDATA[Cannot use "parent" when current class scope has no parent]]></xdebug:message></notify>

エラーとなっている行、Cannot use "parent" when current class scope has no parentという原因が分かる。

ああ、勘違い

動かせば、普通にエラーは出るので、知ってはいた。 繰り返しになるが、インタプリタ型言語だから、そこが実行されなればいいはずと勘違いしていた。

「現行機(古いPHP)では動いているのだから、コードに致命的な間違いはないはず」
「現行機でエラーが起きないのは、そもそもその処理を通っていないからだ」
「それなのに、デバッグ機(新しいPHP)ではエラーが出力される。ということは、なぜかそこを通ってしまっている」
「どうして通ってしまうのか分からない。よし、デバッグして実行ルートを追いかけよう」
「……あれ? デバッグできない(ブレークポイントで止まらない)」

しかし実際は 、通る・通らない以前に、PHP8.4がファイルを読み込んだ瞬間に構文チェックで致命的エラーを出して死んでいた。そのため、Xdebugがブレークポイントで止まることなく、プロセスが終了していた。

ソースを修正しないでデバッグできない原因を探ろうとして、あれこれ設定ファイルをいじったり、ファイヤウォールを開けたり迷走を繰り返した。しかし、こうやって、デバッグできない原因として出ているので、これはソースの修正がいると確信した。

ちなみにpathMappingsがおかしいと、[4962] [Step Debug] WARN: Breakpoint file name does not exist: /var/www/html/virtual/test/public_html/testphp.coresv.com/virtual/test/public_html/testphp.coresv.com/index.php (No such file or directory).と存在しないパスが出力されるので、ズレていると分かる。

ちゃんと理解しないでgeminiに質問すると、前提条件をちゃんと伝えてないから、その場しのぎの回答をしてきて、不要な修正をしてしまったりする。とはいえ、ググることなく、ここまで辿りつけたのもgeminiのおかげだ。

それでは、よりよい開発環境を!


  1. php8.4イメージのときはphp -dxdebug.mode=debug -dxdebug.start_with_request=yes -dxdebug.client_host=127.0.0.1 -dxdebug.client_port=9003 -S 0.0.0.0:8000 -t ${workspaceFolder}/virtual/test/public_html/testphp.coresv.comでデバッグしてた。なので"pathMappings"は同じく設定不要だった。 ↩︎