This page provides a full overview of PHP's SessionHandler
life-cycle - this was generated by a set of test-scripts, in order to provide an exact overview of when and
what you can expect will be called in your custom SessionHandler
implementation.
Each example is a separate script being run by a client with cookies enabled.
To the left, you can see the function being called in your script, and to the right, you can see the resulting calls being made to a custom session-handler registed using session_set_save_handler().
Output on the left side (from echo
statements) is indicated by an extra level of indentation.
When you start a new session, and no session is active, create_sid()
is called, and should
return the new session ID, which PHP will return to the client in a cookie, and use in
future calls to read()
and write()
.
Note the arguments to open()
which includes the absolute root path to the folder
where PHP's standard file session handler normally stores the data - as your custom
session handler likely uses something other than files, you can safely ignore this.
Also note that the actual content of $_SESSION
has already been serialized at the
point where PHP calls write()
- if you need access to the session data for some reason
(such as wishing to encode it in a different format) you can not simply
unserialize the data, as it isn't encoded in the normal
serialization format; unfortunately, session_decode(),
the appropriate function for that format, decodes the data and writes it directly to
$_SESSION
, which probably isn't what you want either, so if you do need access to
session data on access, your best bet may be to bootstrap your applicatio with a call
to ini_set('session.serialize_handler', 'php_serialize')
which will switch to
normal serialization compatible with serialize and
unserialize. (note that this feature is only available
in PHP versions starting from 5.5.4.)
session_start();
# SessionHandler::open('C:\\server\\temp', 'PHPSESSID')
# SessionHandler::create_sid()
# SessionHandler::read('f57cvufkbu6qgfiqkksuagl257')
$_SESSION['foo'] = 'bar';
session_write_close();
# SessionHandler::write('f57cvufkbu6qgfiqkksuagl257', 'foo|s:3:"bar";')
# SessionHandler::close()
Note that write()
will be called, regardless of whether or not any changes were actually
made to the contents of $_SESSION
- you may be able to optimize this internally in your
custom handler and avoid a redundant write; with certain storage back-ends, for example,
you may be able to "touch" a key/value and avoid the overhead of re-writing the same data.
session_start();
# SessionHandler::open('C:\\server\\temp', 'PHPSESSID')
# SessionHandler::read('f57cvufkbu6qgfiqkksuagl257')
echo $_SESSION['foo'];
bar
session_write_close();
# SessionHandler::write('f57cvufkbu6qgfiqkksuagl257', 'foo|s:3:"bar";')
# SessionHandler::close()
If a call to session_regenerate_id() is made,
create_sid()
will be called again to create a new session ID.
Note that destroy()
isn't called for the old session ID, which means the previous
session ID and associated data isn't disposed! - I'm uncertain as ti whether this is
"by design", and it seems like this could be a potential (minor) security issue, as
you end up effectively with two identical, distinct, valid sessions. For some reason,
the user needs to call session_regenerate_id(true)
to explicitly ask for the old
session ID to be destroyed, which would generate an extra call to destroy()
not
shown in the following example.
session_start();
# SessionHandler::open('C:\\server\\temp', 'PHPSESSID')
# SessionHandler::read('f57cvufkbu6qgfiqkksuagl257')
session_regenerate_id();
# SessionHandler::create_sid()
echo $_SESSION['foo'];
bar
session_write_close();
# SessionHandler::write('dp1srap0fn9isne4na6mm83mt4', 'foo|s:3:"bar";')
# SessionHandler::close()
Note that a call to session_reset()
without a prior call to session_start()
will result
in no calls being made to the session handler, whatsoever; apparently, this is by design,
as neither session_reset()
or session_write_close()
will issue any notice or warning.
Assuming a session has been started, resetting the session will result in both another
call to open()
and read()
in your custom session handler, so be prepared for that.
(that is, do not expect synchronous calls to open()
and close()
during the lifecycle
of a script - you could be getting more than one call to open()
, so you will want to
check in your session handler if the connection to your back-end is already open, so
you don't accidentally open more than one connection.)
session_start()
# SessionHandler::open('C:\\server\\temp', 'PHPSESSID')
# SessionHandler::read('ui4odihluc5nkc1oh4gftlgtd7')
session_reset()
# SessionHandler::open('C:\\server\\temp', 'PHPSESSID')
# SessionHandler::read('ui4odihluc5nkc1oh4gftlgtd7')
session_write_close();
# SessionHandler::write('ui4odihluc5nkc1oh4gftlgtd7', 'foo|s:3:"bar";')
# SessionHandler::close()
Note that manually changing the current session ID using session_id($id)
is only
effective if done before calling session_start()
- if called afterwards, it will
"set" the session ID, and will invoke write()
with the new session ID, however, it
will never set the cookie, which means that, upon the next request, the session ID (and
session contents) will simply go back to the previous one. Also note, if you do this
in the wrong order, the calls to read()
and write()
will be made with two different
session IDs.
session_id(sha1('my custom id'));
session_start();
# SessionHandler::open('C:\\server\\temp', 'PHPSESSID')
# SessionHandler::read('b9361e543a36b9318334f618c3645ae270f773b6')
session_write_close();
# SessionHandler::write('b9361e543a36b9318334f618c3645ae270f773b6', 'a:0:{}')
# SessionHandler::close()
Calling session_destroy()
will trigger calls to both destroy()
and close()
in
your session handler.
What can be pretty confusing about session management here, is that, while a call
to session_destroy()
does trigger the call to destroy()
, causing your handler to
wipe out the data stored for the session, it does not generate a new session ID,
nor does it clear the current state of $_SESSION
from memory. If you were to
make another call to session_start()
after calling session_destroy()
, at this
point the contents of $_SESSION
is wiped out, as this causes subsequent calls to
open()
and read()
, with the same session ID, which your handler was just told
to erase - thus loading an empty session state into a "new" session with the same ID.
session_start();
# SessionHandler::open('C:\\server\\temp', 'PHPSESSID')
# SessionHandler::read('dp1srap0fn9isne4na6mm83mt4')
session_destroy();
# SessionHandler::destroy('dp1srap0fn9isne4na6mm83mt4')
# SessionHandler::close()
The simplest way to think about custom session handlers, is to avoid thinking of them as objects with instance methods, although that happens to be how it's implemented - instead, think of the methods as being static; or in other words, avoid state in your implementation, with the exception of accidental state (for optimization/caching purposes), and bear in mind, the original API for custom session handlers was a set of individual functions, essentially the analog to a set of static methods, not to an object.
In other words, it's simpler not to make any assumptions about the order in which any of the methods are going to be called, how many times they are going to be called, or with what arguments - implement every individual method as though you had no previous knowledge of what had already been called, and with no expectation of any subsequent calls. The only exception being, as mentioned, accidental state, e.g. from caching.
Do not leave any operation half done.
Good luck.