CodeIgniter上でデータベース利用方式でのSession使用時に、2度も3度も余計なsetcookie()を走らせないようにする。

皆様こんにちは。お久しぶりです。


今日は表題の小ネタ。(CodeIgniterの学習とは呼べないのでタイトルから外します。)


症状

以下のバグレポートの内容がCodeIgniter2.0の system/libraries/Session.phpでも直ってなさそうなので、俺俺修正コードを貼り付けておきます。
参照:http://codeigniter.com/bug_tracker/bug/7358/

Description

When using session using a database every call to un/set_userdata results in a cookie being set. When using a DB the session cookie shouldn’t change when the userdata is changed. no need for another cookie being sent.

un/set_userdata calls sess_write which is where it sets the new cookie. simple fix would be to drop the $this->_set_cookie($cookie_userdata);

超俺訳:

データベース利用方式でセッションを使っているとき
unset_userdataやset_userdataを毎回呼ぶ度に、クッキーへの書き込みが走ってるんだけど
そもそも保持したいデータはDBに入れてるのだから、unset_userdataやset_userdataしたタイミングでは
クッキー内容は何にも変わんねーよ。毎回クッキーに同じ値を書かなくてもいいじゃん。つかSet-Cookie:何回走らせてんだ。これバグだろ常考


現状の動きを確認してみる

証拠の写真1:開発時などでクッキーを暗号化していないとき

$config['sess_encrypt_cookie']= FALSE; で
クッキーの暗号化をしてなければ一発でわかる。
(ここでは3回setcookie()している。動かしたソースでset_userdataとかflash_dataが動いているため)

(写真赤丸内はSet-Cookie: csn=…ってなってるけど、$config['sess_cookie_name']のデフォルトだとSet-Cookie: ci_session=…)

証拠の写真2:クッキーを暗号化しているとき

$config['sess_encrypt_cookie']= TRUE; で
暗号化すると一見違うSet-Cookie:が走っているように見えるんだけど、
実はCI_Encryptのencodeを呼び出した時に呼ばれる_xor_encode()の中でmt_randが走っているからで復号した中身は一緒なのだ。

(確認は、$hoge=$this->CI->encrypt->decode($cookie_data); してmd5取ってvar_dump($hoge);してみれば良い。 )


修正ソース抜粋

いつもながら無保証です。

直前の$cookie_dataのmd5を保持しておいて、今回も同じだったら何もせず_set_cookie()を抜け出すって処理を入れてるだけ。

変更部分は、 //add start here 〜 //add end の間、他はオリジナルソースのSession.phpからコピーした。
あと、証拠の写真2記述のデバグ用に #$hoge=$this->CI->encrypt->decode($cookie_data); ってのをコメントアウトして入れている。


/application/libraries/MY_Session.php 内で_set_cookie()をオーバーライドする。
(MY_Session.phpが無ければ作るべし、MY_うんたらの説明はもう省略)

<?php if ( ! defined( 'BASEPATH' ) ) exit( 'No direct script access allowed' );
class MY_Session extends CI_Session {
  function __construct ()
  {
    parent :: __construct();
  }
//途中略(function _serialize()とかfunction _unserialize()とかのbugfix等々が間にある)
  // --------------------------------------------------------------------

  /**
   * Write the session cookie
   *
   * @access  public
   * @return  void
   */
  function _set_cookie($cookie_data = NULL)
  {
    if (is_null($cookie_data))
    {
      $cookie_data = $this->userdata;
    }

    // Serialize the userdata for the cookie
    $cookie_data = $this->_serialize($cookie_data);

    //add start here
    //see:http://codeigniter.com/bug_tracker/bug/7358/
    $cookie_md5 = md5($cookie_data);
    static $last_cookie_md5='';
    if($last_cookie_md5 !== $cookie_md5){
      $last_cookie_md5 = $cookie_md5;
    }else{
      return;
    }
    //add end

    if ($this->sess_encrypt_cookie == TRUE)
    {
      $cookie_data = $this->CI->encrypt->encode($cookie_data);
      #debug
      #$hoge=$this->CI->encrypt->decode($cookie_data);
      #var_dump(md5($hoge)) ;
    }
    else
    {
      // if encryption is not used, we provide an md5 hash to prevent userside tampering
      $cookie_data = $cookie_data.md5($cookie_data.$this->encryption_key);
    }

    // Set the cookie
    setcookie(
          $this->sess_cookie_name,
          $cookie_data,
          $this->sess_expiration + time(),
          $this->cookie_path,
          $this->cookie_domain,
          0
        );
  }

//下略

}//endofclass
/**
 * End of file MY_Session.php
 */
/**
 * Location: ./application/libraries/MY_Session.php
 */

?>


修正後の動きを確認してみる

$config['sess_encrypt_cookie']= FALSE;のとき


$config['sess_encrypt_cookie']= TRUE;のとき

ってなかんじで、全く同一のSet-Cookie:重複が回避された。


ではでは!