たとえばAjaxからPHPを呼び出して、何かレスポンスを返すとき
レスポンスを返した後に、後始末的な処理をしたいことがあったんです。
例えばファイル書き込みとかDB操作とか、
レスポンスを速めるために後でやりたいこともあるはず
そこで、レスポンス後の後始末する方法とかコード例 を色々まとめてみました。
レスポンスの速度を少しでも早めたいときに便利だと思います。
まずPHPからレスポンスを普通に返す方法のおさらい
知ってる人は飛ばしてOK
ここでは次の条件でレスポンスを返すものとします。
- ブラウザ側から Ajax で呼び出す
- PHP側でゴニョゴニョ処理をする
- 最後に JSON でエンコードして返す
もちろんレスポンスを受ける側は XMLHttpRequest でも Fetch API でもOK
ちなみに Fetch API については次記事参照
大昔は XMLHttpRequest しかなかったけど、今は選択肢が充実してます。
例えば普通に Ajax を使う場合、コード例はこんな感じになるはず
▼ リクエストを送る側のコード例(jQuery & Ajax)
1 2 3 4 5 6 7 |
$.ajax({ url: '/path/to/hoge.php', type: 'POST', data: { csv: 'a,b,c,d,e,f' } }).done(function(resp){ console.log('response', resp); }); |
▼ レスポンスを返す側のコード(PHP)
1 2 3 |
$csv = $_POST['csv']; $data = explode( ',', $csv ); echo json_encode( $data ); |
▼ ブラウザ側に返ってきたレスポンス
1 |
resp ["a","b","c","d","e","f"] |
一般的にはこんな感じが多そう
それで上のPHPコード、レスポンス返してる部分ありますよね?
もちろん echo json_encode( $data ); のあとに処理も書けます。
でもそれだと、レスポンス以外の余計な処理が発生しちゃいます。
なるべくならレスポンス後に色々やるのがスマートかもしれない。
レスポンス後に回した方がいい処理の例
たとえばこんな処理はレスポンス後の方がいいです。(レスポンスに関係ないなら)
- ファイルへの書き込み
- MySQLの更新・操作
- 負荷がかかりすぎる処理
もし何万件とかあるDBの操作をしてからレスポンスするとかだったら・・・
本当は数秒で返せるのに、何十秒もかかることになるかもしれません。かなりのムダ
例えば重い処理を疑似的に再現するコードを書いてみます。
▼ 多重 for ループで高負荷処理を再現してみる
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/// POST変数を受け取る $csv = $_POST['csv']; /// レスポンスを返す $data = explode( ',', $csv ); echo json_encode( $data ); /// 何か重い処理(DB操作とか) for($i = 0; $i < 10000; $i++){ for($j = 0; $j < 50000; $j++){ /// Do nothing... } } |
このように書くと、「え!?別にこれいいじゃん?」と思う人も多いと思いますが、実はレスポンスした内容がブラウザ側に返るのは全処理が終わった後 です。
なので普通のやり方だと、レスポンスが遅くなる原因になりかねないという訳です。
そこで途中でレスポンスを返せる方法を試してみました。
PHPでレスポンス終了後に処理をする方法とは...
ではPHPでレスポンス終了後に重い処理をするやり方について
その方法を箇条書きすると・・・
- まず出力前に ob_start を呼び出す
- いつも通りにレスポンス出力
- TCP切断するヘッダーを送信
- ob_end_flush 、 ob_flush 、 flush 実行
こういう感じで、内容出力後にレスポンスを "返し終わった状態" にするのがポイント
ただ echo をするのではなく、バッファ出力するのが大事ですね。
実際のコード例を示すなら次の通り
▼ さっきのコード例を書き換えてみた
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
/// POST変数を受け取る $csv = $_POST['csv']; // 必ず呼び出す ob_start(); /// レスポンスを返す $data = explode( ',', $csv ); echo json_encode( $data ); /// TCP接続終了のヘッダー送信 header('Connection: close'); header('Content-Length: '.ob_get_length()); // とりあえず全てを出力 ob_end_flush(); ob_flush(); flush(); /// なんか重い処理(DB更新とか) for($i = 0; $i < 10000; $i++){ for($j = 0; $j < 50000; $j++){ /// Do nothing... } } |
最初でも書いたように次の3つがポイント
- まず ob_start を呼び出す
この関数は出力のバッファリングをオンにするための関数
- レスポンス後にヘッダー送信
上のように Connection: close を送るとTCP接続コネクションを終えることができる
あと Content-Length に出力したバイト数も指定しないとダメみたい - バッファを全部クリアする
順に ob_end_flush(); 、 ob_flush(); 、 flush(); を呼び出す。
実際に実行してみたら、すぐにレスポンスが返ってきました。
(ちなみに普通に返すだけだと数十秒くらいかかってた)
レスポンスの後始末が必要なら、この方法を使うのがスマートかもしれません。
ここまでのまとめ
ということで、ここまでの箇条書き
- レスポンス後にした方がいい処理
たとえばレスポンスと関係のない、ファイル書き込みとかDB操作とか・・・
- 普通にレスポンスを返すのではダメ
たとえば echo したあとに処理を書くこともできるが、レスポンスがブラウザ側に送られるのはスクリプトが全部実行されたタイミングだから
- レスポンスを即反映する工夫が必要
出力前に ob_start(); を呼び出し、レスポンス後にTCP接続が終わったことを表すヘッダー送信、 ob_end_flush(); とか呼び出してバッファをクリアする
以上、PHPでレスポンスした後になにか処理する方法でした。ではでは(^^)/~~~