PSR-7 and Session Cookies
One of the great things about PHP is its session handling capabilities. One call to session_start()
and a huge amount of heavy lifting is done for you. It’s a great aid when writing page scripts.
However, as you start to need finer control over the HTTP response in your project, some of the automatic session behaviors begin to get in the way. In particular, when you are using PSR-7 to build your HTTP response, you realize that session_start()
and session_regenerate_id()
both automatically do the equivalent of calling setcookie()
to write headers directly to the output. This means you cannot buffer those calls into the Response object for later sending.
How then can we use PHP’s session handling, when we want finer control over when and how cookies get sent?
The first trick is to tell PHP not to send a cookie when it does session work. This is accomplished with three ini_set()
calls:
ini_set('session.use_trans_sid', false);
ini_set('session.use_cookies', false);
ini_set('session.use_only_cookies', true);
These direct PHP not to use transparent session IDs, not to use cookies, and (counterintuitively) to use only cookies. If I understand correctly, the combination of the last two means that PHP will read only from the cookies, and from nowhere else, to find the session ID value.
With those settings, a call to session_start()
will cause PHP to read from the cookie values for the session ID, but it will not cause PHP to set any cookies for the session.
The second trick is to compare the session ID in the incoming request, to the session_id()
value at the time you want to send the response. If they are different, that means a session has been started or regenerated, at which point you can send the session cookie manually. The following is an example Relay-compatible middleware that puts session cookie handling logic into effect:
When you examine the class, note that the cookie-creation code is intended to be the same as in the PHP session handling code itself. Note also that you can extract the relevant logic (“compare the Request session ID to the current one, and send a cookie if they’re different”) and use it in a non-middleware-based application.
With SessionHeadersHandler in place, subsequent middleware decorators can call session_start()
and session_regenerate_id()
, and PHP will no longer automatically write out a session cookie on its own. The handler will set the cookie into a PSR-7 Response object for later sending.
Unfortunately, this is only a partial solution for session headers. The handler does not deal with things like session cache expire and limiter headers. However, it does give you control over when session cookie itself get sent, and that’s a great aid when you want to work with PSR-7 Response objects.
Read the Reddit discussions about this post here and here.