Help us understand the problem. What is going on with this article?

PHP7調査(33)カスタムセッションハンドラの返り値の意味を変更

More than 5 years have passed since last update.

PHPでは独自のセッションハンドラをプログラマが実装することができます。具体的には、session_set_save_handler関数を使うことで、標準のセッションハンドラが対応していないようなストレージにセッション情報を保存することができます。

ところで、PHP5まではカスタムセッションハンドラの返り値の扱いが間違っていました。マニュアルではtruefalseを返すことになっているのですが、実際には失敗なら-1を、成功ならそれ以外を返すというインターフェースになっていました。

これが、PHP7ではマニュアル通りになるよう修正されます。

以下、PHP5でのセッションハンドラの利用例です。

<?php
ini_set('session.gc_divisor',1);

/* PHP5用のカスタムセッションハンドラ */
session_set_save_handler(
    /* open */
    function ($savePath, $sessionName) { return 0; }, /* -1だとFatal error */
    /* close */
    function () { return 0; }, /* -1でもエラーにならない */
    /* read */
    function ($sessionId) { return ['foo' => 'bar']; },
    /* write */
    function ($sessionId, $data) { return 0; }, /* -1だとWarning */
    /* destroy */
    function ($sessionId) { return 0; }, /* -1だとWarning */
    /* gc */
    function ($lifetime) { return 0; },  /* -1でもエラーにならない */
    /* create_sid */
    function () { return 'baz'; }
);

session_id('baaz');
session_start();
session_regenerate_id(TRUE);
session_write_close();

このように、セッションハンドラ内でエラーが起きたときは-1を返す必要がありました。驚くべきことに、マニュアル通りにtrueを返そうがfalseを返そうが成功として扱われていたのです(truefalseを整数にキャストしても10なので、失敗ではない=成功として扱われる)。

一方、PHP7では次のように記述できます。

<?php
ini_set('session.gc_divisor',1);

/* PHP7用のカスタムセッションハンドラ */
session_set_save_handler(
    /* open */
    function ($savePath, $sessionName) { return true; }, /* falseだとFatal error */
    /* close */
    function () { return true; }, /* falseでもエラーにならない */
    /* read */
    function ($sessionId) { return ['foo' => 'bar']; },
    /* write */
    function ($sessionId, $data) { return true; }, /* falseだとWarning */
    /* destroy */
    function ($sessionId) { return true; }, /* falseだとWarning */
    /* gc */
    function ($lifetime) { return true; },  /* falseでもエラーにならない */
    /* create_sid */
    function () { return 'baz'; }
);

session_id('baaz');
session_start();
session_regenerate_id(TRUE);
session_write_close();

成功ならtrueを、失敗ならfalseを返すというわけです。平和ですね。

どうしてこうなった

PHP内部のCの関数は次のような返り値で成功失敗を受け渡しています。

typedef enum {
  SUCCESS =  0,
  FAILURE = -1,     /* this MUST stay a negative number, or it may affect functions! */
} ZEND_RESULT_CODE;

今回のセッションハンドラはbooleanを返すPHP関数ですが、この返り値をそのままC関数の返り値に利用していたために意味が変わっていたというわけです。Cでありがちなうっかりミスと言えなくもないですが、今まで誰も気がつかなかったのがビックリですね。

参照

hnw
境界値バグが大好物。自分の日記で書くには小ネタすぎるネタをQiitaに書いています。
https://hnw.hatenablog.com/
klab
モバイルオンラインゲーム、その他スマートフォン関連サービス、及びサーバーインフラ開発・運用
http://www.klab.com/jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした