Skip to content

Instantly share code, notes, and snippets.

@mindplay-dk
Last active February 11, 2024 12:23
Star You must be signed in to star a gist
Save mindplay-dk/623bdd50c1b4c0553cd3 to your computer and use it in GitHub Desktop.
Complete overview of the PHP SessionHandler life-cycle

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.

Open, write and close

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

Resume, read and 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()

Open, regenerate and write

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

Session reset

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

Custom session ID

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

Destruction

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

In Summary

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.

Copy link

ghost commented Jan 26, 2016

Good overview but like for my you missed better implementation of sid creation, made of all characters like in original , default session handler implementation.

@azazqadir
Copy link

Did you used any server for session handling in PHP. I think it becomes easier when you are using Redis as php session handler. Redis is now being used for session handling instead of memcached, because it is faster, support different data types and supports application scalability.

@amfleurke
Copy link

amfleurke commented Nov 24, 2020

Note that session_start() will call open() followed by close() instead of read(), if open() returns false and you're using php7! (I think from v7.1)

@duboism
Copy link

duboism commented Jun 23, 2021

I'm considering to update the documentation about SessionHandlerInterface and I think that a graphical representation of the session life-cycle would greatly help. Would you be interested to cooperate on that ?

@mindplay-dk
Copy link
Author

@duboism I'm not currently using PHP for work, so, no thanks.

(I also back then ended up not using PHP's session abstraction, at all - instead, we ended up going all-in on PSR middleware, and built a much simpler custom solution for session management, avoiding $_REQUEST and everything around PHP native sessions. It was a much better experience - easy to write tests, and so on.)

@duboism
Copy link

duboism commented Jun 24, 2021

@mindplay-dk Thanks for your thoughts on this, it's interesting. I will probably reuse your approach to gain some understanding of PHP sessions. Do you still have the code for that ?

@mindplay-dk
Copy link
Author

@duboism for old-fashioned PHP session handlers described in this gist? No, there was no code. We decided PHP's native session-handling was too much of a mess, and went the PSR middleware route instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment