Baldanders.info

PHP は制御に向かない?

PHP でどハマりしてしまったので, 覚え書きとして残しておく。

以前「PHP を CGI モードで動かす」というのをやったのだが, そもそもこれをやりたかったのはマルチプロセスで非同期制御をやりたかったからだ。 しかし実際にやってみるとどうもうまくいかない。 最初はこんな感じで普通に pcntl_fork を使っていたのだが,

$pid = pcntl_fork();
if ($pid == -1) { //fork 失敗
    return -1;
}
else if ($pid) { //親プロセス
    return 0;
}
else {
    // 子プロセス
    //非同期制御の呼び出し
    asynchronousControl();
    exit(0);
}

Web 上でこれを動かすと, どうも子プロセスが終わるまでサーバサイドから制御が返ってこない感じである。 んで, いろいろ調べたら, 子プロセスでは標準出力を閉じないとだめらしい。 確かに Perl では close( STDOUT ); とかやるので, 同じ類の話かと思い以下のように直してみた。

$pid = pcntl_fork();
if ($pid == -1) { //fork 失敗
    return -1;
}
else if ($pid) { //親プロセス
    return 0;
}
else {
    // 子プロセス
    //標準出力をクローズ
    fclose(STDIN);
    fclose(STDOUT);
    fclose(STDERR);
    //非同期制御の呼び出し
    asynchronousControl();
    exit(0);
}

が, 結果は同じ。 更にネットを漁ってみると既にバグとして上がってるようである。

このバグ報告が2005年の話でステータスはいまだに「No Feedback」である。 で, こちらで条件を変えていろいろ試してみたが, どうしてもうまくいかない。 要するに子プロセスを非同期で動かすためには標準出力を閉じる必要があるが PHP では閉じる方法がないのである。

ダメぢゃ~ん!!!

で, しょうがないのでアプローチを変えることにした。 fork がダメなら exec で別プロセスを起動するしかない。 しかし, これも結構曲者で, 普通に起動したのではダメらしい。 exec や system のマニュアルにはこのように書かれている。

「プログラムがこの関数で始まる場合、バックグラウンドで処理を続けさせるには、プログラムの出力をファイルや別の出力ストリームにリダイレクトする必要があります。そうしないと、プログラムが実行を終えるまで PHP はハングしてしまいます。」 (via PHP: exec - Manual

なんじゃそら! つまり, 以下のように標準出力をリダイレクトしバックグラウンドで呼び出す必要がある。

exec("/usr/bin/php ./child_script.php > /dev/null 2>&1 &");

参考:

ここまで分かるのに, 半日も無駄な時間を潰してしまったよ。 思うのだが, PHP って制御に向かない(できないわけではない)んじゃないのかなぁ。 無理に PHP で実装するメリットが見えない。 そりゃ UI 周りは楽かもしれないけどさ。 いや, もう, 今更だけど。