PHPのお勉強!

PHP TOP

session_set_save_handler

(PHP 4, PHP 5, PHP 7, PHP 8)

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

次のプロトタイプでも登録できます。

session_set_save_handler(object $sessionhandler, bool $register_shutdown = true): bool

session_set_save_handler() は、セッションに 関連するデータを保存および取得するために使用されるユーザー定義の セッション保存関数を設定します。この関数は、PHP セッションにより 提供されるもの以外の保存方法を使用したい場合に有用です。 例えば、セッションデータをローカルデータベースに保存します。

パラメータ

この関数には二種類のプロトタイプがあります。

sessionhandler

SessionHandlerInterfaceSessionIdInterface(オプション) または SessionUpdateTimestampHandlerInterface を実装したクラス、たとえば SessionHandler のインスタンスで、 これをセッションハンドラとして登録する。

register_shutdown

session_write_close()register_shutdown_function() 関数として登録するかどうか。

あるいは
open

以下のシグネチャを持つ callable:

open(string $savePath, string $sessionName): bool

open コールバックはクラスのコンストラクタのようなもので、セッションを開くときに実行されます。 セッションが自動で開始したり、あるいは手動で session_start() で開始させたりするときに、最初に実行されるコールバック関数がこれです。 成功した場合は true、失敗した場合は false を返します。

close

以下のシグネチャを持つ callable:

close(): bool

close コールバックはクラスのデストラクタのようなもので、write コールバックがコールされた後で実行されます。 また、session_write_close() がコールされたときにも実行されます。 成功した場合は true、失敗した場合は false を返します。

read

以下のシグネチャを持つ callable:

read(string $sessionId): string

read コールバックは、常にセッションエンコード (シリアライズ) された文字列を返さなければなりません。何もデータを読み込まなかった場合は空文字列を返します。

このコールバックは、セッションが開始したときや session_start() がコールされたときに PHP が内部的に実行します。 このコールバックを実行する前に、PHP は open コールバックを実行します。

このコールバックが返す値は、 write コールバックがストレージに渡した形式とまったく同じシリアライズ形式でなければなりません。 返された値を PHP が自動的にアンシリアライズして、スーパーグローバル $_SESSION に格納します。データの形式は serialize() したものと似ていますが、実際は違う形式であることに注意しましょう。 シリアライズの方法は ini 設定 session.serialize_handler で指定します。

write

以下のシグネチャを持つ callable:

write(string $sessionId, string $data): bool

write コールバックは、セッションの保存や終了が必要となったときにコールされます。 このコールバックが受け取るのは、現在のセッション ID とシリアライズ後のスーパーグローバル $_SESSION です。 PHP が内部で利用するシリアライズ方法は、ini 設定 session.serialize_handler で指定します。

このコールバックに渡されたシリアライズ後のセッションデータを、 渡されたセッション ID に対応させて格納しなければなりません。 このデータを取得した read コールバックは、 write コールバックに最初に渡されたのとまったく同じ値を返さなければなりません。

このコールバックが実行されるのは、PHP のスクリプトが終了するときか、あるいは明示的に session_write_close() がコールされたときです。この関数の実行後には、PHP が内部的に close コールバックを実行することに注意しましょう。

注意:

"write" ハンドラは、出力ストリームが閉じてから実行されます。 したがって、"write" ハンドラ内でデバッグ出力を行っても、 それはブラウザに表示されません。 デバッグ出力が必要なら、それをファイルに書き出すようにしましょう。

destroy

以下のシグネチャを持つ callable:

destroy(string $sessionId): bool

このコールバックが実行されるのは、 session_destroy() あるいは session_regenerate_id() (destroy パラメータを true にした場合) を実行し、セッションを破棄した場合です。 成功した場合は true、失敗した場合は false を返します。

gc

以下のシグネチャを持つ callable:

gc(int $lifetime): bool

ガベージコレクタコールバックは PHP が内部で定期的に実行し、古いセッションデータを破棄します。 実行頻度は session.gc_probability および session.gc_divisor で設定します。 この関数に渡される有効期限の値は session.gc_maxlifetime で設定できます。 成功した場合は true、失敗した場合は false を返します。

create_sid

以下のシグネチャを持つ callable:

create_sid(): string

このコールバックは、新しいセッション ID が必要になったときに実行されます。 パラメータは不要です。戻り値は、使っているハンドラで有効なセッション ID を表す文字列となります。

validate_sid

以下のシグネチャを持つ callable:

validate_sid(string $key): bool

このコールバックは、セッションが開始された場合に実行されます。 但し、セッションID が渡され、 session.use_strict_mode が有効な場合に限ります。 key は検証する セッションID を指定します。 渡されたID のセッションが既に存在する場合、セッションID は有効です。 成功した場合は true、失敗した場合は false を返します。

update_timestamp

以下のシグネチャを持つ callable:

update_timestamp(string $key, string $val): bool

このコールバックは、セッションが更新された時に実行されます。 key はセッションID、 val はセッションデータを指定します。 成功した場合は true、失敗した場合は false を返します。

戻り値

成功した場合に 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() でセッションを閉じます。

参考

add a note

User Contributed Notes 41 notes

up
48
andreipa at gmail dot com
9 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($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!
?>
up
4
ohcc at 163 dot com
7 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.
up
3
peter at brandrock dot co dot za
6 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.
up
14
ohcc at 163 dot com
7 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 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
// ...
}
}
?>
up
3
polygon dot co dot in at gmail dot com
3 years ago
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
up
5
Steven George
10 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"