session_set_save_handler
(PHP 4, PHP 5, PHP 7, PHP 8)
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
次のプロトタイプでも登録できます。
session_set_save_handler() は、セッションに 関連するデータを保存および取得するために使用されるユーザー定義の セッション保存関数を設定します。この関数は、PHP セッションにより 提供されるもの以外の保存方法を使用したい場合に有用です。 例えば、セッションデータをローカルデータベースに保存します。
パラメータ
この関数には二種類のプロトタイプがあります。
sessionhandler
-
SessionHandlerInterface、 SessionIdInterface(オプション) または SessionUpdateTimestampHandlerInterface を実装したクラス、たとえば SessionHandler のインスタンスで、 これをセッションハンドラとして登録する。
register_shutdown
-
session_write_close() を register_shutdown_function() 関数として登録するかどうか。
open
-
以下のシグネチャを持つ callable:
open コールバックはクラスのコンストラクタのようなもので、セッションを開くときに実行されます。 セッションが自動で開始したり、あるいは手動で session_start() で開始させたりするときに、最初に実行されるコールバック関数がこれです。 成功した場合は
true
、失敗した場合はfalse
を返します。 close
-
以下のシグネチャを持つ callable:
close(): boolclose コールバックはクラスのデストラクタのようなもので、write コールバックがコールされた後で実行されます。 また、session_write_close() がコールされたときにも実行されます。 成功した場合は
true
、失敗した場合はfalse
を返します。 read
-
以下のシグネチャを持つ callable:
read
コールバックは、常にセッションエンコード (シリアライズ) された文字列を返さなければなりません。何もデータを読み込まなかった場合は空文字列を返します。このコールバックは、セッションが開始したときや session_start() がコールされたときに PHP が内部的に実行します。 このコールバックを実行する前に、PHP は
open
コールバックを実行します。このコールバックが返す値は、
write
コールバックがストレージに渡した形式とまったく同じシリアライズ形式でなければなりません。 返された値を PHP が自動的にアンシリアライズして、スーパーグローバル $_SESSION に格納します。データの形式は serialize() したものと似ていますが、実際は違う形式であることに注意しましょう。 シリアライズの方法は ini 設定 session.serialize_handler で指定します。 write
-
以下のシグネチャを持つ callable:
write
コールバックは、セッションの保存や終了が必要となったときにコールされます。 このコールバックが受け取るのは、現在のセッション ID とシリアライズ後のスーパーグローバル $_SESSION です。 PHP が内部で利用するシリアライズ方法は、ini 設定 session.serialize_handler で指定します。このコールバックに渡されたシリアライズ後のセッションデータを、 渡されたセッション ID に対応させて格納しなければなりません。 このデータを取得した
read
コールバックは、write
コールバックに最初に渡されたのとまったく同じ値を返さなければなりません。このコールバックが実行されるのは、PHP のスクリプトが終了するときか、あるいは明示的に session_write_close() がコールされたときです。この関数の実行後には、PHP が内部的に
close
コールバックを実行することに注意しましょう。注意:
"write" ハンドラは、出力ストリームが閉じてから実行されます。 したがって、"write" ハンドラ内でデバッグ出力を行っても、 それはブラウザに表示されません。 デバッグ出力が必要なら、それをファイルに書き出すようにしましょう。
destroy
-
以下のシグネチャを持つ callable:
このコールバックが実行されるのは、 session_destroy() あるいは session_regenerate_id() (destroy パラメータを
true
にした場合) を実行し、セッションを破棄した場合です。 成功した場合はtrue
、失敗した場合はfalse
を返します。 gc
-
以下のシグネチャを持つ callable:
ガベージコレクタコールバックは PHP が内部で定期的に実行し、古いセッションデータを破棄します。 実行頻度は session.gc_probability および session.gc_divisor で設定します。 この関数に渡される有効期限の値は session.gc_maxlifetime で設定できます。 成功した場合は
true
、失敗した場合はfalse
を返します。 create_sid
-
以下のシグネチャを持つ callable:
create_sid(): stringこのコールバックは、新しいセッション ID が必要になったときに実行されます。 パラメータは不要です。戻り値は、使っているハンドラで有効なセッション ID を表す文字列となります。
validate_sid
-
以下のシグネチャを持つ callable:
このコールバックは、セッションが開始された場合に実行されます。 但し、セッションID が渡され、 session.use_strict_mode が有効な場合に限ります。
key
は検証する セッションID を指定します。 渡されたID のセッションが既に存在する場合、セッションID は有効です。 成功した場合はtrue
、失敗した場合はfalse
を返します。 update_timestamp
-
以下のシグネチャを持つ callable:
このコールバックは、セッションが更新された時に実行されます。
key
はセッションID、val
はセッションデータを指定します。 成功した場合はtrue
、失敗した場合はfalse
を返します。
例
例1 自作のセッションハンドラ: 完全なコードは SessionHandlerInterface を参照
ここでは実行するところだけを示します。完全な例は、上でリンクしている SessionHandlerInterface のページを参照ください。
session_set_save_handler() でオブジェクト指向型のプロトタイプを使っていることと、 シャットダウン関数をその parameter フラグで登録していることに注目しましょう。 オブジェクトをセッション保存ハンドラとして使うときには、この方法をおすすめします。
<?php
class MySessionHandler implements SessionHandlerInterface
{
// ここでインターフェイスを実装します
}
$handler = new MySessionHandler();
session_set_save_handler($handler, true);
session_start();
// $_SESSION への値の設定や格納されている値の取得を進めます
注意
write
ハンドラおよび
close
ハンドラはオブジェクトが破棄されたあとにコールされます。
そのため、これらのハンドラでオブジェクトを使ったり例外をスローしたりすることはできません。
例外をキャッチできないのでその情報をトレースすることもできず、
例外は単純に無視されてしまいます。
しかし、オブジェクトのデストラクタではセッションを使えます。
この「ニワトリが先かタマゴが先か」の問題を解決するために、 デストラクタから session_write_close() をコールできますが、より確実なのは先述のとおりシャットダウン関数を登録することです。
SAPI の種類によっては、スクリプトの終了時にセッションを閉じると 現在の作業ディレクトリが変わってしまうことがあります。これを防ぐには、 事前に session_write_close() でセッションを閉じます。
参考
- 設定ディレクティブ session.save_handler
- 設定ディレクティブ session.serialize_handler
- register_shutdown_function() - シャットダウン時に実行する関数を登録する
- session_register_shutdown() - セッションのシャットダウン関数
- 手続き型の完全なリファレンス実装は » save_handler.inc を参照ください。
User Contributed Notes 41 notes
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($handler, true);
?>
<?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!
?>
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.
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.
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 SessionHandlerInterface, SessionUpdateTimestampHandlerInterface {
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
// ...
}
}
?>
Below is a demo to check the order in which session function executes.
<?php
ini_set('session.use_strict_mode',true);
function sess_open($sess_path, $sess_name) {
echo '<br/>sess_open';
return true;
}
function sess_close() {
echo '<br/>sess_close';
return true;
}
function sess_read($sess_id) {
echo '<br/>sess_read';
return '';
}
function sess_write($sess_id, $data) {
echo '<br/>sess_write';
return true;
}
function sess_destroy($sess_id) {
echo '<br/>sess_destroy';
return true;
}
function sess_gc($sess_maxlifetime) {
echo '<br/>sess_gc';
return true;
}
function sess_create_sid() {
echo '<br/>sess_create_sid';
return 'RNS'.rand(0,10);
}
function sess_validate_sid($sess_id) {
echo '<br/>sess_validate_sid';
return true;
}
function sess_update_timestamp($sess_id,$data) {
echo '<br/>sess_update_timestamp';
return true;
}
session_set_save_handler(
'sess_open',
'sess_close',
'sess_read',
'sess_write',
'sess_destroy',
'sess_gc',
'sess_create_sid',
'sess_validate_sid',
'sess_update_timestamp'
);
session_start();
echo '<br/>code here...';
?>
O/P Below when above code executed first time.
sess_open
sess_create_sid
sess_read
code here...
sess_write
sess_close
O/P Below for next execution.
sess_open
sess_validate_sid
sess_read
code here...
sess_write
sess_close
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"