Description
I've successfully gotten a fairly large Symfony 2 application working under FastCGIDaemon. Initial testing shows up to 400ms saved per request!
However, I ran into some issues with handling multiple simultaneous sessions. I gutted the Symfony 2 handling (other than HttpFoundation Request/Response) to try to get an understanding of how PHP is handling things. Here's what I've got so far:
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use PHPFastCGI\FastCGIDaemon\ApplicationFactory;
use PHPFastCGI\FastCGIDaemon\Http\RequestInterface;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
$stream = fopen('php://stdout', 'w');
ini_set('session.use_cookie', false);
$kernel = function (RequestInterface $originalRequest) use ($stream) {
$request = $originalRequest->getHttpFoundationRequest();
fwrite($stream, $request->headers."\n");
if ($sessId = $request->cookies->get(session_name())) {
session_id($sessId);
} else {
session_id(hash('sha1', uniqid(mt_rand(), true)));
}
session_start();
if (!isset($_SESSION['test'])) {
fwrite($stream, 'Saving test!'."\n");
$_SESSION['test'] = 'thing';
} else {
fwrite($stream, 'Showing test: '.$_SESSION['test']."\n");
}
$response = Response::create();
$cookieParams = session_get_cookie_params();
$response->headers->setCookie(new Cookie(session_name(), session_id(), time()+86400, $cookieParams['path'], $cookieParams['domain'], $cookieParams['secure'], $cookieParams['httponly']));
fwrite($stream, $response->headers."\n");
session_write_close();
return $response;
};
$application = (new ApplicationFactory)->createApplication($kernel);
$application->run();
This works, though obviously ugly. Key points:
- You must generate your own session ID, always. PHP seems to want to keep the last used ID if it is not explicitly overwritten.
- You must set the cookie on the response manually. I used
session_get_cookie_params()
so that the ini settings could be used.
Thoughts:
- Implementing a
SessionHandlerInterface
will invariably be a better solution. This also gives you a clear place to implement the regenerating of a session_id (by setting acreate_sid
callback [yes, that means the oldsession_set_save_handler
prototype must be used]). - Disabling
session.use_cookie
might not be necessary
I'm concerned that I'm missing something as far as maintaining session security (preventing hijacking or fixation). Since this code blindly checks for a PHPSESSID
cookie, it would be a trivial affair to hijack someone else's session, if you could guess the ID. However, I don't think this logic differs from the built-in default PHP session_id validation logic, so maybe it's a moot point.