Xdebug入門 カンタンな使い方

こんばんは。滑り込みですが、
この記事は<a href="http://qiita.com/advent-calendar/2017/code_igniter" target="_blank">CodeIgniter Advent Calendar 2017</a>の19日目です。
この記事単体でも読めますが、[9日目](https://qiita.com/ayato/items/25c5d06a3ba8ce78520a)と[11日目](https://qiita.com/ayato/items/4a620928160db02f302c)の続きとしても読める様に書いていきます。

CodeIgniterは基本的なPHPの構文とClassの概念が分かっていれば使い始める事が出来るフレームワークなので、学習コストはかなり低い方だと思います。

初心者でも理解しやすい・触りやすいフレームワークと言えますが、もし初心者の頃から変わらず `var_dump();` しまくるプリントデバッグをしているなら、デバッガを使ってみるとより快適な開発が出来ます。

この記事ではDockerを使ってXdebugが動くPHP開発環境を作り、そこでCodeIgniterを動かす事でXdebugの使い方を紹介します。

## Dockerで開発環境を作る

docker-composeを使います。開発マシンはMacでDocker for Macを使っている想定です。

### docker-composeの設定

`docker-compose.yml` は以下の通り。

```yaml
version: '2'

services:
  dev-with-xdebug:
    container_name: "codeigniter-xdebug"
    image: webdevops/php-apache-dev:ubuntu-16.04
    working_dir: /app
    volumes:
      - ./:/app
      - ./custom.ini:/opt/docker/etc/php/php.ini
    ports:
      - "80:80"
```

Dockerイメージには、既にXdebugをインストールしたPHPが入っている `webdevops/php-apache-dev` を使っています。
イメージの中身が気になる方は [GitHub](https://github.com/webdevops/Dockerfile/tree/master/docker/php-apache-dev)や[Docker Hub](https://hub.docker.com/r/webdevops/php-apache-dev/)を見てみて下さい。

このイメージではドキュメントルートが `/app` なので、 `docker-compose.yml` を置く場所（`./`）をコンテナ内の `/app` と共有しています。

また、PHPの設定でデフォルトから変える物は `custom.ini` に書き出して、Dockerコンテナ内に渡します。
`webdevops/php-apache-dev:ubuntu-16.04` のDockerイメージの場合は、PHPが `/opt/docker/etc/php/php.ini` という空のINIファイルを読んでいるので、ホスト側の `custom.ini` をそこへマウントしています。

### Xdebugの設定（.ini）

`custom.ini` にXdebugの設定を記述していきます。内容は以下の通り。

```ini
xdebug.remote_connect_back = 0
xdebug.remote_host = docker.for.mac.localhost
xdebug.remote_port = 9000
xdebug.remote_handler = "dbgp"
xdebug.remote_autostart = 1
xdebug.remote_log = /app/application/logs/xdebug.log
xdebug.idekey = "docker"
```

MacでDocker for Macを使っているという想定で `xdebug.remote_host = docker.for.mac.localhost` の値を指定していますが、他の環境の場合は適宜変更して下さい。
Dockerコンテナ内から見たホスト側のIPアドレスやホスト名が入るかと思います。

また、ドキュメントルートが `/app` なので、そこに置いたCodeIgniterの `application/logs/` の中にXdebugのログを書く様に指定しています。

そしてXdebugが9000番ポートを使う様に指定していますが、これは `webdevops/php-apache-dev` のDockerイメージの方で穴が開いているので、 `docker-compose.yml` の方でEXPOSEの設定を書かなくても大丈夫です。


### コンテナ起動

この `docker-compose.yml` と `custom.ini` を使ってローカル開発環境を建てます。

```
$ cd ~/Desktop
$ wget https://github.com/bcit-ci/CodeIgniter/archive/3.1.6.zip
$ unzip 3.1.6.zip
$ cd CodeIgniter-3.1.6
$ vim docker-compose.yml
$ vim custom.ini
$ docker-compose up -d
```

コンテナが起動したか確認

```
$ docker ps
CONTAINER ID        IMAGE                                   COMMAND                  CREATED             STATUS              PORTS                                   NAMES
505b74f892b9        webdevops/php-apache-dev:ubuntu-16.04   "/entrypoint super..."   39 minutes ago      Up 39 minutes       443/tcp, 0.0.0.0:80->80/tcp, 9000/tcp   codeigniter-xdebug

$ docker-compose ps
       Name                  Command           State                   Ports
--------------------------------------------------------------------------------------------
codeigniter-xdebug   /entrypoint supervisord   Up      443/tcp, 0.0.0.0:80->80/tcp, 9000/tcp

```

http://localhost （ http://0.0.0.0:80 ）にアクセスすれば、見慣れたCodeIgniterのウェルカムページが出ているハズです。
9000番ポートも穴が空いていますね。
今回はHTTPSで通信しないので、443番は使いません。

## エディタの設定

ここまでの設定で、コンテナ内でPHPが実行されるとXdebugが走り、コンテナから見た `docker.for.mac.localhost:9000` へ向けて通信をしようとします。
その様子は `application/logs/xdebug.log` に書き込まれているハズです。

なので、エディタ側でその通信を受け取ればOKです。
ここからは個々のエディタの設定に依るので省きますが、肝心なのは

- エディタ側から見たら `localhost:9000` (`0.0.0.0:9000`) から通信が飛んでくる
- idekeyは `docker`
- パスのマッピングは `/app` ⇔ `~/Desktop/CodeIgniter-3.1.6`

辺りでしょうか。
僕の場合はVimで `joonty/vdebug` プラグインを使っているので以下のような設定値になります。

```vim
let g:vdebug_options = {
\    "port" : 9000,
\    "debug_file_level" : 2,
\    "debug_file" : '~/.vim/vdebug.log',
\    "ide_key" : 'docker',
\    "path_maps" : {
\      '/app': '/Users/ayato/Desktop/CodeIgniter-3.1.6',
\    },
\}

```

## Xdebugを使う

手順としては

1. 解析したい場所のコードにBreakPointを打つ
2. エディタでXdebugからの通信を待ち受ける
3. PHPを実行させる（ページの再読込みする等）
4. BreakPointを打った所でPHPが中断するので、そこで変数の中身を調べたり、その時点での関数の実行結果を確認したりする

といった感じです。
都度 `var_dump();` を書くよりも素早く色んな情報を引き出せるので開発スピードが上がるでしょう。

2つ例を出します。

### 例: application/views/welcome_message.php

<img width="1179" alt="スクリーンショット 2017-12-19 22.41.58.png" src="https://qiita-image-store.s3.amazonaws.com/0/25524/91fbb076-6dcf-c1b2-057a-b5e025d68e06.png">

こんな感じで85行目にBreakPointを打ちました。この状態で
エディタの方でXdebugからの通信を待ち受けつつ、 http://localhost を開けば解析が始まるハズです。

BreakPointを打った85行目では定数 `ENVIRONMENT` によって振る舞いが変わっているので、予想外の振る舞いをした時はこの中身をチェックすると良いでしょう。
慣れるとわざわざ `var_dump();` するより圧倒的に楽です。

### 例: application/controllers/Welcome.php

<img width="391" alt="スクリーンショット 2017-12-19 22.57.14.png" src="https://qiita-image-store.s3.amazonaws.com/0/25524/141b84ec-293b-30f0-0ad5-8bbc20ae72c0.png">

23行目にBreakPointを打ちました。
ここまで解析を進めた状態でStep inすると、このview関数の中に入って確認する事が出来ます。

<img width="723" alt="スクリーンショット 2017-12-19 23.00.29.png" src="https://qiita-image-store.s3.amazonaws.com/0/25524/67e1531d-c3fc-1022-d91e-defab87809f5.png">

`application/controllers/Welcome.php` の `$this->load->view()` は `system/core/Loader.php` で定義される関数 `view()` なので、Step inするとそこまでジャンプしていきます。

Step in / Step out / Step over を駆使すれば、おかしな挙動をしている部分を速く見つける事ができるかもしれません。

### おわりに

いかがでしょうか？
デバッガは最初は動作の仕組みを勉強する所から初めないといけませんが、慣れればプリントデバッグよりも遥かに速くコードの挙動を調べる事が出来ます。

また、CodeIgniterはコアの部分でも複雑な事はほとんどしていないので、デバッガがあればその挙動を追いかけて勉強する良い教材になると思います。

この記事がデバッガ初心者の方の助けになれば幸いです。