【2019年08月24日】

最近のWebサイトではAjaxは結構普通に使われています。
昔はあまりみなかったLoadingのアニメーションをよく目にするようになりました。
Ajaxを利用するとページ遷移を伴わないスムーズなインターフェイスが作ることができます。

ご存知の通り、AjaxではJavascriptにより、裏側でサーバーとHTTPリクエストを行い、レスポンスを得ることができます。
ここで結構落とし穴になるのがPHPのセッションの扱いです。

PHPのセッションはファイルで管理されています。セッションに書き込みを行うと、セッションデータ保持用のファイルにデータを書き込んでいるわけです。そして通常、session_start関数によってセッションを開始するとそのファイルをロックします。明示的に閉じない限りはスクリプトの終了時に自動的に閉じられます。

Ajaxが使われていない時代はそれで何の問題もありませんでした。
しかしAjaxを利用する場合ここに落とし穴があります。
Ajaxは非同期通信です。HTTPリクエストは投げっぱなしで、Javascriptの処理はお構いなしに続行されます。つまり、一度リクエストを投げて、そのレスポンスが返ろうが返るまいが、次のリクエストを投げられちゃうわけです。

これはつまり、同じセッションに対して同時にアクセスが発生する可能性があるわけです。そしてPHPの処理でセッションへアクセスがある場合にどうなるか。一回目のリクエストでセッションファイルはロックされている状態なので、そのスクリプトが終了しないうちに次のリクエストが来てしまったらセッションにアクセスできません。この場合どうなるか。一回目のリクエストによるセッションファイルのロックが開放されるまで待たされるのです。
そうすると、例えば1回目のリクエストが時間のかかる処理で、続くリクエストが一瞬の処理の場合でも、1回目の処理が終了後、やっと次のリクエストの処理が動き出すような形になってしまいます。

非同期処理である以上、一つ一つのリクエストが干渉し合うのはよろしくありません。

問題なのはセッションがロックされていること。セッションの書き込み処理なんて一瞬です。でも、一回session_startしちゃったらスクリプトが終了するまでロックしっぱなし。それなら書き込み処理が終われば閉じちゃえばいいんです。
それを行うのがsession_write_close関数。この関数はセッションへの書き込みを終了し、セッションファイルのロックを開放してくれます。

だから、$_SESSIONへ値のセットの直後にsession_write_close()ってやればいいわけです。何なら、専用のセッション書き込み用関数(またはメソッド)を作っちゃうのもいいです。

例えば、

<?php

function setSession($key, $value)
{
    session_start();
    $_SESSION[$key] = $val;
    session_write_close();
}

?>

こうやれば、書き込みの瞬間だけセッションファイルがロックされることになり、PHPへの同時リクエストにも対応できます。

というわけでAjax実装の際はセッションを意識してみてください。