CookieStoreなセッションの試み

Rails 2からデフォルトになってるCookieStoreなセッションいいなーと思ったのでkarinto_session2とか試しに作ってみた。どうするか未定。

レンタルサーバーでPHPを使うときって、ストレージを考えるとセッションが使いづらい。DBに保存してもいいけど管理面倒だし。そう考えると、クライアント側のCookieに保存するのは便利。Cookieを扱えない携帯電話とかは無視する方向で(そんなもんブラウザが悪い)。

<?php
class karinto_session2
{
    const default_secret_key = 'karinto_session_secret_key';
    const cookie_name = '_karinto_app_session';
    protected $_vars;
    public function __construct()
    {
        if (karinto::$session_secret_key === self::default_secret_key) {
            throw new karinto_session('secret key not changed');
        }
        $this->_vars = $this->_restore();
    }
    public function __destruct()
    {
        $this->_write();
    }
    public function __set($name, $value)
    {
        $this->_vars[$name] = $value;
    }
    public function __get($name)
    {
        if (isset($this->_vars[$name])) {
            return $this->_vars[$name];
        }
        return null;
    }
    public function __isset($name)
    {
        return isset($this->_vars[$name]);
    }
    public function __unset($name)
    {
        if (isset($this->_vars[$name])) {
            unset($this->_vars[$name]);
        }
    }
    protected function _restore()
    {
        if (!isset($_COOKIE[self::cookie_name])) {
            return array();
        }
        $cookie_data = $_COOKIE[self::cookie_name];
        if (get_magic_quotes_gpc()) {
            $cookie_data = stripslashes($cookie_data);
        }
        list($data, $digest_data) = explode('--', $cookie_data);
        if (is_null($data) || is_null($digest_data)) {
            $this->_destroy();
            return array();
        }
        $data = @unserialize(base64_decode($data));
        if ($data === false || !is_array($data)) {
            $this->_destroy();
            return array();
        }
        if ($this->_generate_digest($data) !== $digest_data) {
            $this->_destroy();
            return array();
        }
        return $data;
    }
    protected function _write()
    {
        // TODO check: length < 4KB
        $data = base64_encode(serialize($this->_vars));
        $digest_data = $this->_generate_digest($this->_vars);
        $cookie_data = $data . '--' . $digest_data;
        $this->_set_cookie($cookie_data);
    }
    protected function _destroy()
    {
        $this->_set_cookie('', -3600);
    }
    protected function _generate_digest($data)
    {
        $data = serialize($data);
        return sha1(karinto::$session_secret_key . $data);
    }
    protected function _set_cookie($value, $lifetime = null)
    {
        // use session settings
        $param = session_get_cookie_params();
        if (is_null($lifetime)) {
            $lifetime = $param['lifetime'];
        }
        $expire = time() + $lifetime;
        if (empty($param['domain']) && empty($param['secure'])) {
            setcookie(self::cookie_name, $value, $expire, $param['path']);
        } else if (empty($param['secure'])) {
            setcookie(self::cookie_name, $value,
                $expire, $param['path'], $param['domain']);
        } else {
            setcookie(self::cookie_name, $value,
                $expire, $param['path'], $param['domain'], $param['secure']);
        }
    }
}

あー、セッションタイムアウトのこととか考えてないや。考慮しないと。