PHPの開発効率化！VSCodeでDockerコンテナに入って、Xdebugでデバッグする設定＆使用方法

# はじめに

PHP の定番のデバッグツールの Xdebug のおすすめの設定や使用方法をご紹介する記事です。
![xdebug.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/6739edf1-feb1-92c2-f8ce-789e23798016.gif)

コンソール画面で操作するブラックジャックゲーム開発の際に Xdebug を導入していたので、その環境をサンプルとして使用します。

https://qiita.com/ryamate/items/f77efb0888bb025e5f5b

その環境で Xdebug を起動させて、プログラムを実行したときにどんなことができるかを実際に見ていきます。

## Xdebug とは

**Xdebug** は、 **PHP** の **デバッグツール** です。

https://xdebug.org/

**デバッグツール** は、 **バグの原因を見つける作業を手伝ってくれるツール** のことです。


例えばこんなシーンがあるかと思います。

「実装うまくいかない、この行を処理しているときの変数の中身が知りたい」
「if文書いたけど、ちゃんと機能しとるんかあやしいので確認したい」
「既存のこの機能のここのあたり…どう動いているかよくわからない」

そんなときには、Xdebug を使用すると効率的に確認できます。

https://qiita.com/official-events/8e3542610897d988e66d

このイベントの「開発する上で知っておくべき知見」について考えたときに、真っ先に Xdebug が思い浮かびました。はじめて使ったときは感嘆の声が漏れ出ました…。

開発効率が劇的に良くなるツールだと思っています。

## 環境

環境は以下です。

- **macOS** Big Sur 11.6
- **Visual Studio Code**（VScode） 1.70.2
    - **Remote Development** 0.21.0（VSCode の拡張機能）
    - ****PHP Debug**** 1.29.0（VSCode の拡張機能）
- **Docker Desktop** 4.7.0
    - **Docker Engine** 20.10.14
- **PHP** 8.1.7（Dockerコンテナ内）
- **Xdebug** 3.1.5（Dockerコンテナ内）

※ 記事は、VScode、Docker を利用する前提の内容です

サンプルとして実際にローカル環境で動かしてみると、イメージしやすいかと思いますので、ぜひ試してみてください。

```bash
git clone https://github.com/ryamate/php-oop-cli-blackjack.git
```

https://github.com/ryamate/php-oop-cli-blackjack

## 目次
1. [Xdebug でできること](#1-xdebug-でできること)
1. [Xdebug を利用するおすすめの方法](#2-xdebugを利用するおすすめの方法)
1. [Xdebug の導入（Remote Development 利用）](#3-xdebugの導入remote-development-利用)
1. [Xdebug の準備](#4-xdebug-の準備)
1. [Xdebug の使用](#5-xdebug-の使用)

# 1. Xdebug でできること

以下、Xdebug でできることです。

- コードにブレークポイント（プログラムを止める行）を設定して、１行ずつプログラムを実行できます。
- ブレークポイントごとに変数の中身を出力して、確認することができます。
- エラーが発生するまでの経過を詳細に表示できます。

VScode で拡張機能 PHP Debug を導入して Xdebug を利用できます。（PHP Debug は、 VSCode で Xdebug を動かすための拡張機能であって、 Xdebug そのものではありません）

https://marketplace.visualstudio.com/items?itemName=xdebug.php-debug

# 2. Xdebug を利用するおすすめの方法

Xdebug を利用するおすすめの方法は、 Visual Studio Code の拡張機能 Remote Development を利用して、 Docker コンテナ内でデバッグ機能を設定する方法です。

https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/4bd1f889-8548-a02d-f9ab-f5c3d978b637.png)

（引用元：https://code.visualstudio.com/docs/devcontainers/containers）

# 3. Xdebug の導入（Remote Development 利用）

Visual Studio Code の拡張機能として導入した Remote Development を利用して、 Docker コンテナ内でデバッグ機能を設定する方法を見ていきます。

以下のリポジトリのファイルをサンプルに見ていきます。

https://github.com/ryamate/php-oop-cli-blackjack

## 3-1. Remote Development のインストール

VSCode の拡張機能 Remote Development をインストールします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/6a8fee29-540f-fc3d-919f-0f5f9977b52f.png)

## 3-2. Remote Development の設定（php-debug のインストール）

Docker コンテナ内でデバッグ機能の設定をしているファイルを確認します。

```json:.devcontainer/devcontainer.json
{
  "name": "Blackjack",
  "dockerComposeFile": [
    "../docker-compose.yml"
  ],
  "service": "app",
  "workspaceFolder": "/var/www/html",
  "forwardPorts": [
    9003
  ],
  "extensions": [
    "xdebug.php-debug",
    "bmewburn.vscode-intelephense-client",
    "oderwat.indent-rainbow",
    "streetsidesoftware.code-spell-checker"
  ]
}
```

以下、簡単な説明です。

```json:.devcontainer/devcontainer.json
{
  "name": "Blackjack",  ← 名前はなんでもOK
  "dockerComposeFile": [  ← docker-compose.ymlのパスを指定
    "../docker-compose.yml"
  ],
  "service": "app",  ← Dockerコンテナを指定
  "workspaceFolder": "/var/www/html",  ← Dockerの src/ 以下に合わせる
  "forwardPorts": [
    9003  ← Xdebugは9003番ポートがデフォルト
  ],
  "extensions": [  ← Remote Development で使う拡張機能
    "xdebug.php-debug",  ← Xdebug の拡張機能
    "bmewburn.vscode-intelephense-client",
    "oderwat.indent-rainbow",
    "streetsidesoftware.code-spell-checker"
  ]
}
```

VSCode の拡張機能（`xdebug.php-debug` など）を `extensions` に書いておくことで、Docker コンテナ内での開発時に指定した拡張機能が動作するようにしています。

## 3-3. Docker 関連ファイルの設定（Xdebug のインストール）

Docker 関連ファイルの設定を確認します。

```yaml:docker-compose.yml
version: "3"

services:
  app:
    build:
      context: .
      dockerfile: docker/app/Dockerfile
    volumes:
      - ./src:/var/www/html
    ports:
      - "50080:80"
```

```docker:docker/app/Dockerfile
FROM php:8-apache

WORKDIR /var/www/html

# PHP で必要なライブラリをインストール
RUN apt-get update \
    && apt-get install -y libonig-dev libzip-dev unzip \
    && docker-php-ext-install mbstring zip bcmath \
    && pecl install xdebug \
    && docker-php-ext-enable xdebug

# composer のインストール
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
ENV COMPOSER_ALLOW_SUPERUSER 1

# ファイルのコピー
COPY ./src /var/www/html
COPY ./docker/app/php.ini /usr/local/etc/php/php.ini
COPY ./docker/app/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini
```

以下、 Dockerfile についての簡単な説明です。

```docker
    && pecl install xdebug \
    && docker-php-ext-enable xdebug
```

 Xdebug をインストールして、有効にしています。

```docker
COPY ./docker/app/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini
```

PHP を実行した時に、 xdebug.ini が読み込まれて、設定が反映されます。

## 3-4. Xdebug の設定

Xdebug についての設定を確認します。

```ini:docker/app/xdebug.ini
xdebug.client_host = localhost
xdebug.start_with_request = yes
xdebug.mode = debug
xdebug.client_port = 9003
xdebug.log = /var/log/xdebug.log
```

以下、簡単な説明です。

```ini:docker/app/xdebug.ini
# 呼び出し元のIPアドレス（同じDockerコンテナ内なので、localhost）
xdebug.client_host = localhost

# PHPリクエスト開始時に、Xdebugを起動させるか
xdebug.start_with_request = yes

# debugだと、ステップ実行ができる
xdebug.mode = debug

# デフォルトで9003で書かなくても動くけど、明示しておく
xdebug.client_port = 9003

# ログファイルの場所を指定
xdebug.log = /var/log/xdebug.log
```

## 3-5. VSCode デバッグ機能の設定

VSCode でデバッグを実行するための設定ファイルを確認します。

```json:src/.vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "php",
      "request": "launch",
      "name": "Listen for Xdebug",
      "port": 9003,
    }
  ]
}
```

以下、簡単な説明です。

```json:src/.vscode/launch.json
{
  "version": "0.2.0",  ← バージョン指定
  "configurations": [  ← 設定内容
    {
      "type": "php",
      "request": "launch",
      "name": "Listen for Xdebug",  ← わかりやすければ、なんでもOK
      "port": 9003,  ← Xdebug のポート番号
    }
  ]
}
```

公式ドキュメントで詳しくは確認できます。

https://code.visualstudio.com/docs/editor/debugging#_launchjson-attributes

# 4. Xdebug の準備

使用する準備について、見ていきます。

- Docker の起動
- VSCode の起動
- Remote Development の起動
- Xdebug の起動

## 4-1. Docker の起動

Docker Desktop を起動しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/1ed67f4c-4f22-ba0c-5bf2-178cb5d41f19.png)


## 4-2. VSCode の起動

VSCode を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/3ebe8f72-a7fa-7179-af5e-beb6b5018574.png)

上記では以下のリポジトリのファイルを開いています。

https://github.com/ryamate/php-oop-cli-blackjack

## 4-3. Remote Development の起動

VScode 画面左下「><」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/1ddd232e-f11a-1929-eb1f-87dd59f9de55.png)

開くと以下の選択肢が出ますので、「Reopen in Container」を押して、Docker コンテナを開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/77944263-9c3e-bcee-17b2-edc316fa4b10.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/5c975430-f5e6-8e04-c676-05d15a4cd3f3.png)

Docker の app コンテナ内が表示されている状態になりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/a8f98cc6-9654-2b09-4f55-da0f2cd5b754.png)

**sorce/** ディレクトリ 以下が表示されています。

## 4-4. Xdebug の起動

サイドバー（画面左）の「実行とデバッグ」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/28621430-48ec-641d-8214-2673a1e5bfaf.png)

再生ボタン ▷ をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/83836290-dec5-7ff3-e9e3-be004a8b9ae9.png)

以下のバーが出ていたら、 Xdebug の準備OKです！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/09ae9130-9f74-be5a-21f5-2643653cac1d.png)

# 5. Xdebug の使用

Xdebug を使用する方法です。
コンソール画面で操作するブラックジャックゲームをサンプルとしていますので、以下の実装がうまくできているかを確認したいとします。

- トランプのカード52種類を生成して、シャッフルした状態のデッキを作る

## 5-1. ブレークポイントの設置

コードにブレークポイント（プログラムを止める行）を設定します。

- lib/Deck.php の 41 行目にブレークポイントをセットします。（41の少し左にカーソルを合わせると押せます）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/553cb506-5a4b-50e9-54f3-c456cc071714.png)

```php:lib/Deck.php
/**
 * デッキを初期化する
 *
 * @return array<int,array<string,int|string>> $deck デッキ
 */
public function initDeck(): array
{
    $card = new Card();
    $this->deck = $card->createNewDeck(); // トランプのカード52種類を生成して、
    shuffle($this->deck); // シャッフルした状態のデッキを作る
    return $this->deck;
}
```

## 5-2. プログラムの実行（ブレークポイントでの停止）

VSCodeのターミナルで、プログラムを実行します。

```bash:ターミナル
php Main.php
```

:::note
VSCodeのターミナルからファイルのプログラムを実行する他には、ブラウザで localhost でアプリを操作して、ブレークポイントを設置したところで処理を止めることもできます。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/d977451d-b072-9fa1-e3f7-aa50deefc958.png)

```bash
root@44aeb3938a96:/var/www/html# php Main.php 
ブラックジャックの設定をします。
プレイヤーの人数を入力してください。（1〜3）
🙋‍ 1
プレイヤー1名でゲームを開始します。

あなたの持っているチップは100ドルです。
ベットする額を入力してください。（1〜1000ドル）
💲 100
100ドルをベットしました。

ブラックジャックを開始します。
```

ブレークポイントの行が実行されるところまで来ると、このように処理を止めてくれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/38fdde87-87f0-364a-191c-4c3daed925a6.png)

画面左に「変数」とあり、ローカル変数、スーパーグローバル変数などの一覧を見ることができます。

「コールスタック」には、処理の流れが表示されています。

## 5-3. ステップ実行

こちらのバーでステップ実行の操作をします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/54bf1b69-8630-fffe-5cd9-7e3af57b5fd9.png)

左から順に以下のボタンです。

1. 続行
2. ステップオーバー
3. ステップイン
4. ステップアウト
5. 再起動
6. 停止

例えば「続行」を押すと次のブレークポイントまで進みます。

### 41→42行目

今回は１行ずつ見ていきたいので、「ステップオーバー」を押します。押すと、41行目から42行目へ進みます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/b47eda59-1ff4-fc85-005c-f38883f33be3.png)

:::note
「ステップオーバー」は、 関数内の処理のデバッグをスキップして次の行に進みます。（「ステップイン」の場合、関数内の処理に進みます）
:::

Card クラスが new されて、変数 `$card` に、代入されました。

### 42→43行目

「ステップオーバー」を押します。（42行目から43行目へ進みます）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/faafe6a8-303f-46d5-cb3e-94d71f026a13.png)

`$card` の `createNewDeck` メソッドが実行されて、 `$this->deck` に、カードが 52 枚入っている形の配列になっていることが確認できました。

### 43→44行目

「ステップオーバー」を押します。（43行目から44行目へ進みます）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/457acdd4-191c-5a8b-eb0e-7b9204410bba.png)

シャッフルされて、`$this->deck` のカードの順番が変わっていることが確認できました。

# おわりに

PHPの定番のデバッグツールの Xdebug のおすすめの設定や使用方法をご紹介する記事でした。

ありがとうございました。

## 参考教材

学習初期に Xdebug を教えてもらった教材はこちらです。

https://dokugaku-engineer.com/