Recently, I was working on a web application in which we were passing PHP session IDs around to emulate users. As a result, the app experienced severe slowness on page load as well as in a few other places. Upon investigation, it turned out this slowness was a result of the sessions blocking each other as they were passed around. All of the following refers to PHP’s default session handling functionality.
PHP’s internal session handling mechanisms put a lock on the session file to prevent different scripts from overwriting session data. Unless you are explicitly releasing these with session_write_close(), scripts accessing the same session are pushed onto a stack (in FIFO order) and must wait for all earlier scripts to terminate. This becomes especially problematic when a script is calling the other with PHP cURL. In this case, the first script waits for the second to return before releasing it’s lock, and the second won’t start until the first release it’s lock. This results in pages that do very little.
After I found session_write_close(), and patched the code, I decided to dig in a bit deeper to how this function worked. The comments on php.net  suggested you could have overwriting problems when not used correctly (read: trying to write to the session after closing the write), so I pulled together a bit of code to test those effects.
The following three files  are pulled together after my testing to best describe the situation involving session write locks and their interaction with session_write_close() and the effect on $_SESSION.
First is our main file, session_locker.php, which as the name suggests can lock the session when session_write_close() is commented out. This is our work-horse, making the requests, outputting the results, and finally giving session_locker.php’s view of $_SESSION.
Our second file is session_user.php, which takes a session ID (with no validation :trollface: ) and becomes that ‘user.’ Since sessions are handled with cookies, this is an easy way to ‘proxy’ a user internally, tho not necessarily the recommended implementation. Here we set the session up, output our intial view of $_SESSION, then make some modifications and output that $_SESSION again.
And our last file is very basic, just a stand alone $_SESSION viewer. Hence the overly clever name, session_viewer.php.
When we visit session_locker.php, it sets a few $_SESSION variables, then closes the write lock, and sets one more before requesting that session_user.php be run. The output order at the bottom is important in showcasing the overwriting issues, and is as follows:
We can see here that the ‘second file’ output, or session_user.php, being called by cURL sees the $_SESSION variables set by session_locker.php minus otest, as it was set after the write lock was closed. It then sets it’s own variables including overwriting otest and returns. We then let session_locker.php account for it’s actions and tell us what it thinks $_SESSION is. It’s version of $_SESSION has the old otest value and is entirely missing stuff (the variable name, not random things.) As a result of closing the the session write lock, session_locker.php ends up with an outdated and unbound version of $_SESSION (essentially moving the entire thing to the local scope, rather than super global.) Using session_viewer.php, we can see what the true value of $_SESSION is and that it matches session_user.php.
PHP won’t error out when you try to write to the session after closing it, nor will it simply ignore the call. It will actually modify the $_SESSION variable and for the rest of that script’s iteration will work perfectly. Other scripts, running concurrently or thereafter (anything outside of the script where that assignment was made) won’t see the update. Based on this behavior, I’d assume the variable is only changed in memory, but never pushed to the session file for saved state. This could certainly be a debugging gotcha , as your code would dump out the right value, but it wouldn’t persist anywhere else.