4

我正在使用session_set_save_handler()将会话保存到数据库。

从 php v.5.3 迁移到 v.5.4 后,该write()函数根本没有被调用;无论是在调用session_write_close()函数时,还是在脚本终止时(之前它工作正常,并且没有对代码进行任何更改)。和函数仍照常调用read()。 我知道 php 5.4 中有一些与机制相关的变化。有没有人有类似的问题或知道发生了什么变化?open()close()

session_set_save_handler()

class session {

    private $table_name;

    function __construct() {
        $this->table_name = SESS_TABLE;

        session_set_save_handler(array($this, 'open'), array($this, 'close'), array($this, 'read'), array($this, 'write'), array($this, 'destroy'), array($this, 'gc'));
        register_shutdown_function('session_write_close');

    }

    function start_session($session_name, $secure) {
        global $session;

        // Make sure the session cookie is not accessable via javascript.
        $httponly = true;

        // Hash algorithm to use for the sessionid. (use hash_algos() to get a list of available hashes.)
        $session_hash = 'sha512';

        // Check if hash is available
        if (in_array($session_hash, hash_algos())) {
          // Set the has function.
          ini_set('session.hash_function', $session_hash);
        }
        // How many bits per character of the hash.
        // The possible values are '4' (0-9, a-f), '5' (0-9, a-v), and '6' (0-9, a-z, A-Z, "-", ",").
        ini_set('session.hash_bits_per_character', 5);

        // Force the session to only use cookies, not URL variables.
        ini_set('session.use_only_cookies', 1);

        // Get session cookie parameters 
        $cookieParams = session_get_cookie_params(); 
        // Set the parameters
        session_set_cookie_params($cookieParams["lifetime"], $cookieParams["path"], $cookieParams["domain"], $secure, $httponly); 
        // Change the session name 
        session_name($session_name);
        // Now we cat start the session
        session_start();
        // This line regenerates the session and delete the old one. 
        // It also generates a new encryption key in the database. 
        if(USE_REGENERATE){
            $this->regenerate_id();
        }
        //session_regenerate_id(true);    
    }

    function regenerate_id() {

        $old_sess_id = session_id();
        session_regenerate_id(true);
        $new_sess_id = session_id();
        Logger::write($old_sess_id .'-'.$new_sess_id   ,  'session.log');

        $time = time();
        if(!isset($this->u_stmt)) {
          $this->u_stmt = $this->db->prepare(" UPDATE ".$this->table_name." set id = ? where id=?");
        }

        $this->u_stmt->bind_param('ss', $new_sess_id,$old_sess_id);
        $this->u_stmt->execute();
        return true;
    }

    function open() {
        $host = 'localhost';
        $user = SESS_USER;
        $pass = SESS_PASSWORD;
        $name = SESS_DBNAME;
        $mysqli = new mysqli($host, $user, $pass, $name);
        $this->db = $mysqli;
        return true;
    }

    function close() {
        $this->db->close();
        return true;
    }

    function read($id) {
        global $s_read_start, $s_read_end;
        $s_read_start = microtime(true);
        if(!isset($this->read_stmt)) {
          $this->read_stmt = $this->db->prepare("SELECT data FROM ".$this->table_name." WHERE id = ? LIMIT 1");
        }
        $this->read_stmt->bind_param('s', $id);
        $this->read_stmt->execute();
        $this->read_stmt->store_result();
        $this->read_stmt->bind_result($data);
        $this->read_stmt->fetch();
        $key = $this->getkey($id);
        $data = $this->decrypt($data, $key);
        $s_read_end = microtime(true);
        if($s_read_end-$s_read_start > MORE_THEN)
            error_log (date("Y-m-d H:i:s").' '.'READ: '. ($s_read_end-$s_read_start).PHP_EOL,3,BASE_DIR.'/logs/cookies.log');
        return $data;
    }

    function write($id, $data) {
        error_log (date("Y-m-d H:i:s").' '.'WRITE: '.PHP_EOL,3,BASE_DIR.'/logs/cookies.log');
        global $s_write_start, $s_write_end;
        $s_write_start = microtime(true);
        // Get unique key
        $key = $this->getkey($id);
        // Encrypt the data
        $data = $this->encrypt($data, $key);

        $time = time();
        if(!isset($this->w_stmt)) {
          $this->w_stmt = $this->db->prepare("REPLACE INTO ".$this->table_name." (id, set_time, data, session_key) VALUES (?, ?, ?, ?)");
        }

        $this->w_stmt->bind_param('siss', $id, $time, $data, $key);
        $this->w_stmt->execute();
        $s_write_end = microtime(true);
        if($s_write_end-$s_write_start > MORE_THEN)
            error_log (date("Y-m-d H:i:s").' '.'WRITE: '. ($s_write_end-$s_write_start).PHP_EOL,3,BASE_DIR.'/logs/cookies.log');
        return true;
    }

    function destroy($id) {
        global $s_destroy_start, $s_destroy_end;
        $s_destroy_start = microtime(true);

        if(!isset($this->delete_stmt)) {
          $this->delete_stmt = $this->db->prepare("DELETE FROM ".$this->table_name." WHERE id = ?");
        }
        $this->delete_stmt->bind_param('s', $id);
        $this->delete_stmt->execute();
        $s_destroy_end = microtime(true);
        if($s_destroy_end-$s_destroy_start > MORE_THEN)
            error_log (date("Y-m-d H:i:s").' '.'DESTROY: '. ($s_destroy_end-$s_destroy_start).PHP_EOL,3,BASE_DIR.'/logs/cookies.log');
        return true;
    }

    function gc($max) {
        global $s_gc_start, $s_gc_end;
        $s_gc_start = microtime(true);

        if(!isset($this->gc_stmt)) {
            $this->gc_stmt = $this->db->prepare("DELETE FROM ".$this->table_name." WHERE set_time < ?");
        }
        $old = time() - $max;
        $this->gc_stmt->bind_param('s', $old);
        $this->gc_stmt->execute();
        $s_gc_end = microtime(true);
        if($s_gc_end-$s_gc_start > MORE_THEN)
            error_log (date("Y-m-d H:i:s").' '.'GC: '. ($s_gc_end-$s_gc_start).PHP_EOL,3,BASE_DIR.'/logs/cookies.log');
        return true;
    }

    private function getkey($id) {
        if(!isset($this->key_stmt)) {
          $this->key_stmt = $this->db->prepare("SELECT session_key FROM ".$this->table_name." WHERE id = ? LIMIT 1");
        }
        $this->key_stmt->bind_param('s', $id);
        $this->key_stmt->execute();
        $this->key_stmt->store_result();
        if($this->key_stmt->num_rows == 1) { 
          $this->key_stmt->bind_result($key);
          $this->key_stmt->fetch();
          return $key;
        } else {
          $random_key = hash('sha512', uniqid(mt_rand(1, mt_getrandmax()), true));
          return $random_key;
        }
    }

    private function encrypt($data, $key) {
        $salt = 'cH!swe!retReGu7W6bEDRup7usuDUh9THeD2CHeGE*ewr4n39=E@rAsp7c-Ph@pH';
        $key = substr(hash('sha256', $salt.$key.$salt), 0, 32);
        $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
        $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
        $encrypted = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $data, MCRYPT_MODE_ECB, $iv));
        return $encrypted;
    }

    private function decrypt($data, $key) {
        $salt = 'cH!swe!retReGu7W6bEDRup7usuDUh9THeD2CHeGE*ewr4n39=E@rAsp7c-Ph@pH';
        $key = substr(hash('sha256', $salt.$key.$salt), 0, 32);
        $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
        $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
        $decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, base64_decode($data), MCRYPT_MODE_ECB, $iv);
        return $decrypted;
    }
}

用法:

$session = new session();
$session->start_session('name', false);

对不起,代码中的一些调试。

4

1 回答 1

0

似乎您的会话对象在关闭函数调用之前被 PHP 内部垃圾收集器破坏了。在这种情况下,您需要将session_write_close()函数移动到析构函数中:

function __destruct() {
  session_write_close();
}

我建议你根据新的会话处理机制重写你的代码。SessionHandlerInterface今后实施以避免此类行为。

于 2013-09-27T01:04:11.457 回答