浏览代码

fixed session management

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.)
Fabien Potencier 14 年之前
父节点
当前提交
7b02766373

+ 4 - 1
src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php

@@ -42,7 +42,6 @@ class ControllerResolver extends BaseControllerResolver
     {
         $this->container = $container;
         $this->converter = $converter;
-        $this->esiSupport = $container->has('esi') && $container->getEsiService()->hasSurrogateEsiCapability($container->getRequestService());
 
         parent::__construct($logger);
     }
@@ -137,6 +136,10 @@ class ControllerResolver extends BaseControllerResolver
             $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']);
 

+ 36 - 5
src/Symfony/Bundle/FrameworkBundle/RequestListener.php

@@ -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.
@@ -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;
     }
@@ -40,14 +44,39 @@ class RequestListener
      */
     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(
@@ -59,9 +88,11 @@ class RequestListener
         }
 
         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))));

+ 0 - 7
src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml

@@ -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>
@@ -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>

+ 1 - 0
src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml

@@ -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>

+ 6 - 5
src/Symfony/Component/HttpFoundation/Request.php

@@ -200,6 +200,12 @@ class Request
         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;
@@ -245,11 +251,6 @@ class Request
 
     public function getSession()
     {
-        if (null === $this->session) {
-            $this->session = new Session(new NativeSessionStorage());
-        }
-        $this->session->start();
-
         return $this->session;
     }
 

+ 11 - 30
src/Symfony/Component/HttpFoundation/Session.php

@@ -36,7 +36,7 @@ class Session implements \Serializable
     {
         $this->storage = $storage;
         $this->options = $options;
-        $this->attributes = array();
+        $this->attributes = array('_flash' => array(), '_locale' => $this->getDefaultLocale());
         $this->started = false;
     }
 
@@ -58,7 +58,7 @@ class Session implements \Serializable
         }
 
         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
@@ -76,10 +76,6 @@ class Session implements \Serializable
      */
     public function has($name)
     {
-        if (false === $this->started) {
-            $this->start();
-        }
-
         return array_key_exists($name, $this->attributes);
     }
 
@@ -93,10 +89,6 @@ class Session implements \Serializable
      */
     public function get($name, $default = null)
     {
-        if (false === $this->started) {
-            $this->start();
-        }
-
         return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
     }
 
@@ -122,10 +114,6 @@ class Session implements \Serializable
      */
     public function getAttributes()
     {
-        if (false === $this->started) {
-            $this->start();
-        }
-
         return $this->attributes;
     }
 
@@ -187,10 +175,6 @@ class Session implements \Serializable
      */
     public function getLocale()
     {
-        if (false === $this->started) {
-            $this->start();
-        }
-
         return $this->attributes['_locale'];
     }
 
@@ -201,6 +185,10 @@ class Session implements \Serializable
      */
     public function setLocale($locale)
     {
+        if ($locale === $this->getDefaultLocale()) {
+            return;
+        }
+
         if (false === $this->started) {
             $this->start();
         }
@@ -210,10 +198,6 @@ class Session implements \Serializable
 
     public function getFlashMessages()
     {
-        if (false === $this->started) {
-            $this->start();
-        }
-
         return $this->attributes['_flash'];
     }
 
@@ -228,10 +212,6 @@ class Session implements \Serializable
 
     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;
     }
 
@@ -247,10 +227,6 @@ class Session implements \Serializable
 
     public function hasFlash($name)
     {
-        if (false === $this->started) {
-            $this->start();
-        }
-
         return array_key_exists($name, $this->attributes['_flash']);
     }
 
@@ -280,4 +256,9 @@ class Session implements \Serializable
         $this->attributes = array();
         $this->started = false;
     }
+
+    protected function getDefaultLocale()
+    {
+        return isset($this->options['default_locale']) ? $this->options['default_locale'] : 'en';
+    }
 }

+ 2 - 7
src/Symfony/Component/HttpKernel/HttpKernel.php

@@ -45,18 +45,13 @@ class HttpKernel extends BaseHttpKernel
      */
     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;
     }