The purpose of this document is to provide a complete overview of the PHP session
handler life cycle updated to version 7.0 or above. In particular, I want to
emphasize what methods and in what order are called when the native PHP functions
are used for session management.
I created this document because the information on the web and the official
documentation are very superficial on this topic, in particular on what
concerns the implementation of a safe and stable session handler.
There are three interfaces that can be implemented to create a session handler. The first one is mandatory and the other two are optional.
-
SessionHandlerInterface
(PHP 5 >= 5.4.0, PHP 7)
SessionHandlerInterface is an interface which defines a prototype for creating a custom session handler.-
open( string $savePath , string $sessionName ) : bool
Re-initialize existing session, or creates a new one. Called when a session starts or whensession_start()
is invoked.
Return value should betrue
for success orfalse
for failure. -
read ( string $sessionId ) : string
Reads the session data from the session storage, and returns the results. Before this method is calledSessionHandlerInterface::open()
is invoked.
The data returned by this method will be decoded internally by PHP using the unserialization method specified insession.serialize_handler
. The resulting data will be used to populate the$_SESSION
superglobal.
Return value should be the session data or an empty string. -
write ( string $sessionId , string $sessionData ) : bool
Writes the session data to the session storage.
SessionHandlerInterface::close()
is called immediately after this function. This method encodes the session data from the$_SESSION
superglobal to a serialized string and passes this along with the session ID to this method for storage. The serialization method used is specified in thesession.serialize_handler
ini setting.
Return value should betrue
for success orfalse
for failure. -
close ( void ) : bool
Closes the current session.
This function is automatically executed when closing the session, or explicitly viasession_write_close()
.
Return value should betrue
for success orfalse
for failure. -
destroy ( string $sessionId ) : bool
Destroys a session.
Called bysession_regenerate_id()
(with $destroy = TRUE),session_destroy()
and whensession_decode()
fails.
Return value should betrue
for success orfalse
for failure. -
gc ( int $maxlifetime ) : int
Cleans up expired sessions.
Called bysession_start()
, based onsession.gc_divisor
,session.gc_probability
andsession.gc_maxlifetime
settings.
Return value should betrue
for success orfalse
for failure.
-
-
SessionIdInterface
(PHP 5 >= 5.5.1, PHP 7)
SessionIdInterface is an additional interface that gives the possibility to manage the creation of a session ID in a personalized way.create_sid ( void ) : string
This method is invoked internally when a new session id is needed.
The returned ID should be generated checking for collision with other saved sessions. However, from PHP 7session_create_id ([ string $prefix ] ) : string
(documentation) is available. This function creates new collision free (if session is active) session ID for the current session in according to ini settings:session.sid_length
(length of session ID string) andsession.sid_bits_per_character
(number of bits in encoded session ID character).
No parameter is needed and return value should be the new session id created.
-
SessionUpdateTimestampHandlerInterface
(PHP 7)
SessionUpdateTimestampHandlerInterface is an additional interface that completes the functionalities of a session handler object.-
validateId( string $sessionId ) : bool
Validate session ID.
This method is called when thesession.use_strict_mode
ini setting is set to1
in order to avoid uninitialized session ID. The validity of session ID is checked on starting and on regenerating if strict mode is enabled.
Return value should betrue
if the session ID is valid otherwisefalse
. Iffalse
is returned a new session id will be generated. -
updateTimestamp( string $sessionId, string $sessionData ) : bool
Update timestamp of a session.
This method is called when thesession.lazy_write
ini setting is set to1
and no changes are made to session variables. In other words, when the session need to be closed, if lazy_write mode is enabled and$_SESSION
is not modified, this method is called instead ofSessionHandlerInterface::write()
in order to update session timestamp without rewriting all session data.
Return value should betrue
for success orfalse
for failure.
-
In this part I tried to translate the behavior of native PHP session function
from the point-of-view of a session handler.
Note that the code written below does not work at all, it has only to be
understanded such as an explanation of what PHP does when a custom session
handler is set and native session functions are invoked.
-
session_start();
(documentation)
Creates a session or resumes the current one based on a session identifier passed via a GET or POST request, or passed via a cookie.$savePath = ini_get('session.save_path'); $sessionName = ini_get('session.name'); SessionHandlerInterface::open($savePath, $sessionName); // find $sessionId from server request, for example from $_COOKIES superglobal. if (!isset($sessionId)) { $sessionId = SessionIdInterface::create_sid(); } if (ini_get('session.use_strict_mode') && !SessionUpdateTimestampHandlerInterface::validateId($sessionId)) { $sessionId = SessionIdInterface::create_sid(); } $data = SessionHandlerInterface::read($sessionId); // here PHP does Garbage Collection based on probability if (ini_get('session.serialize_handler') === 'php_serialize') { $_SESSION = unserialize($data); } else { session_decode($data); }
-
session_commit();
orsession_write_close();
(documentation)
End the current session and store session data.if (ini_get('session.serialize_handler') === 'php_serialize') { $sessionData = serialize($_SESSION); } else { $sessionData = session_encode(); } // if session.lazy_write is enabled and $_SESSION is NOT changed: SessionUpdateTimestampHandlerInterface::updateTimestamp($sessionId, $sessionData); // else: SessionHandlerInterface::write($sessionId, $sessionData); SessionHandlerInterface::close();
-
session_regenerate_id($deleteOldSession);
(documentation)
Update the current session id with a newly generated one.if ($deleteOldSession) { SessionHandlerInterface::destroy($sessionId); } else { SessionHandlerInterface::write($sessionId, $sessionData); } SessionHandlerInterface::close(); $savePath = ini_get('session.save_path'); $sessionName = ini_get('session.name'); SessionHandlerInterface::open($savePath, $sessionName); $sessionId = SessionIdInterface::create_sid(); if (ini_get('session.use_strict_mode') && !SessionUpdateTimestampHandlerInterface::validateId($sessionId)) { // A session ID is recreated even if it is collision free. // See the note below for more details. $sessionId = SessionIdInterface::create_sid(); } SessionHandlerInterface::read($sessionId);
-
session_destroy();
(documentation)
Destroys all data registered to a session.SessionHandlerInterface::destroy($sessionId); SessionHandlerInterface::close();
-
session_unset();
(documentation)
Free all session variables.$_SESSION = [];
-
session_gc();
(documentation)
Perform session data garbage collection.$maxlifetime = (int) ini_get('session.gc_maxlifetime'); SessionHandlerInterface::gc($maxlifetime);
Note: there is a bug in functions session_regenerate_id()
and
session_create_id()
(you can see
https://bugs.php.net/bug.php?id=77178
for more information) that forces the recreation of session ID even when
it is collision free.
After new session ID is created with create_sid()
, the method validateId()
is called and if it returns false
, which means that the session does not
exist in storage, session ID is recreated.
While we wait for the fixation of this bug, a simple workaround can
be the following:
class CustomSessionHandler implements SessionHandlerInterface,
SessionIdInterface,
SessionUpdateTimestampHandlerInterface
{
private $lastCreatedId;
// ...methods implementation...
public function create_sid()
{
$this->lastCreatedId = // create session ID
return $this->lastCreatedId;
}
public function validateId($sessionId)
{
if ($sessionId === $this->lastCreatedId) {
return true;
}
// checks session existance
}
}
I hope this analysis will help all the developers interested in understanding
in detail the native session management performed by PHP and what a custom
session handler should do.
Any comment or suggestion is appreciated.
Hi, first, thanks for this writeup.
Do you know if native gc() method calls destroy() internally for expired sessions to clean or it simply destroy files ?
I wonder if it possible in some way to execute custom code when sessions are removed via gc() ...
I think it is not recommended to attach "user space" code to the SessionHandler, but i'm curious if it is even possible.