欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

session_set_save_handler 有大用

session_set_save_handler

(PHP 4, PHP 5, PHP 7)

session_set_save_handler — 设置用户自定义会话存储函数

说明

session_set_save_handler ( callable $open , callable $close , callable $read , callable $write , callable $destroy , callable $gc [, callable $create_sid [, callable $validate_sid [, callable $update_timestamp ]]] ) : bool

自 PHP 5.4 开始,可以使用下面的方式来注册自定义会话存储函数:

session_set_save_handler ( object $sessionhandler [, bool $register_shutdown = TRUE ] ) : bool

session_set_save_handler() 设置用户自定义 会话存储函数。 如果想使用 PHP 内置的会话存储机制之外的方式, 可以使用本函数。 例如,可以自定义会话存储函数来将会话数据存储到数据库。

参数

本函数有 2 种原型:

或者
  • open(string $savePath, string $sessionName)

  • open 回调函数类似于类的构造函数, 在会话打开的时候会被调用。 这是自动开始会话或者通过调用 session_start() 手动开始会话 之后第一个被调用的回调函数。 此回调函数操作成功返回 TRUE,反之返回 FALSE

  • close()

  • close 回调函数类似于类的析构函数。 在 write 回调函数调用之后调用。 当调用 session_write_close() 函数之后,也会调用 close 回调函数。 此回调函数操作成功返回 TRUE,反之返回 FALSE

  • read(string $sessionId)

  • 如果会话中有数据,read 回调函数必须返回将会话数据编码(序列化)后的字符串。 如果会话中没有数据,read 回调函数返回空字符串。

    在自动开始会话或者通过调用 session_start() 函数手动开始会话之后,PHP 内部调用 read 回调函数来获取会话数据。 在调用 read 之前,PHP 会调用 open 回调函数。

    read 回调返回的序列化之后的字符串格式必须与 write 回调函数保存数据时的格式完全一致。 PHP 会自动反序列化返回的字符串并填充 $_SESSION 超级全局变量。 虽然数据看起来和 serialize() 函数很相似, 但是需要提醒的是,它们是不同的。 请参考: session.serialize_handler

  • write(string $sessionId, string $data)

  • 在会话保存数据时会调用 write 回调函数。 此回调函数接收当前会话 ID 以及 $_SESSION 中数据序列化之后的字符串作为参数。 序列化会话数据的过程由 PHP 根据 session.serialize_handler 设定值来完成。

    序列化后的数据将和会话 ID 关联在一起进行保存。 当调用 read 回调函数获取数据时,所返回的数据必须要和 传入 write 回调函数的数据完全保持一致。

    PHP 会在脚本执行完毕或调用 session_write_close() 函数之后调用此回调函数。 注意,在调用完此回调函数之后,PHP 内部会调用 close 回调函数。

    Note:

    PHP 会在输出流写入完毕并且关闭之后 才调用 write 回调函数, 所以在 write 回调函数中的调试信息不会输出到浏览器中。 如果需要在 write 回调函数中使用调试输出, 建议将调试输出写入到文件。

  • destroy($sessionId)

  • 当调用 session_destroy() 函数, 或者调用 session_regenerate_id() 函数并且设置 destroy 参数为 TRUE 时, 会调用此回调函数。此回调函数操作成功返回 TRUE,反之返回 FALSE

  • gc($lifetime)

  • 为了清理会话中的旧数据,PHP 会不时的调用垃圾收集回调函数。 调用周期由 session.gc_probability 和 session.gc_divisor 参数控制。 传入到此回调函数的 lifetime 参数由 session.gc_maxlifetime 设置。 此回调函数操作成功返回 TRUE,反之返回 FALSE

  • create_sid()

  • 当需要新的会话 ID 时被调用的回调函数。 回调函数被调用时无传入参数, 其返回值应该是一个字符串格式的、有效的会话 ID。

返回值

成功时返回 TRUE, 或者在失败时返回 FALSE

更新日志

版本说明
7.0.0新增可选参数 validate_sid 和 update_timestamp
5.5.1新增可选参数 create_sid
5.4.0新增用来实现自定义会话处理器的接口 SessionHandlerInterface; 新增 PHP 内部会话处理器类:SessionHandler

范例


Example #1 自定义会话管理器: 完整代码请参见 SessionHandlerInterface

下列代码适用于 PHP 5.4.0 及以上版本。 这里仅列出了调用方式,完整代码请参见 SessionHandlerInterface

这里使用了 session_set_save_handler() 函数的 OOP 原型 并且使用第二个参数来注册 shutdown 函数。 当将对象注册为会话保存管理器时,建议使用这种方式。

<?php
class MySessionHandler implements SessionHandlerInterface
{
    
// 在这里实现接口
}

$handler = new MySessionHandler();
session_set_save_handler($handlertrue);
session_start();

// 现在可以使用 $_SESSION 保存以及获取数据了

Example #2 使用对象自定义会话保存管理器

下列代码适用于 PHP 5.4.0 之前的版本。

下例演示了基于文件的会话数据存储, 和 PHP 默认的 files 存储器很相似。 通过对此示例代码进行扩展, 你可以很方便的实现使用数据库保存会话数据的功能。

针对于 PHP 5.4.0 之前的版本, 通过调用 register_shutdown_function() 函数 来注册 session_write_close() 回调函数。 这也是我们建议的方式。

<?php
class FileSessionHandler
{
    private 
$savePath;

    function 
open($savePath$sessionName)
    {
        
$this->savePath $savePath;
        if (!
is_dir($this->savePath)) {
            
mkdir($this->savePath0777);
        }

        return 
true;
    }

    function 
close()
    {
        return 
true;
    }

    function 
read($id)
    {
        return (string)@
file_get_contents("$this->savePath/sess_$id");
    }

    function 
write($id$data)
    {
        return 
file_put_contents("$this->savePath/sess_$id"$data) === false false true;
    }

    function 
destroy($id)
    {
        
$file "$this->savePath/sess_$id";
        if (
file_exists($file)) {
            
unlink($file);
        }

        return 
true;
    }

    function 
gc($maxlifetime)
    {
        foreach (
glob("$this->savePath/sess_*") as $file) {
            if (
filemtime($file) + $maxlifetime time() && file_exists($file)) {
                
unlink($file);
            }
        }

        return 
true;
    }
}

$handler = new FileSessionHandler();
session_set_save_handler(
    array(
$handler'open'),
    array(
$handler'close'),
    array(
$handler'read'),
    array(
$handler'write'),
    array(
$handler'destroy'),
    array(
$handler'gc')
    );

// 下面这行代码可以防止使用对象作为会话保存管理器时可能引发的非预期行为
register_shutdown_function('session_write_close');

session_start();
// 现在可以使用 $_SESSION 保存以及获取数据了

注释

Warning

在脚本执行完毕之后,PHP 内部会清除对象, 所以有可能不调用 write 和 close 回调函数。 这样可能会引发非预期的行为,所以当使用对象作为会话保存管理器时, 需要通过注册 shutdown 回调函数来规避风险。 通常,你可以通过调用 register_shutdown_function() 函数 来注册 'session_write_close' 回调函数。

在 PHP 5.4.0 中,可以调用 session_register_shutdown() 函数来注册 shutdown 回调函数。 如果你使用 session_set_save_handler() 的 OOP 原型, 那么仅需设置 “register shutdown” 为 TRUE 即可。

Warning

在 PHP 5.0.5 中,在对象销毁之后才会调用 write 和 close 回调函数, 所以,在这两个回调函数中不可以使用对象,也不可以抛出异常。 如果在函数中抛出异常,PHP 既不会捕获它,也不会跟踪它, 这样会导致程序异常终止。 但是对象析构函数可以使用会话。

可以在析构函数中调用 session_write_close() 函数来解决这个问题。 但是注册 shutdown 回调函数才是更加可靠的做法。

Warning

如果会话在脚本结束后关闭,对于某些 SAPI 而言,当前工作目录可能已经被改变。 可以调用 session_write_close() 函数在脚本执行结束之前关闭会话。

参见


add a note add a note

User Contributed Notes 40 notes

andreipa at gmail dot com ¶
4 years ago
After spend so many time to understand how PHP session works with database and unsuccessful attempts to get it right, I decided to rewrite the version from our friend stalker.

//Database
CREATE TABLE `Session` (
  `Session_Id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `Session_Expires` datetime NOT NULL,
  `Session_Data` text COLLATE utf8_unicode_ci,
  PRIMARY KEY (`Session_Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
SELECT * FROM mydatabase.Session;

<?php
//inc.session.php

class SysSession implements SessionHandlerInterface
{
    private 
$link;
   
    public function 
open($savePath$sessionName)
    {
        
$link mysqli_connect("server","user","pwd","mydatabase");
        if(
$link){
            
$this->link $link;
            return 
true;
        }else{
            return 
false;
        }
    }
    public function 
close()
    {
        
mysqli_close($this->link);
        return 
true;
    }
    public function 
read($id)
    {
        
$result mysqli_query($this->link,"SELECT Session_Data FROM Session WHERE Session_Id = '".$id."' AND Session_Expires > '".date('Y-m-d H:i:s')."'");
        if(
$row mysqli_fetch_assoc($result)){
            return 
$row['Session_Data'];
        }else{
            return 
"";
        }
    }
    public function 
write($id$data)
    {
        
$DateTime date('Y-m-d H:i:s');
        
$NewDateTime date('Y-m-d H:i:s',strtotime($DateTime.' + 1 hour'));
        
$result mysqli_query($this->link,"REPLACE INTO Session SET Session_Id = '".$id."', Session_Expires = '".$NewDateTime."', Session_Data = '".$data."'");
        if(
$result){
            return 
true;
        }else{
            return 
false;
        }
    }
    public function 
destroy($id)
    {
        
$result mysqli_query($this->link,"DELETE FROM Session WHERE Session_Id ='".$id."'");
        if(
$result){
            return 
true;
        }else{
            return 
false;
        }
    }
    public function 
gc($maxlifetime)
    {
        
$result mysqli_query($this->link,"DELETE FROM Session WHERE ((UNIX_TIMESTAMP(Session_Expires) + ".$maxlifetime.") < ".$maxlifetime.")");
        if(
$result){
            return 
true;
        }else{
            return 
false;
        }
    }
}
$handler = new SysSession();
session_set_save_handler($handlertrue);
?>

<?php
//page 1
require_once('inc.session.php');

session_start();

$_SESSION['var1'] = "My Portuguese text: SOU Gaucho!";
?>

<?php
//page 2
require_once('inc.session.php');

session_start();

if(isset(
$_SESSION['var1']){
echo 
$_SESSION['var1'];
}
//OUTPUT: My Portuguese text: SOU Gaucho!
?>
ohcc at 163 dot com ¶
2 years ago
As of PHP 7.0, you can implement SessionUpdateTimestampHandlerInterface to
define your own session id validating method like validate_sid and the timestamp updating method like update_timestamp in the non-OOP prototype of session_set_save_handler().

SessionUpdateTimestampHandlerInterface is a new interface introduced in PHP 7.0, which has not been documented yet. It has two abstract methods: SessionUpdateTimestampHandlerInterface :: validateId($sessionId) and SessionUpdateTimestampHandlerInterface :: updateTimestamp($sessionId, $sessionData).

<?php
    
/*
       @author Wu Xiancheng
       Code structure for PHP 7.0+ only because SessionUpdateTimestampHandlerInterface is introduced in PHP 7.0
       With this class you can validate php session id and update the timestamp of php session data
       with the OOP prototype of session_set_save_handler() in PHP 7.0+
    */
    
class PHPSessionXHandler implements SessionHandlerInterfaceSessionUpdateTimestampHandlerInterface {
        public function 
close(){
            
// return value should be true for success or false for failure
            // ...
        
}
        public function 
destroy($sessionId){
            
// return value should be true for success or false for failure
            // ...
        
}
        public function 
gc($maximumLifetime){
            
// return value should be true for success or false for failure
            // ...
        
}
        public function 
open($sessionSavePath$sessionName){
            
// return value should be true for success or false for failure
            // ...
        
}
        public function 
read($sessionId){
            
// return value should be the session data or an empty string
            // ...
        
}
        public function 
write($sessionId$sessionData){
            
// return value should be true for success or false for failure
            // ...
        
}
        public function 
create_sid(){
            
// available since PHP 5.5.1
            // invoked internally when a new session id is needed
            // no parameter is needed and return value should be the new session id created
            // ...
        
}
        public function 
validateId($sessionId){
            
// implements SessionUpdateTimestampHandlerInterface::validateId()
            // available since PHP 7.0
            // return value should be true if the session id is valid otherwise false
            // if false is returned a new session id will be generated by php internally
            // ...
        
}
        public function 
updateTimestamp($sessionId$sessionData){
            
// implements SessionUpdateTimestampHandlerInterface::validateId()
            // available since PHP 7.0
            // return value should be true for success or false for failure
            // ...
        
}
    }
?>
carlos dot vini at gmail dot com ¶
4 years ago
If you have a custom handler registered ini_get('session.save_handler') will return 'user' instead of 'file'
ivo at magstudio dot net ¶
17 years ago
Just a few words to explain some troubles while using session_set_save_handler(). It appears that internally PHP calls session management functions in this order: open(), read(), write(), close(). Close() function is called even if you does not make a call to sesison_start(), perhaps for some reasons like cleaning.
   If you try to redefine these functions and call sessions_set_save_handler() but something doesn't work, (in my case the ssion data hasn't been written) it's a good idea to debug them in the order they are called. They doesn't produce error output to browser but you can use print or echo.
   Shortly, if your write() function doesn't work as expected take a look for errors in previous functions - open() and read().
   I hope that this will help to save someone few hours debugging.
frank at interactinet dot com ¶
9 years ago
I had trouble with committing session data.
To "commit and continue" without closing your session, put this at the top of your "write" method:

<?php

$id 
session_id();
session_write_close();
session_id($id);
session_start();

?>

Note that ANY time php generates a new session id, it is not automatically updated in a database. This can be helpful:

<?php

public function resetSessionId()
{
    
$old session_id();
    
session_regenerate_id();
    
$new session_id();
    
SessionHandler::regenerate_id($old,$new);
}

public function 
regenerate_id($old,$new)
{
    
$db mysqli->connect(...);

    
$db->query('UPDATE sessions SET session_id = \''.$db->escape_string($new).'\'
    WHERE session_id = \''
.$db->escape_string($old).'\'');
}
?>
e dot sand at elisand dot com ¶
11 years ago
The "binary" data that is in the session data appears to surround class/object names, and if you pass your session data through a function to sanitize it for SQL injection, you may indeed run in to problems.

For example, using the PDO::quote() function to prepare the data for injection (in my case for SQLite3), it was stopping short as soon as it encountered the first bit of binary data, causing my session information to be corrupted.

This change *must* have happened somewhere in the 5.2 series, because I just started encountering this problem recently on a code base that had been tested & working on earlier versions of PHP 5.2.

This may in fact be a bug - I have not yet checked... but beware, and perhaps using base64 to encode/decode your session data is a good thing to do just to be sure (though you are now left unable to visually inspect serialized session information at the storage level which is a rather big problem for on-the-fly debugging of sessions).
matt at openflows dot org ¶
14 years ago
Note that for security reasons the Debian and Ubuntu distributions of php do not call _gc to remove old sessions, but instead run /etc/cron.d/php*, which check the value of session.gc_maxlifetime in php.ini and delete the session files in /var/lib/php*.  This is all fine, but it means if you write your own session handlers you'll need to explicitly call your _gc function yourself.  A good place to do this is in your _close function, like this:

<?php
function _close() {
    
_gc(get_cfg_var("session.gc_maxlifetime"));
   
// rest of function goes here
}
?>
Steven George ¶
6 years ago
Note that as well as destructing objects before calling write() and close(), it seems PHP also destroys classes.  That is, you can't even call a static method of an external class in the write() and close() handlers - PHP will issue a Fatal error stating "Class xxxx not found"
pavelc at users dot sourceforge dot net ¶
9 years ago
I write a class that unites whole handler functionality. It's not even needed to save instances of this class in variables. Just add a row:
<?php
new SessionSaveHandler();
?>
and the handler will rule the sessions ;-)
<?php

class SessionSaveHandler {
    protected 
$savePath;
    protected 
$sessionName;

    public function 
__construct() {
        
session_set_save_handler(
            array(
$this"open"),
            array(
$this"close"),
            array(
$this"read"),
            array(
$this"write"),
            array(
$this"destroy"),
            array(
$this"gc")
        );
    }

    public function 
open($savePath$sessionName) {
        
$this->savePath $savePath;
        
$this->sessionName $sessionName;
        return 
true;
    }

    public function 
close() {
        
// your code if any
        
return true;
    }

    public function 
read($id) {
        
// your code
    
}

    public function 
write($id$data) {
        
// your code
    
}

    public function 
destroy($id) {
        
// your code
    
}

    public function 
gc($maxlifetime) {
        
// your code
    
}
}

new 
SessionSaveHandler();

?>
tomas at slax dot org ¶
12 years ago
Regarding the SAPIs: The warning mentioned in function's description (that the Current working directory is changed with some SAPIs) is very important.

It means that if your callback 'write' function needs to write to a file in current directory, it will not find it. You have to use absolute path and not rely upon the current working directory.

I thought this warning applies only to some strange environments like Windows, but it happens exactly on Linux + Apache 2.2 + PHP 5.
james at dunmore dot me dot uk ¶
12 years ago
I think it is very important here to stress that the WRITE method should use UPDATE+INSERT (or mysql specific REPLACE).

There is example code "out there" that uses just UPDATE for the write method, in which case, when session_regenerate_id is called, session data is lost (as an update would fail, as the key has changed).

I've just wasted a whole day due to this (I know I should have thought it through / RTFM, but it is an easy trap to fall into).
korvus at kgstudios dot net ¶
15 years ago
It seems when you call 'session_name()', php loads the session id automatically from GET ( if the index exists ) and passes it to the 'read' callback method correctly, but the 'write' callback is invoked twice: first the auto-generated session id, then the custom session id

So be aware of what queries you execute inside the callback .. I got crazy because I used a MySQL 'REPLACE' statement to agilize, and I spent a lot of hours trying to understand why 2 rows instead of 1 were being affected ( the first id was inserting, the second updating )

I hope this helps!
yangqingrong at gmail dot com ¶
11 years ago
session_set_save_handler is used before session_start.if your session is setted as auto start. it will return FALSE value.so you need add session_write_close() before session_set_save_handler to cancel the session's auto start.it  likes this:

<?php
/*
qq:290359552
*/
session_write_close(); //cancel the session's auto start,important

function open()
{
   ...
}
....
session_set_save_handler( ... );
session_start();
?>
harald at hholzer at ¶
11 years ago
after spending 8 hours to find out whats going on..

just for the records, because php.net ignore the real world out there:

debian 5 installs by default the php-suhosin module, which changes the behavior of session_set_save_handler read/write function.

on calling the session write function the session data will be encrypted, and the returning string from the read function are decrypted and verified.

the encrypted data is no more compatible with session_encode/session_decode.

and breaks by default, subdomain handling and multiple host setups where different document roots are used.

for futher information look at:
http://www.hardened-php.net/suhosin/configuration.html

session sample data (debian 4):
test|s:3:"sdf";

session sample data (debian 5, with php-suhosin):
3GdlPEGr2kYgRFDs-pUSoKomZ4fN7r5BM5oKOCMsWNc...

i thing the suhosin patch should report a warning in case of invalid session data, to get a clue whats going wrong.
oliver at teqneers dot de ¶
15 years ago
For some people it might be important to know, that if the standard session handler has been overwritten with session_set_save_handler, no locking is working anymore (between session_read and session_write). The following might happen:

script "A" start         .
read session data        .
.                        script "B" start
.                        read session data
running (30secs)         add session data
.                        write sesion data
.                        script "B" stop
write session data       .
script "A" stop          .

If a script "A" runs for a long time (say 30secs) the same user might start another script "B", which also uses a session. Script "B" will start and read session data, even though script "A" is still running. Because script "B" is much faster it will finish its work and write back its session data before script "A" has ended. Now script "A" ends and overwrites all of script "B"'s session data. If you DON'T use session_set_save_handler, this cannot happend, because in this case, PHP will not start script "B" until script "A" ends.
stalker at ruun dot de ¶
14 years ago
object- and mysql-based session-handler, requires the following table:

CREATE TABLE `ws_sessions` (
  `session_id` varchar(255) binary NOT NULL default '',
  `session_expires` int(10) unsigned NOT NULL default '0',
  `session_data` text,
  PRIMARY KEY  (`session_id`)
) TYPE=InnoDB;

<?php
class session {
    
// session-lifetime
    
var $lifeTime;
    
// mysql-handle
    
var $dbHandle;
    function 
open($savePath$sessName) {
       
// get session-lifetime
       
$this->lifeTime get_cfg_var("session.gc_maxlifetime");
       
// open database-connection
       
$dbHandle = @mysql_connect("server","user","password");
       
$dbSel = @mysql_select_db("database",$dbHandle);
       
// return success
       
if(!$dbHandle || !$dbSel)
           return 
false;
       
$this->dbHandle $dbHandle;
       return 
true;
    }
    function 
close() {
        
$this->gc(ini_get('session.gc_maxlifetime'));
        
// close database-connection
        
return @mysql_close($this->dbHandle);
    }
    function 
read($sessID) {
        
// fetch session-data
        
$res mysql_query("SELECT session_data AS d FROM ws_sessions
                            WHERE session_id = '
$sessID'
                            AND session_expires > "
.time(),$this->dbHandle);
        
// return data or an empty string at failure
        
if($row mysql_fetch_assoc($res))
            return 
$row['d'];
        return 
"";
    }
    function 
write($sessID,$sessData) {
        
// new session-expire-time
        
$newExp time() + $this->lifeTime;
        
// is a session with this id in the database?
        
$res mysql_query("SELECT * FROM ws_sessions
                            WHERE session_id = '
$sessID'",$this->dbHandle);
        
// if yes,
        
if(mysql_num_rows($res)) {
            
// ...update session-data
            
mysql_query("UPDATE ws_sessions
                         SET session_expires = '
$newExp',
                         session_data = '
$sessData'
                         WHERE session_id = '
$sessID'",$this->dbHandle);
            
// if something happened, return true
            
if(mysql_affected_rows($this->dbHandle))
                return 
true;
        }
        
// if no session-data was found,
        
else {
            
// create a new row
            
mysql_query("INSERT INTO ws_sessions (
                         session_id,
                         session_expires,
                         session_data)
                         VALUES(
                         '
$sessID',
                         '
$newExp',
                         '
$sessData')",$this->dbHandle);
            
// if row was created, return true
            
if(mysql_affected_rows($this->dbHandle))
                return 
true;
        }
        
// an unknown error occured
        
return false;
    }
    function 
destroy($sessID) {
        
// delete session-data
        
mysql_query("DELETE FROM ws_sessions WHERE session_id = '$sessID'",$this->dbHandle);
        
// if session was deleted, return true,
        
if(mysql_affected_rows($this->dbHandle))
            return 
true;
        
// ...else return false
        
return false;
    }
    function 
gc($sessMaxLifeTime) {
        
// delete old sessions
        
mysql_query("DELETE FROM ws_sessions WHERE session_expires < ".time(),$this->dbHandle);
        
// return affected rows
        
return mysql_affected_rows($this->dbHandle);
    }
}
$session = new session();
session_set_save_handler(array(&$session,"open"),
                         array(&
$session,"close"),
                         array(&
$session,"read"),
                         array(&
$session,"write"),
                         array(&
$session,"destroy"),
                         array(&
$session,"gc"));
session_start();
// etc...
?>
dummynick at gmail dot com ¶
10 years ago
I was getting Fatal error: Exception thrown without a stack frame and it took days to figure out the reason. I am using memcache to store sessions and in my custom class I use Memcache class in write method.

I put the code in the write method inside try-catch block and it solved my problem.
joel the usual at sign then purerave.com ¶
10 years ago
When storing sessions in a DB, it's usually beneficial to use an existing custom DB object, but this creates problems with the latest version of PHP 5.3.1. This used to work fine on PHP 5.2.x (Linux and Windows).

The problem now is that session_write_close() is not automatically called when execution ends, but rather after all the objects have been destructed, including the DB object!

There are two ways around this, either manually calling session_write_close() at the end of your script(s), or not using the DB object.

I'm sure this is the intended behavior from the beginning.
james dot ellis at gmail dot com ¶
12 years ago
When writing your own session handler, particularly database session handlers, play close attention to garbage cleanup and how it could affect server load.

To pick a round number example:

If you have 1000 requests per minute on session enabled pages, everyone needs a session started but the session garbage cleanup does not need to run every request. Doing so would cause unrequired queries on the database server.

In this example, setting your probability/divisor to 1/1000 would be sufficient to clean up old sessions at a minimum once a minute. If you don't need that kind of granularity, increase the gc divisor.

Finding the tradeoff between clearing up old sessions and server load is the important aspect here.
information at saunderswebsolutions dot com ¶
14 years ago
Note that if session.auto_start is set to On in the php.ini, your session_set_save_handler will return false as the session has already been initialized.

If you are finding that your code works OK on one machine but doesn't work on another, check to see if session.auto_start is set to On
Rusty X ¶
8 years ago
It is important to understand that PHP's default file-based session handling LOCKS the session file, inherently allowing ONLY ONE thread handling any given session at a time.
When you implement a DB-backed session storage and you do not do any locking, you may run into situations where more than one thread is serving the same session, and you may LOSE DATA because the second thread will overwrite any session changes done by the first thread.
You should therefore think about locking the session somehow if you want to have the exact same behavior as with the default file-based implementation. For example, with InnoDB you could do a SELECT ... FOR UPDATE, or you can use the GET_LOCK() function.
peter at brandrock dot co dot za ¶
2 years ago
If saving to a database, as in the examples on this page, for performance, consider the following.

Build the Sessions table with an index on the SessionExpires column to quickly identify rows to be deleted in the garbage collection phase.

Rather do a garbage collection "delete from sessions where expiresOn < $now" on every session start/open. If you have an index on expiry time, this will not be a big hit, and evens out the load across all users. If it is possible that a large number of sessions will expire at the same time, include a "limit 100" clause, set for whatever number is reasonable, so that each user shares the load.

Use a varchar rather than Text to store the data, as Text will store the column off-page and is retrieved slightly slower. Use Text only if your application really does store large amounts of text in the session.
ohcc at 163 dot com ¶
2 years ago
What is not documented is that callables $validate_sid and $update_timestamp are supported since PHP 7.0. for the
prototype of "bool session_set_save_handler ( callable $open , callable $close , callable $read , callable $write , callable $destroy , callable $gc [, callable $create_sid [, callable $validate_sid [, callable $update_timestamp ]]] )".

validate_sid($sessionId)
  This callback is to validate $sessionId. Its return value should be true for valid session id $sessionId or false for invalid session id $sessionId. If false is returned, a new session id is generated to replace the invalid session id $sessionId.

update_timestamp($sessionId)
  This call back is to update timestamp, and its return value should be true for success or false for failure.

If you use this prototype, if you provide less than 6 parameters or if you provide more parameters than session_set_save_handler() accepts, you will get a "Wrong parameter count for session_set_save_handler()" warning.

If you use the OOP prototype of session_set_save_handler(SessionHandlerInterface $sessionhandler [, bool $register_shutdown = true ] ), a member method named neither validate_sid nor update_timestamp of the class of $sessionhandler are not invoked even in PHP 7.2, but a member method named create_sid is supported as of PHP 5.5.1.

It's 16th December, 2017 today, the documetation even PHP may get updated sometime afterwards.
centurianii at yahoo dot co dot uk ¶
3 years ago
Adding to the very useful class from: andreipa at gmail dot com

1. You should handle session expiration & data I/O from the SessionHandlerInterface methods,
2. You should NOT handle session regeneration and data modification from these methods but from a static method, e.g. sth like Session::start().
3. PHP gives a lot of examples but does NOT say what's the perspective under which one should work.

A skeleton of such a class:
namespace xyz;
class Session implements \SessionHandlerInterface, Singleton {
   /** @var SessionToken $token The SessionToken of this command;
          this is part of my programming approach */
   protected $token;
   /** @var PDO $dbh The PDO handler to the database */
   protected $dbh;
   /** @var $savePath Where sessions are stored */
   protected $savePath;
   /** @var $type Type of sessions (['files'|'sqlite']) */
   protected $type;
   /** @var self $instance An instance of this class */
   static private $instance = null;

   private function __construct() { ... }
   static public function getInstance() {
      if (self::$instance === null) {
         self::$instance = new self();
      }
      return self::$instance;
   }
   public function open($savePath, $sessionName) { ... }
   public function close() {
      if ($this->type == static::FILES) {
         return true;
      } elseif ($this->type == static::SQLITE) {
         return true;
      }
   }
   public function read($id) { ... }
   public function write($id, $data) { ... }
   public function destroy($id) { ... }
   public function gc($maxlifetime) { ... }
   static public function get($key) {
      return (isset($_SESSION[$key]))? $_SESSION[$key] : null;
   }
   static public function set($key, $value) {
      return $_SESSION[$key] = $value;
   }
   static public function newId() {...}
   static public function start($call = null, $log = false) {
      //1. start session (send 1st header)
      if (session_status() != PHP_SESSION_ACTIVE) {
         session_start();   //calls: open()->read()
      }

      //2. $_SESSION['session']: array of session control data
      // existed session
      if (is_array(static::get('session'))) {
         $session = static::get('session');
      // new session
      } else {
         $session = array();
      }

      $tmp = $_SESSION;
      //do sth with $session array...
      static::set('session', $session);
      session_write_close();   //calls: write()->read()->close()
      //create a new session inside if...else...
      session_id(static::newId());
      session_start();   //calls: open()->read()
      //if you want previous session data to be copied:
      //$_SESSION = $tmp;
      //do sth else with $session array and save it to new session...
      static::set('session', $session);

      //6. call callback function (only on valid/new sessions)
      if ($call)
         $call();
      session_write_close();   //calls: write()->read()->close()
   }
   /**
    * Defines custom session handler.
    */
   static public function setHandler() {
      // commit automatic session
      if (ini_get('session.auto_start') == 1) {
         session_write_close();
      }
      $handler = static::getInstance();
      session_set_save_handler($handler, true);
   }
}

Let's start a session:
Session::setHandler();
Session::start();

Trying for hours to trace my error where the 3rd Session::read() ended to use a null Session::dbh until I realized that Session::close() should NOT destroy properties of this class!
Also I avoid the use of session_create_id() as it's only for PHP 7 >= 7.1.0 and I use in place a static Session::newId().
tony at marston-home dot demon dot co dot uk ¶
2 years ago
Your custom session handler should not contain calls to any of the session functions, such as session_name() or session_id(), as the relevant values are passed as arguments on various handler methods. Attempting to obtain values from alternative sources may not work as expected.
dimzon541 at gmail dot com ¶
5 years ago
Persisting PHP sessions into mongodb (allows NLB without affinity)
https://gist.github.com/dimzon/62eeb9b8561bcb9f0c6d
nickleus ¶
3 years ago
i dont see any mention of what happens when eg "open" calls "die", like mentioned in docs for "register_shutdown_function":

"If you call exit() within one registered shutdown function, processing will stop completely and no other registered shutdown functions will be called."

http://php.net/manual/en/function.register-shutdown-function.php

my result: same behavior--"read" will not get called if "open" calls "die"/"exit".
shanikawm at gmail dot com ¶
4 years ago
Here is a class to handle session using an Oracle table.
https://github.com/shanikawm/PHP_Oracle_Based_Session_Handler_Class

<?php
/**
* By Shanika Amarasoma
* Date: 6/24/2016
* PHP session handler using Oracle database
* Oracle Create table statement
        CREATE TABLE PHP_SESSIONS
        (
            SESSION_ID  VARCHAR2(256 BYTE) UNIQUE,
            DATA        CLOB,
            TOUCHED     NUMBER(38)
        );
*/
class session_handler implements SessionHandlerInterface
{
    private 
$con;
    public function 
__construct() {
        if(!
$this->con=oci_pconnect(DBUSER,DBPASS,CONNECTION_STR)){
            die(
'Database connection failed !');
        }
    }
    public function 
open($save_path ,$name){
        return 
true;
    }
    public function 
close(){
        return 
true;
    }
    public function 
read($session_id){
        
$query "SELECT \"DATA\" FROM PHP_SESSIONS WHERE SESSION_ID=Q'{" $session_id "}'";
        
$stid oci_parse($this->con$query);
        
oci_execute($stidOCI_DEFAULT);
        
$row oci_fetch_array($stidOCI_ASSOC OCI_RETURN_LOBS);
        
oci_free_statement($stid);
        return 
$row['DATA'];
    }
    public function 
write($session_id,$session_data){
        
$dquery="DELETE FROM PHP_SESSIONS WHERE SESSION_ID=Q'{".$session_id."}'";
        
$dstid oci_parse($this->con,$dquery);
        
oci_execute($dstidOCI_NO_AUTO_COMMIT);
        
oci_free_statement($dstid);
        
$query="INSERT INTO PHP_SESSIONS(SESSION_ID,TOUCHED,\"DATA\") VALUES(Q'{".$session_id."}',".time().",EMPTY_CLOB()) RETURNING \"DATA\" INTO :clob";
        
$stid oci_parse($this->con,$query);
        
$clob=oci_new_descriptor($this->con,OCI_D_LOB);
        
oci_bind_by_name($stid':clob'$clob, -1OCI_B_CLOB);
        if(!
oci_execute($stidOCI_NO_AUTO_COMMIT)){
            @
oci_free_statement($stid);
            return 
false;
        }
        if(
$clob->save($session_data)){
            
oci_commit($this->con);
            
$return=true;
        } else {
            
oci_rollback($this->con);
            
$return=false;
        }
        
$clob->free();
        
oci_free_statement($stid);
        return 
$return;
    }
    public function 
destroy($session_id){
        
$query="DELETE FROM PHP_SESSIONS WHERE SESSION_ID=Q'{".$session_id."}'";
        
$stid oci_parse($this->con,$query);
        
oci_execute($stidOCI_DEFAULT);
        
$rows=oci_num_rows($stid);
        
oci_commit($this->con);
        
oci_free_statement($stid);
        if(
$rows>0){
            return 
true;
        } else {
            return 
false;
        }
    }
    public function 
gc($maxlifetime){
        
$query="DELETE FROM PHP_SESSIONS WHERE TOUCHED<".(time()-$maxlifetime);
        
$stid oci_parse($this->con,$query);
        
oci_execute($stidOCI_DEFAULT);
        
$rows=oci_num_rows($stid);
        
oci_commit($this->con);
        
oci_free_statement($stid);
        if(
$rows>0){
            return 
true;
        } else {
            return 
false;
        }
    }
}
session_set_save_handler(new session_handler(), true);
session_start();
bart2yk at yahoo dot com ¶
10 years ago
You can call the session_write in db object destructor to be shore that you still have a connection to mysql and the session is write.
skds1433 at hotmail dot com ¶
11 years ago
I pulled a really stupid move. If you are trying to debug your garbage collector, make sure you call the following >>> BEFORE <<< "session_start":

<?php
ini_set
('session.gc_probability'100);
ini_set('session.gc_divisor'100);
?>

I was sure it was a bug in PHP, but turned out (like 99% of the time) to be me own fault.
anonymous at anonymous dot org ¶
11 years ago
if you simply append the information from session variables every time you'll have many multiples for variables each time they are changed. a simple way to do this is explode the data twice to seperate the variable name from the other relevant information and foreach() check against the stored set. here is a little bit of a mess i wrote to do it.
assuming stored session variables in both database and passed through function:

<?php
$buffer 
= array();
$buffer explode('|',$sessiondata);
$buf1 = array();
$buf2 = array();
$finalbuff '';
foreach(
$buffer as $i){
    
$i explode(';',$i);
    foreach(
$i as $b){
        
array_push($buf1,$b);
    }
}
$buffer explode('|',$result['data']);
foreach(
$buffer as $i){ $i explode(';',$i); foreach($i as $b){ array_push($buf2,$b);}}
$z 0;
$x 0;
while(
$buf2[$z]){
    while(
$buf1[$x]){
        if(
$buf2[$z] == $buf1[$x]){
            
$buf2[($z+1)] = $buf1[($x+1)];
        }
        
$x+=2;
    }
    
$z+=2;
}
foreach(
$buf2 as $i){ $finalbuff .= $i; }
?>

$sessiondata is the variable passed through the function and $result['data'] is the data stored in an sql database.
mixailo at mercenaries dot ru ¶
12 years ago
It is useful to use MEMORY storage engine in MySQL while handling sessions.
http://dev.mysql.com/doc/refman/5.0/en/memory-storage-engine.html
sneakyimp AT hotmail DOT com ¶
14 years ago
the behavior, return values, and exact time of calling for these functions is pretty poorly documented here.  i thought folks might like to know that:

1) calling session_start() triggers PHP to first call your open function and then call your read function before resuming with the code immediately following your session_start() call.

2) calling session_id('some_value') within your open function WILL NOT SET THE SESSION COOKIE (at least not on my setup - PHP 4.4.1).  Assuming you defined some function to validate a session id called my_func(), you might want to do something like this in your open function:

<?php
  
function _open($save_path$session_name) {
    
// check for session id
    
$sess_id session_id();
    if (empty(
$sess_id) || !myfunc($sess_id)) {
      
//session_id is INVALID - generating new
      
$new_id md5(uniqid("some random seed here"));
      
session_id($new_id);
      
setcookie(session_name(),
                    
$new_id,
                    
0,
                    
"/",
                    
".mydomain.com");
      }

    return 
true;
  } 
// _open()
?>
Balu ¶
16 years ago
If a session is closed the save-handlers seem to be resetted (in PHP 4.1.2 they are), so you need to run session_set_save_handler() again after e.g. running session_write_close() and restarting the session with session_start();
spam at skurrilo dot de ¶
18 years ago
You can't use the session autostart feature with

session.save_handler = user

set in your php.ini. Use instead the auto_prepend_file directive in the php.ini and point it to your save_handler with an session_start() at the end.
Colin ¶
13 years ago
When using a custom session handler, if the first callback function (sessOpen in my case) finds no session id, one is set by the time the second argument (sessRead in my case) is called.
coco at digitalco2 dot com ¶
16 years ago
When using mySQL for your session handling functions, don't forget to call mysql_select_db() to change the database if you are using a separate database for your session data. Call mysql_select_db() INSIDE every handler function that accesses the database, since if you write session data after accessing another database, it will not change the database to your session database, and therefore, not write the session data.
cmanley ¶
7 years ago
Below is a session id value validator I just wrote. It is especially important to validate session id cookie values when using a custom file based validator, otherwise hackers could potentially trick it into overwriting non-session files.

/**
* Validates the value (the session id) of a session cookie.
* Useful for detecting potential hack attempts.
* It is up to the caller to delete the cookie if necessary.
* See also: http://lxr.php.net/xref/PHP_TRUNK/ext/session/session.c#php_session_valid_key
*
* @param string $value
* @param boolean $debug
* @return boolean
*/
function session_validate($cookie_value, $debug = false) {
    // session.hash_function allows you to specify the hash algorithm used to generate the session IDs. '0' means MD5 (128 bits) and '1' means SHA-1 (160 bits). Since PHP 5.3.0 it is also possible to specify any of the algorithms provided by the hash extension (if it is available), like sha512 or whirlpool. A complete list of supported algorithms can be obtained with the hash_algos() function.
    // session.hash_bits_per_character allows you to define how many bits are stored in each character when converting the binary hash data to something readable. The possible values are '4' (0-9, a-f), '5' (0-9, a-v), and '6' (0-9, a-z, A-Z, "-", ",").
    if (!(isset($cookie_value) && is_string($cookie_value) && strlen($cookie_value))) {
        return false;
    }
    $bits = null;
    if (1) {
        $hash_function = ini_get('session.hash_function');
        $hash_function_to_bits = array(
            0    => 128,
            1    => 160,
        );
        $bits = @$hash_function_to_bits[$hash_function];
    }
    $bits_per_char = ini_get('session.hash_bits_per_character');
    $bits_per_char_to_charclass = array(
        4    => '0-9a-f',
        5    => '0-9a-v',
        6    => '0-9a-zA-Z\-,',    // this is also the default
    );
    $charclass = array_key_exists($bits_per_char, $bits_per_char_to_charclass) ? $bits_per_char_to_charclass[$bits_per_char] : $bits_per_char_to_charclass[6];
    $charlength = $bits ? (integer)ceil($bits / $bits_per_char) : '1,128'; // the last value is a somewhat arbitrary default
    $re = '/^[' . $charclass . ']{' . $charlength . '}$/';
    $result = preg_match($re, $cookie_value);
    $debug && error_log(__FUNCTION__ . ' regexp: ' . $re . "\tresult: " .intval($result));
    return $result;
}
jamesbenson944 at hotmail dot com ¶
7 years ago
I'm not using objects for the save handlers I'm using functions but still get weird behaviour with session writing not being called.

This fixes the problem though:
register_shutdown_function('session_write_close');
mjohnson at pitsco dot com ¶
14 years ago
With regards to the read handler, the docs say:

  "Read function must return string value always to make save
  handler work as expected. Return empty string if there is no
  data to read."

I can't emphasize this enough. I just spent half a day trying to figure out why my sessions weren't storing any information. I was blithely returning the results of a query on the database from the read handler. Since there was no match for the new ID, the result was NULL. Since it wasn't a string, sessions were essentially disabled. So, the safe thing might be something like this:

<?php
function sessRead($id)
{
    
// Look up data
    
$results getStuff($id);
   
    
// Make sure it's a string
    
settype($results'string');
    return 
$results;
}
?>

Of course, you can do whatever you want with it. But, no matter what, make sure you return a string.

HTH,
Michael

来自   https://www.php.net/manual/zh/function.session-set-save-handler.php



利用session_set_save_handler()函数将session保存到MySQL数据库中

PHP保存session默认的是采用的文件的方式来保存的,这仅仅在文件的空间开销很小的windows上是可以采用的,但是如果我们采用uinx或者是liux上的文件系统的时候,这样的文件系统的文件空间开销是很大的,然而session是要时时刻刻的使用的,大量的用户就要创建很多的session文件,这样对整个的服务器带来性能问题。

另一方面,如果服务器起采用群集的方式的话就不能保持session的一致性,所以我们就绪要采用数据库的方式来保存session,这样,不管有几台服务器同时使用,只要把他们的session保存在一台数据库服务器上就可以保证session的完整了,具体如何来实现请继续看下去。

PHP保存session默认的情况下是采用的文件方式来保存的,我们在PHP的配制文件PHP.ini中可以看到这样的一行,

session.save_handler="files"

这样的意思就是采用文件来保存session 的,要采用数据库来保存的话,我们需要修改成用户模式,改成

session.save_handler="use"

就可以了,但是,这仅仅是说明我门没有采用文件的方式存储session,我们还要选择数据库和建立数据库的表。

建立数据库和数据库的表结构,我们可以采用PHP可以使用的任何的数据库,因为PHP和mysql的结合最好,我就使用mysql来做示例,当然根据你的需要可以改称别的数据库。

创建数据库

create database 'session';

创建表结构

create table 'session'( id char(32) not null , 'user 'char(30), data char(3000) ,primary key ('id') );

PHP保存session编写PHP文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php
$con = mysql_connect("127.0.0.1""user" "pass");
mysql_select_db("session");
 
function open($save_path$session_name) {
    return(true);
}
 
function close() {
    return(true);
}
 
function read($id) {
    if ($result = mysql_query("select * from session where id='$id'")) {
        if ($row = mysql_felth_row($result)) {
            return $row["data"];
        }
    else {
        return "";
    }
}
 
function write($id$sess_data) {
    if ($result = mysql_query("update session set data='$sess_data' where id='$id'")) {
        return true;
    else {
        return false;
    }
}
 
function destroy($id) {
    if ($result = mysql_query("delete * from session where id='$id'")) {
        return true;
    else {
        return false;
    }
}
 
function gc($maxlifetime) {
    return true;
}
session_set_save_handler("open""close""read""write""destroy""gc");
session_start();
// proceed to use sessions normally
?>

保存成为session_user_start.php。

现在我们的PHP保存session的工作就已经完成了,只要你在需要在使用session的时候,把session_user_start.php包含进来.注意,这个文件一定要在文件的第一行包含,然后就像使用文件的session一样的方法使用就可以了。

 


 

以上仅仅是个简单教程,在实际的应用中,可以对它封装得更专业些,参考代码如下:

SessionMysql.class.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
<?php
/**
 * SessionMysql 数据库存储类
 */
 
defined('IN_QIAN'or exit('Access Denied');
 
class SessionMysql {
 
    public $lifetime = 1800; // 有效期,单位:秒(s),默认30分钟
    public $db;
    public $table;
 
    /**
     * 构造函数
     */
    public function __construct() {
        $this->db = Base::loadModel('SessionModel');
        $this->lifetime = Base::loadConfig('system''session_lifetime');
        session_set_save_handler(
            array(&$this'open'),      // 在运行session_start()时执行
            array(&$this'close'),     // 在脚本执行完成 或 调用session_write_close() 或 session_destroy()时被执行,即在所有session操作完后被执行
            array(&$this'read'),      // 在运行session_start()时执行,因为在session_start时,会去read当前session数据
            array(&$this'write'),     // 此方法在脚本结束和使用session_write_close()强制提交SESSION数据时执行
            array(&$this'destroy'),   // 在运行session_destroy()时执行
            array(&$this'gc')         // 执行概率由session.gc_probability 和 session.gc_divisor的值决定,时机是在open,read之后,session_start会相继执行open,read和gc
        );
        session_start(); // 这也是必须的,打开session,必须在session_set_save_handler后面执行
    }
 
    /**
     * session_set_save_handler open方法
     *
     * @param $savePath
     * @param $sessionName
     * @return true
     */
    public function open($savePath$sessionName) {
        return true;
    }
 
    /**
     * session_set_save_handler close方法
     *
     * @return bool
     */
    public function close() {
        return $this->gc($this->lifetime);
    }
 
    /**
     * 读取session_id
     *
     * session_set_save_handler read方法
     * @return string 读取session_id
     */
    public function read($sessionId) {
        $condition array(
            'where' => array(
                'session_id' => $sessionId
            ),
            'fields' => 'data'
        );
        $row $this->db->fetchFirst($condition);
        return $row $row['data'] : '';
    }
 
    /**
     * 写入session_id 的值
     *
     * @param $sessionId 会话ID
     * @param $data 值
     * @return mixed query 执行结果
     */
    public function write($sessionId$data) {
        $userId = isset($_SESSION['userId']) ? $_SESSION['userId'] : 0;
        $roleId = isset($_SESSION['roleId']) ? $_SESSION['roleId'] : 0;
        $grouId = isset($_SESSION['grouId']) ? $_SESSION['grouId'] : 0;
        $m = defined('ROUTE_M') ? ROUTE_M : '';
        $c = defined('ROUTE_C') ? ROUTE_C : '';
        $a = defined('ROUTE_A') ? ROUTE_A : '';
        if (strlen($data) > 255) {
            $data '';
        }
        $ip = get_ip();
        $sessionData array(
            'session_id'    => $sessionId,
            'user_id'       => $userId,
            'ip'            => $ip,
            'last_visit'    => SYS_TIME,
            'role_id'       => $roleId,
            'group_id'      => $grouId,
            'm'             => $m,
            'c'             => $c,
            'a'             => $a,
            'data'          => $data,
        );
        return $this->db->insert($sessionData, 1, 1);
    }
 
    /**
     * 删除指定的session_id
     *
     * @param string $sessionId 会话ID
     * @return bool
     */
    public function destroy($sessionId) {
        return $this->db->delete(array('session_id' => $sessionId));
    }
 
    /**
     * 删除过期的 session
     *
     * @param $lifetime session有效期(单位:秒)
     * @return bool
    */
    public function gc($lifetime) {
        $expireTime = SYS_TIME - $lifetime;
        return $this->db->delete("`last_visit`<$expireTime");
    }
}
?>

在系统文件的某个地方,实例化这个类即可!!!

new SessionMysql();

 

延伸阅读:PHP如何将session保存到memcached中?

来自  https://www.cnblogs.com/52php/p/5665460.html




php 深入理解session_set_save_handler执行机制

session handler默认启动顺序是session_start分别调用的回调函数。为open read ,然后等待脚本结束,收集$_SESSION(默认在内存中),然后关闭脚本,然后执行write,写入文件,然后close。

如何改变session handler默认启动顺序呢?

我们可以使用 session_commit(); 或者 session_write_close();函数调用的时候,session的机制就改变了,直接写入文件,关闭文件。然后再执行脚本。

 

下面来看一下PHP 使用 session_set_save_handler() 对 Session 进行自定义处理

  1. /*
  2. Session open (called by session_start( ))
  3. Session close (called at page end)
  4. Session read (called after session_start( ) )
  5. Session write (called when session data is to be written)
  6. Session destroy (called by session_destroy( ) )
  7. Session garbage collect (called randomly)
  8. */
  9. <?
  10. function sess_open($sess_path, $sess_name) {
  11. print "Session opened.\n";
  12. print "Sess_path: $sess_path\n";
  13. print "Sess_name: $sess_name\n\n";
  14. return true;
  15. }
  16. function sess_close( ) {
  17. print "Session closed.\n";
  18. return true;
  19. }
  20. function sess_read($sess_id) {
  21. print "Session read.\n";
  22. print "Sess_ID: $sess_id\n";
  23. return '';
  24. }
  25. /* http://www.manongjc.com/article/1293.html */
  26. function sess_write($sess_id, $data) {
  27. print "Session value written.\n";
  28. print "Sess_ID: $sess_id\n";
  29. print "Data: $data\n\n";
  30. return true;
  31. }
  32. function sess_destroy($sess_id) {
  33. print "Session destroy called.\n";
  34. return true;
  35. }
  36. function sess_gc($sess_maxlifetime) {
  37. print "Session garbage collection called.\n";
  38. print "Sess_maxlifetime: $sess_maxlifetime\n";
  39. return true;
  40. }
  41. session_set_save_handler("sess_open", "sess_close", "sess_read",
  42. "sess_write", "sess_destroy", "sess_gc");
  43. session_start( );
  44. $_SESSION['foo'] = "bar";
  45. print "Some text\n";
  46. $_SESSION['baz'] = "wombat";
  47. ?>
  1. 1. 返回值的问题,为什么每个回调函数都有个retrun 的bool值?
  2. 2. 返回值的问题,比如read函数  返回一个false 会怎么样?true又怎么样
1. 返回值的问题,为什么每个回调函数都有个retrun 的bool值? Return Values Returns TRUE on success or FALSE on failure. 这个是函数的定义值。如果你不想用,可以不用catch他。 如果需要,这样写 A = session_set_save_handler() 返回值的问题,比如read函数 返回一个false 会怎么样?true又怎么样? 你可以结合IF来使用 比如 IF(session_set_save_handler()) 意思就是如果函数真.... 



session_set_save_handler---设置用户级 session 存储函数

函数原型 

void session_set_save_handler (string openstring closestring readstring writestring destroy, string gc)

session_set_save_handler() 设置用户级 session 存储函数,用于存储和取回 session 相关的数据. 用于那些使用不同于 PHP Session 指定的存储方式的情况. 例如,在本地数据库存储 session 数据. 

注意: 你必须设置 php.ini 里面的 session.save_handler配置参数来让 session_set_save_handler() 正常工作. 

下面的例子提供了类似于 PHP 默认的保存文件句柄的基于文件的 session storage 方式. 这个例子可以很简单的扩展到使用熟悉的数据库引擎来进行数据库存储. 
例子:

程序代码:[session_inc.php]

  1. <?php 
  2. $SESS_DBHOST = "yourhost"; /* database server hostname */ 
  3. $SESS_DBNAME = "yourdb"; /* database name */ 
  4. $SESS_DBUSER = "youruser"; /* database user */ 
  5. $SESS_DBPASS = "yourpassword"; /* database password */ 
  6. $SESS_DBH = ""
  7. $SESS_LIFE = get_cfg_var("session.gc_maxlifetime"); 
  8. function sess_open($save_path, $session_name)
  9.     global $SESS_DBHOST, $SESS_DBNAME, $SESS_DBUSER, $SESS_DBPASS, $SESS_DBH; 
  10.     if (! $SESS_DBH = mysqli_pconnect($SESS_DBHOST, $SESS_DBUSER, $SESS_DBPASS,$SESS_DBNAME)) { 
  11.         echo "<li>Can't connect to $SESS_DBHOST as $SESS_DBUSER"
  12.         echo "<li>MySQL Error: " . mysql_error(); 
  13.         die
  14.     } 
  15.   
  16.     return true
  17. function sess_close()
  18. global $SESS_DBH;
  19.     return mysqli_close($SESS_DBH); 
  20. function sess_read($key)
  21.     global $SESS_DBH, $SESS_LIFE; 
  22.     $qry = "SELECT value FROM session_tbl WHERE sesskey = '$key' AND expiry > " . time(); 
  23.     $qid = mysqli_query($SESS_DBH,$qry); 
  24.     if (list($value) = mysqli_fetch_array($qid,MYSQLI_NUM)) { 
  25.         return $value; 
  26.     } 
  27.     return false
  28. function sess_write($key, $val)
  29.     global $SESS_DBH, $SESS_LIFE; 
  30.     $expiry = time() + $SESS_LIFE; //过期时间 
  31.     $value = addslashes($val); 
  32.     $qry = "INSERT INTO session_tbl VALUES ('$key', $expiry, '$value')"
  33.     $qid = mysqli_query($SESS_DBH,$qry); 
  34.     if (! $qid) { 
  35.         $qry = "UPDATE session_tbl SET expiry = $expiry, value = '$value' WHERE sesskey = '$key' AND expiry > " . time(); 
  36.         $qid = mysqli_query($SESS_DBH,$qry); 
  37.     } 
  38.     return $qid; 
  39. function sess_destroy($key)
  40.     global $SESS_DBH; 
  41.     $qry = "DELETE FROM session_tbl WHERE sesskey = '$key'"
  42.     $qid = mysqli_query($SESS_DBH,$qry); 
  43.     return $qid; 
  44. function sess_gc($maxlifetime)
  45.     global $SESS_DBH; 
  46.     $qry = "DELETE FROM session_tbl WHERE expiry < " . time(); 
  47.     $qid = mysqli_query($SESS_DBH,$qry); 
  48.     return true
  49. session_set_save_handler( 
  50. "sess_open"
  51. "sess_close"
  52. "sess_read"
  53. "sess_write"
  54. "sess_destroy"
  55. "sess_gc"); 
  56. session_start(); 
  57. ?>

完成以上步骤后,在程序中使用require("session_inc.php")来代替session_start()即可,其他的session函数还是象以前一样的方法调用

原文地址http://www.manongjc.com/article/1293.html


来自  https://blog.csdn.net/u010433704/article/details/95355186

普通分类: