Skip to content

Commit

Permalink
fixed session management
Browse files Browse the repository at this point in the history
Some explanations on how it works now:

 * The Session is an optional dependency of the Request. If you create the
   Request yourself (which is mandatory now in the front controller) and if
   you don't inject a Session yourself (which is recommended if you want the
   session to be configured via dependency injection), the Symfony2 Kernel
   will associate the Session configured in the Container with the Request
   automatically.

 * When duplicating a request, the session is shared between the parent and
   the child (that's because duplicated requests are sub-requests of the main
   one most of the time.) Notice that when you use ::create(), the behavior is
   the same as for the constructor; no session is attached to the Request.

 * Symfony2 tries hard to not create a session cookie when it is not needed
   but a Session object is always available (the cookie is only created when
   "something" is stored in the session.)

 * Symfony2 only starts a session when:

   * A session already exists in the request ($_COOKIE[session_name()] is
     defined -- this is done by RequestListener);

   * There is something written in the session object (the cookie will be sent
     to the Client).

 * Notice that reading from the session does not start the session anymore (as
   we don't need to start a new session to get the default values, and because
   if a session exists, it has already been started by RequestListener.)
  • Loading branch information
fabpot committed Nov 9, 2010
1 parent d7d4880 commit 7b02766
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 55 deletions.
Expand Up @@ -42,7 +42,6 @@ public function __construct(ContainerInterface $container, ControllerNameConvert
{
$this->container = $container;
$this->converter = $converter;
$this->esiSupport = $container->has('esi') && $container->getEsiService()->hasSurrogateEsiCapability($container->getRequestService());

parent::__construct($logger);
}
Expand Down Expand Up @@ -137,6 +136,10 @@ public function render($controller, array $options = array())
$options['alt'] = array($options['alt']);
}

if (null === $this->esiSupport) {
$this->esiSupport = $this->container->has('esi') && $this->container->getEsiService()->hasSurrogateEsiCapability($this->container->getRequestService());
}

if ($this->esiSupport && $options['standalone']) {
$uri = $this->generateInternalUri($controller, $options['attributes'], $options['query']);

Expand Down
41 changes: 36 additions & 5 deletions src/Symfony/Bundle/FrameworkBundle/RequestListener.php
Expand Up @@ -3,9 +3,11 @@
namespace Symfony\Bundle\FrameworkBundle;

use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/*
* This file is part of the Symfony framework.
Expand All @@ -25,9 +27,11 @@ class RequestListener
{
protected $router;
protected $logger;
protected $container;

public function __construct(RouterInterface $router, LoggerInterface $logger = null)
public function __construct(ContainerInterface $container, RouterInterface $router, LoggerInterface $logger = null)
{
$this->container = $container;
$this->router = $router;
$this->logger = $logger;
}
Expand All @@ -40,14 +44,39 @@ public function __construct(RouterInterface $router, LoggerInterface $logger = n
*/
public function register(EventDispatcher $dispatcher, $priority = 0)
{
$dispatcher->connect('core.request', array($this, 'resolve'), $priority);
$dispatcher->connect('core.request', array($this, 'handle'), $priority);
}

public function resolve(Event $event)
public function handle(Event $event)
{
$request = $event->getParameter('request');
$master = HttpKernelInterface::MASTER_REQUEST === $event->getParameter('request_type');

$this->initializeSession($request, $master);

$this->initializeRequestAttributes($request, $master);
}

if (HttpKernelInterface::MASTER_REQUEST === $event->getParameter('request_type')) {
protected function initializeSession(Request $request, $master)
{
if (!$master) {
return;
}

// inject the session object if none is present
if (null === $request->getSession()) {
$request->setSession($this->container->get('session'));
}

// starts the session if a session cookie already exists in the request...
if ($request->hasSession()) {
$request->getSession()->start();
}
}

protected function initializeRequestAttributes(Request $request, $master)
{
if ($master) {
// set the context even if the parsing does not need to be done
// to have correct link generation
$this->router->setContext(array(
Expand All @@ -59,9 +88,11 @@ public function resolve(Event $event)
}

if ($request->attributes->has('_controller')) {
// routing is already done
return;
}

// add attributes based on the path info (routing)
if (false !== $parameters = $this->router->match($request->getPathInfo())) {
if (null !== $this->logger) {
$this->logger->info(sprintf('Matched route "%s" (parameters: %s)', $parameters['_route'], str_replace("\n", '', var_export($parameters, true))));
Expand Down
Expand Up @@ -7,7 +7,6 @@
<parameters>
<parameter key="event_dispatcher.class">Symfony\Bundle\FrameworkBundle\EventDispatcher</parameter>
<parameter key="http_kernel.class">Symfony\Component\HttpKernel\HttpKernel</parameter>
<parameter key="request.class">Symfony\Component\HttpFoundation\Request</parameter>
<parameter key="response.class">Symfony\Component\HttpFoundation\Response</parameter>
<parameter key="error_handler.class">Symfony\Component\HttpKernel\Debug\ErrorHandler</parameter>
<parameter key="error_handler.level">null</parameter>
Expand All @@ -30,12 +29,6 @@
<argument type="service" id="controller_resolver" />
</service>

<service id="request" class="%request.class%">
<call method="setSession">
<argument type="service" id="session" on-invalid="ignore"></argument>
</call>
</service>

<service id="response" class="%response.class%" shared="false" />
</services>
</container>
Expand Up @@ -29,6 +29,7 @@

<service id="request_listener" class="%request_listener.class%">
<tag name="kernel.listener" />
<argument type="service" id="service_container" />
<argument type="service" id="router" />
<argument type="service" id="logger" on-invalid="ignore" />
</service>
Expand Down
11 changes: 6 additions & 5 deletions src/Symfony/Component/HttpFoundation/Request.php
Expand Up @@ -200,6 +200,12 @@ public function duplicate(array $query = null, array $request = null, array $att
return $dup;
}

/**
* Clones the current request.
*
* Note that the session is not cloned as duplicated requests
* are most of the time sub-requests of the main one.
*/
public function __clone()
{
$this->query = clone $this->query;
Expand Down Expand Up @@ -245,11 +251,6 @@ public function get($key, $default = null)

public function getSession()
{
if (null === $this->session) {
$this->session = new Session(new NativeSessionStorage());
}
$this->session->start();

return $this->session;
}

Expand Down
41 changes: 11 additions & 30 deletions src/Symfony/Component/HttpFoundation/Session.php
Expand Up @@ -36,7 +36,7 @@ public function __construct(SessionStorageInterface $storage, array $options = a
{
$this->storage = $storage;
$this->options = $options;
$this->attributes = array();
$this->attributes = array('_flash' => array(), '_locale' => $this->getDefaultLocale());
$this->started = false;
}

Expand All @@ -58,7 +58,7 @@ public function start()
}

if (!isset($this->attributes['_locale'])) {
$this->attributes['_locale'] = isset($this->options['default_locale']) ? $this->options['default_locale'] : 'en';
$this->attributes['_locale'] = $this->getDefaultLocale();
}

// flag current flash messages to be removed at shutdown
Expand All @@ -76,10 +76,6 @@ public function start()
*/
public function has($name)
{
if (false === $this->started) {
$this->start();
}

return array_key_exists($name, $this->attributes);
}

Expand All @@ -93,10 +89,6 @@ public function has($name)
*/
public function get($name, $default = null)
{
if (false === $this->started) {
$this->start();
}

return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
}

Expand All @@ -122,10 +114,6 @@ public function set($name, $value)
*/
public function getAttributes()
{
if (false === $this->started) {
$this->start();
}

return $this->attributes;
}

Expand Down Expand Up @@ -187,10 +175,6 @@ public function invalidate()
*/
public function getLocale()
{
if (false === $this->started) {
$this->start();
}

return $this->attributes['_locale'];
}

Expand All @@ -201,6 +185,10 @@ public function getLocale()
*/
public function setLocale($locale)
{
if ($locale === $this->getDefaultLocale()) {
return;
}

if (false === $this->started) {
$this->start();
}
Expand All @@ -210,10 +198,6 @@ public function setLocale($locale)

public function getFlashMessages()
{
if (false === $this->started) {
$this->start();
}

return $this->attributes['_flash'];
}

Expand All @@ -228,10 +212,6 @@ public function setFlashMessages($values)

public function getFlash($name, $default = null)
{
if (false === $this->started) {
$this->start();
}

return array_key_exists($name, $this->attributes['_flash']) ? $this->attributes['_flash'][$name] : $default;
}

Expand All @@ -247,10 +227,6 @@ public function setFlash($name, $value)

public function hasFlash($name)
{
if (false === $this->started) {
$this->start();
}

return array_key_exists($name, $this->attributes['_flash']);
}

Expand Down Expand Up @@ -280,4 +256,9 @@ public function unserialize($serialized)
$this->attributes = array();
$this->started = false;
}

protected function getDefaultLocale()
{
return isset($this->options['default_locale']) ? $this->options['default_locale'] : 'en';
}
}
9 changes: 2 additions & 7 deletions src/Symfony/Component/HttpKernel/HttpKernel.php
Expand Up @@ -45,18 +45,13 @@ public function __construct(ContainerInterface $container, EventDispatcher $disp
*/
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
{
$currentRequest = null;
if (HttpKernelInterface::MASTER_REQUEST === $type) {
$currentRequest = $this->container->get('request');
}
$masterRequest = HttpKernelInterface::MASTER_REQUEST === $type ? $request : $this->container->get('request');

$this->container->set('request', $request);

$response = parent::handle($request, $type, $catch);

if (null !== $currentRequest) {
$this->container->set('request', $currentRequest);
}
$this->container->set('request', $masterRequest);

return $response;
}
Expand Down

0 comments on commit 7b02766

Please sign in to comment.