我使用session_set_save_handler()
将会话保存到DB。
从php v.5.3迁移到v.5.4后,根本不调用write()
函数;既不是在调用session_write_close()
函数时,也不是在脚本终止时(它之前工作正常,并且没有对代码进行更改)。read()
、open()
和close()
函数仍然照常调用。
我知道php 5.4中有几个与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);
抱歉在代码中进行了一些调试
在关闭函数调用之前,似乎您的会话对象被PHP内部垃圾收集器销毁。在这种情况下,您需要将session_write_close()
函数移动到destructor:
function __destruct() {
session_write_close();
}
我建议您根据新的会话处理机制重写代码。实现SessionHandlerInterface
以避免将来出现此类行为。