Xdebug, DBGPで簡易デバッガを作ってみました。

この記事はPHP Advent Calendar 2018の23日目の記事です。

Xdebug, DBGPの仕組みについて

N番煎じくらいだと思いますが、改めて説明しますと、Xdebugが有効になったPHPを実行するとPHP(以下、クライアント)がDBGPプロトコルでリモートサーバに接続します。リモートサーバはクライアントに対してコマンドを送り、クライアントがコマンドに対応する処理を実行し、レスポンスを返すことで対話的にデバッグできるようになっています。

実装について

DBGPはTCPなので、まずはTCPサーバを立ち上げます。

ソケットI/Oな処理の部分ですがDBGPはざっくり以下のような仕様になっています

  • リモートサーバ側からクライアントに対してコマンドを送信する
    • コマンドは {コマンド名} -i {トランザクションID} {その他オプション引数}の形式で送信
    • トランザクションIDはリクエスト/レスポンスの対を特定するための識別子(コマンド打つたびにインクリメントして渡せばOKっぽい)
  • 接続元からサーバ側へのレスポンスは bodyの長さ + ヌルバイト + bodyという形式で返され、bodyはXMLで返される
  • コマンドもレスポンスもヌルバイト(\0)が終端

ステップ実行はstep_overコマンドで実行します。

コマンドを送信したらレスポンスを取得します

メッセージ取得は以下のように定義しています。終端がヌルバイトになるまでメッセージを読み取ります。

読み取ったあとはレスポンスに応じたハンドリングを行います。

レスポンスはXMLなのでよしなにパースするのですが、名前空間付きの属性やタグがあるのでsimplexml_load_xxxだとうまくロードできません。XMLが大きくないので、DOMDocument::loadXMLでメモリに読み出す方法が楽だと思います。

ステップ実行をした場合はこんな感じで現在のファイル名と行番号が返ってくるのでそれに応じてファイルの中身を表示します(実際にはXMLの前にはXMLのサイズ(数値)+ヌルバイトが入ります)

evalの場合は入力文字列をbase64エンコードして送る必要があります。

実行結果が返ってきますが、stringの場合はbase64エンコードされているのでよしなにデコードしつつ表示します。

オブジェクトの場合はpropertyがネストされて返ってくるのでよしなにパースしてよしなに表示すればOKです(SimpleDebuggerではクラス名しか表示していない手抜き実装)

ということで簡単にデバッガを実装できました〜

参考URL