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:

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).

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:

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 )

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.

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).

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:<?phpnew SessionSaveHandler();?>and the handler will rule the sessions ;-)<?php

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:

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.

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 // ...} }?>

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.

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.

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().

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"

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.

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.

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:

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.

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:

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();

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.

"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:

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.

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.

Many people are using session_set_save_handler to store the session in a session database, which ofcourse is both valid and smart since it (could) increas security.

What many people forgets, is that session ids can easily be edited by a user as he see fit (by editing a session_cookie for example*)

* If you like to play around to test your site, check Add n edit Cookies extension for firefox.

This might not be a big deal when saving them in a file, since the worst thing that may happen is that the user losts his session and a new one is generated. But when saving to an DB it is*. One should never trust that the server itself add slashes and escapes other vital characters.

Ever since we upgraded to PHP 5.3 we have had issues with session storage using files.

Regularly when session garbage collection was triggered, permissions errors would be thrown.

This is due to the fact that garbage collection is triggered by visitors to a website based on the session.gc_probability and session.gc_divisor settings which factor the chance of garbage collection running.

As these processes are owned by Apache but the directory in which the files reside is owned and readable only by root, the Apache processes clearly cannot list the files in the session directory to work out what to delete.

Changing the ownership and permissions on the directory is a no-no for security reasons, which leaves you with the option of turning off PHP based garbage collection and writing your own, or moving away from using files.

For some reason, when we turned off garbage (session.gc_probability = 0) the garbage collection still ran anyway, so we decided to move away from files.

The initial approach which most people on php.net seem to take is to write a custom session handler class in PHP using MySQL, which is not a bad approach as you can use a memory based MySQL table to gain performance. However it requires you to write code, and also to modify your website to put in the session handler override code. If you have lots of website to change this is out of the question.

The simplest solution we found, which in the end took about 2 minutes in total, and immediately worked for all websites on the server, was to use sqlite.

Some PHP installations already have SQLite installed and when you run php -i on the command line it shows sqlite as a "Registered save handler".

If sqlite is not installed as a registered save handler, you need to install it. On CentOS/RHEL this was simply a case of the following command: yum install php-sqlite. Once done, check the registered save handlers again with the php -i command.

Once sqlite shows as a registered save handler, you simply have to edit your php.ini file to change the following two settings: