LAMP環境のサーバサイドAPIをWindowsでデバッグする 実用編 トライアンドエラー

# 前回
[入門編](https://qiita.com/mintcandy/items/906e0b0ae4c9d54d3419)では、
LAMP環境をWindowsでデバッグするHello Worldについて書きました。

今回は実業務で使えるかもしれないテクニックを紹介していきます。

# APIのデバッグ
前回の内容は、コードに沿って順次コードを実行するだけのものでした。
しかし、APIの場合はどうでしょうか。

GETやPOSTでパラメータを指定したり、各種フレームワークを使っていたりするかと思います。
そういった環境でデバッグするにはどうしたらいいのか考えてみます。

# GET,POSTパラメータの指定

## GETの場合

以下のようにGETパラメータでメソッドを指定して処理を行うAPI的なものを想定します。

```php:api.php
<?php
    $method = filter_input(INPUT_GET, "method");
    if ($method)
    {
        switch ($method)
        {
            case "show":
                // 処理
                break;
            case "insert":
                // 処理
                break;
            default:
                echo "error";
        }
    }
    else {
        echo "error.";
    }
```

GETの場合はURLに直接指定できるので、プロジェクトの実行構成を変えてしまってもいいでしょう。
NetBeanでは以下のように設定できます。

![api2.png](https://qiita-image-store.s3.amazonaws.com/0/179875/8ece92d3-d3a8-4e65-ead9-16ebf0ed5ed3.png)

任意のパラメータでデバッグできます。
![api1.png](https://qiita-image-store.s3.amazonaws.com/0/179875/2cc07894-343c-8143-d764-93fead4f645f.png)

## POSTの場合

では、POSTではどうでしょうか。
GETのような指定の仕方はできません。

```php:api.php
<?php
    $method = filter_input(INPUT_POST, "method");
    if ($method)
    {
        switch ($method)
        {
            case "show":
                // 処理
                break;
            case "insert":
                // 処理
                break;
            default:
                echo "error";
        }
    }
    else {
        echo "error.";
    }
```

個人的には、デバッグと割り切ってスーパーグローバル変数に
値を無理やり入れてしまうのがやりやすいかなと思います。

```php:api.php
<?php
    // デバッグ用
    $_POST["method"] = "show";

    $method = filter_input(INPUT_POST, "method");
    if ($method)
    {
        switch ($method)
        {
            case "show":
                // 処理
                break;
            case "insert":
                // 処理
                break;
            default:
                echo "error";
        }
    }
    else {
        echo "error.";
    }
```

実はこれでは、うまくいきませんでした。
filter_inputで取得している値と$_POSTスーパーグローバル変数に入っている値はイコールでは
ないようです。

$_POSTをいじらなければもちろんイコールだと思いますが、filter_inputはスーパーグローバル変数の値を
とっているわけではないということのようです。

$_POSTをいじってしまっても安全にクライアントから渡されたPOSTパラメータを取得できる、
という意味ではより安全な実装になっているようです。

以下のような実装になっている場合は通用します。

```php:api.php
<?php
    // デバッグ用
    $_POST["method"] = "show";

    $method = ;
    if (isset($_POST["method"]))
    {
        $method = $_POST["method"]
        switch ($method)
        {
            case "show":
                // 処理
                break;
            case "insert":
                // 処理
                break;
            default:
                echo "error";
        }
    }
    else {
        echo "error.";
    }
```

が、スーパーグローバル変数への直接のアクセスは推奨されていないようなので、
やはりfilter_inputなどを使った実装のほうがベターでしょう。

少々不格好ですが、以下のようにPOSTパラメータ取得後に値を書き換えてやっても
いいかもしれません。

```php:api.php
<?php
    //$method = filter_input(INPUT_POST, "method");
    //if ($method)
    if (true)
    {
       // デバッグ用
        $method = "show";

        switch ($method)
        {
            case "show":
                // 処理
                break;
            case "insert":
                // 処理
                break;
            default:
                echo "error";
        }
    }
    else {
        echo "error.";
    }
```

# フレームワークを使っている場合

フレームワークはライブラリとは呼ぶ側、呼ばれる側が逆です。

ライブラリの場合、

クライアントアプリ　→　サーバAPI →　ライブラリ

というコールのされ方をしますが、フレームワークの場合流れが逆転します。

クライアントアプリ　→　フレームワーク　→　サーバAPI

となります。
Zend Frameworkのようにフレームワークとしてもライブラリとしても使えるものも存在しますが、
MVCモデルに沿ったAPIのように、フレームワークから呼ばれる想定で作られているコードのデバッグは、
注意が必要です。

対象のAPIが記述されているphpファイルを直接実行しても、
フレームワーク経由で実行された場合と同じ結果は得られないからです。

フレームワーク内で各種初期処理などが行われてから、
クライアントプログラムに処理が流れてくるのです。

これは、実際にクライアントからコールしているURLと同一のURLをエントリポイントとして
デバッガを動かしてみるとわかります。

最初の方はフレームワーク内のコードが実行されているはずです。

たとえばFuelPHPでは、phpファイルのパスとコントローラ(API)の実行パスが全く異なります。
http://fuelphp.jp/docs/1.7/general/controllers/base.html

これは、フレームワーク側でURLを解析し、クラス名やメソッド名に変換してコールしているからです。

ちなみにFuelPHPでは$_POSTスーパーグローバル変数の書き換えが有効だったので、
$_POSTの値をフレームワークで参照しているものと思われます。

```php:fuel/app/classes/controller/api.php
class Controller_Example extends Controller
{
    public function action_index()
    {
        //デバッグ用
        $_POST["method"] = "show";

        $method = Input::post('method');
    }
}
```

Input::postはFuelPHPの機能ですが、$_POSTスーパーグローバル変数を参照すると
ドキュメントに記載がありますね。
http://fuelphp.jp/docs/1.7/classes/input.html

# Memcachedを使っている場合
MemcachedはWebサーバ上のキャッシュシステムです。
https://ja.wikipedia.org/wiki/Memcached
サーバのスケールアウトを考えた場合にDBサーバはデータの同期の問題等もあり
少々ハードルが高いため、Memcachedを使ってWebサーバ上にキャッシュを溜め込み、
Webサーバをスケールアウトすることで負荷分散を容易に行うことができます。

サイズが小さくあまり変化がないマスタデータなどをキャッシュするケースが
多いようです。

このMemcachedですが、Linux版はパッケージを入れるだけで簡単に動くのですが、
Windows版がなかなか動きませんでした（というか、諦めました）。

API上でこのMemcachedを使っている場合、Memcachedが正常に可動していないと
その部分がエラーになってしまいデバッグができません。

こちらに対する対応策としては

**「Memcachedを使っているところをデバッグ環境ではコメントアウトする」**

です。
身も蓋もありませんが、要はデバッグできればいいのです。

Memcachedはその名の通りキャッシュなので、キャッシュにあればそれを読み込み、
なければ新規でデータを取得する、という使われ方をします。
ですので、該当箇所をコメントアウトしてもAPIでキャッシュを使わなくなるだけで
結果には影響がないはずです。

むしろ、これで結果が変わってしまうようであればMemcachedの使い方に問題が
ある可能性があります。

また、これによりたくさんの箇所をコメントアウトしなければならない、
というケースもまた大変です。
MemcashedへのIN/OUT部分を一箇所に集約するような設計上の改善をしたほうが
いいかもしれません。

# Postmanを使う

Postmanは、APIの動作確認を行うのに大変便利なソフトウェアです。
https://www.getpostman.com/

任意のURLに任意のGET,POSTパラメータをつけて送ることができます。
さらに結果がJSONであれば整形して確認することもできます。

以下のようにデータをjsonで返すAPIであれば、

```api.php
<?php
    $method = filter_input(INPUT_POST, "method", FILTER_SANITIZE_SPECIAL_CHARS);
    if ($method)
    {
        switch ($method)
        {
            case "show":
                $retval = [
                    "error_code" => 0,
                    "message" => "success",
                    "result" => [
                        [
                            "id" => 1,
                            "value" => "hoge"
                        ],
                        [
                            "id" => 2,
                            "value" => "fuga"
                        ]
                    ]
                ];
                echo json_encode($retval);                
                break;
            case "insert":
                // 処理
                break;
            default:
                echo "error";
        }
    }
    else {
        echo "error.";
    }
```

このようにパラメータを渡し、結果を見やすい形で取得できます。

![api3.png](https://qiita-image-store.s3.amazonaws.com/0/179875/6360858f-da91-b659-8cd5-ffff0bbc580a.png)

APIのIN/OUTだけを確認したいならPostmanで、コードの流れまで追いたいならデバッガで、
と使い分けることで効率的に開発を行うことができます。

# さらにその先へ シームレスなデバッグ（調査中）

もし、このPostmanとXdebugを組み合わせて使うことができたら。

たとえば、Postmanで任意のパラメータを入力して任意のAPIにアクセス、
それをNetBeansブレイクしてデバッグ、というようなことです。

これができれば、コードの中に無理やりPOSTパラメータを仕込む必要もなくなります。

さらにPostmanだけではなく、たとえば実際のクライアントアプリであったり、Unity Editor等からの
API呼び出しに対応できれば、クライアントとサーバのデバッグをシームレスに行えます。

残念ながら私の環境ではこれがまだ上手くいっていません。
Eclipseを使っている方で上手くいっているケースがあるようですが、
IDEの違いは関係ないのではないかと予測しています。

キーワードは、「リモートデバッグ」です。
おそらく、IDEがサーバのように待機して、HTTPのコールを待ち受けている、という構造に
なっているのではないかと思います。

php.iniで設定している以下のようなパラメータが怪しいと思っています。

xdebug.remote_enable=1
xdebug.remote_port=9001
xdebug.idekey=netbeans-xdebug

9001番ポートでNetBeansがデバッグサーバとして待ち受ける設定、なのではないでしょうか。

クライアントソフトがサーバになって待ち受けをする、となると陥りがちな罠が
ファイアウォール等によってポートが閉じているケースです。

そのあたりを調べていくともしかしたら解決するかもしれません。

# さいごに
今回は実際に開発現場で行っているデバッグのための創意工夫をつらつらと書きました。
環境の違いをいかに吸収して効率的なデバッグをするか、ということについて、
千差万別のケースに合わせて工夫していくことが、
特にサーバサイドAPIの開発においては必要になるかと思います。