[2.1][HttpFoundation] Refactor session handling and flash messages #2853

Merged
merged 31 commits into from Feb 11, 2012
Commits
Jump to file or symbol
Failed to load files and symbols.
+4,191 −1,421
Diff settings

Always

Just for now

View
@@ -224,6 +224,34 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c
* removed the ContentTypeMimeTypeGuesser class as it is deprecated and never used on PHP 5.3
* added ResponseHeaderBag::makeDisposition() (implements RFC 6266)
* made mimetype to extension conversion configurable
+ * [BC BREAK] Moved all session related classes and interfaces into own namespace, as
+ `Symfony\Component\HttpFoudation\Session` and renamed classes accordingly.
+ * Added `FlashBag`. Flashes expire when retrieved by `get()` or `all()`.
+ This makes the implementation ESI compatible.
+ * Added `AutoExpireFlashBag` (default) to replicate Symfony 2.0.x auto expire behaviour of messages auto expiring
+ after one page page load. Messages must be retrived by `get()` or `all()`.
+ * [BC BREAK] Removed the `close()` method from the Session class
+ * Deprecated the following methods from the Session class: `setFlash()`, `setFlashes()`
+ `getFlash()`, `hasFlash()`, and `removeFlash()`. Use `getFlashBag() instead which returns a `FlashBagInterface`.
+ * `Session->clear()` now only clears session attributes as before it cleared flash messages and
+ attributes. `Session->getFlashBag()->all()` clears flashes now.
+ * Added `Symfony\Component\HttpFoundation\Session\Storage\AbstractSessionStorage` base class for
+ session storage drivers.
+ * Added `Symfony\Component\HttpFoundation\Session\Storage\SessionSaveHandlerInterface` interface
+ which storage drivers should implement after inheriting from
+ `Symfony\Component\HttpFoundation\Session\Storage\AbstractSessionStorage` when writing custom session save handlers.
+ * [BC BREAK] `SessionStorageInterface` methods removed: `write()`, `read()` and `remove()`. Added
+ `getBag()`, `registerBag()`.
+ * Moved attribute storage to `Symfony\Component\HttpFoundation\Attribute\AttributeBagInterface`.
+ * Added `Symfony\Component\HttpFoundation\Attribute\AttributeBag` to replicate attributes storage
+ behaviour from 2.0.x (default).
+ * Added `Symfony\Component\HttpFoundation\Attribute\NamespacedAttributeBag` for namespace session attributes.
+ * Session now implements `Symfony\Component\HttpFoundation\Session\SessionInterface` making
+ implementation customizable and portable.
+ * [BC BREAK] Removed `NativeSessionStorage` and replaced with `NativeFileSessionStorage`.
+ * Added session storage drivers for PHP native Memcache, Memcached and SQLite session save handlers.
+ * Added session storage drivers for custom Memcache, Memcached and Null session save handlers.
+ * Removed `FilesystemSessionStorage`, use `MockFileSessionStorage` for functional testing instead.
### HttpKernel
View
@@ -1,6 +1,8 @@
UPGRADE FROM 2.0 to 2.1
=======================
+### General
+
* assets_base_urls and base_urls merging strategy has changed
Unlike most configuration blocks, successive values for
@@ -11,6 +13,8 @@ UPGRADE FROM 2.0 to 2.1
and/or share a common base configuration (i.e. ``config.yml``), merging
could yield a set of base URL's for multiple environments.
+### [HttpFoundation]
+
* moved management of the locale from the Session class to the Request class
Configuring the default locale:
@@ -28,17 +32,20 @@ UPGRADE FROM 2.0 to 2.1
Retrieving the locale from a Twig template:
- Before: `{{ app.request.session.locale }}` or `{{ app.session.locale }}`
+ Before: `{{ app.request.session.locale }}` or `{{ app.session.locale }}`
+
After: `{{ app.request.locale }}`
Retrieving the locale from a PHP template:
- Before: `$view['session']->getLocale()`
+ Before: `$view['session']->getLocale()`
+
After: `$view['request']->getLocale()`
Retrieving the locale from PHP code:
- Before: `$session->getLocale()`
+ Before: `$session->getLocale()`
+
After: `$request->getLocale()`
* Method `equals` of `Symfony\Component\Security\Core\User\UserInterface` has
@@ -134,7 +141,7 @@ UPGRADE FROM 2.0 to 2.1
* The strategy for generating the HTML attributes "id" and "name"
of choices in a choice field has changed
-
+
Instead of appending the choice value, a generated integer is now appended
by default. Take care if your Javascript relies on that. If you can
guarantee that your choice values only contain ASCII letters, digits,
@@ -144,7 +151,7 @@ UPGRADE FROM 2.0 to 2.1
* The strategy for generating the HTML attributes "value" of choices in a
choice field has changed
-
+
Instead of using the choice value, a generated integer is now stored.
Again, take care if your Javascript reads this value. If your choice field
is a non-expanded single-choice field, or if the choices are guaranteed not
@@ -248,3 +255,63 @@ UPGRADE FROM 2.0 to 2.1
{
return isset($options['widget']) && 'single_text' === $options['widget'] ? 'text' : 'choice';
}
+
+* Flash Messages now returns and array based on type (the old method are still available but deprecated)
+
+ Before (PHP):
+
+ <?php if ($view['session']->hasFlash('notice')): ?>
+ <div class="flash-notice">
+ <?php echo $view['session']->getFlash('notice') ?>
+ </div>
+ <?php endif; ?>
+
+ After (PHP):
+
+ <?php if ($view['session']->getFlashBag()->has('notice')): ?>
+ <div class="flash-notice">
+ <?php echo $view['session']->getFlashBag()->get('notice') ?>
+ </div>
+ <?php endif; ?>

This comment has been minimized.

@stof

stof Feb 11, 2012

Member

this is wrong. $view['session'] is not the Session object in the PHP templates but the SessionHelper which is BC.

The Session object is available through $app->getSession() (equivalent to the Twig way to access it)

@stof

stof Feb 11, 2012

Member

this is wrong. $view['session'] is not the Session object in the PHP templates but the SessionHelper which is BC.

The Session object is available through $app->getSession() (equivalent to the Twig way to access it)

This comment has been minimized.

@fabpot

fabpot Feb 11, 2012

Member

fixed

@fabpot

fabpot Feb 11, 2012

Member

fixed

+
+ If you wanted to process all flash types you could also make use of the `getFlashBag()->all()` API:
+
+ <?php foreach ($view['session']->getFlashBag()->all() as $type => $flash): ?>
+ <div class="flash-$type">
+ <?php echo $flash; ?>
+ </div>
+ <?php endforeach; ?>
+
+ Before (Twig):
+
+ {% if app.session.hasFlash('notice') %}
+ <div class="flash-notice">
+ {{ app.session.flash('notice') }}
+ </div>
+ {% endif %}
+
+ After (Twig):
+
+ {% if app.session.flashes.has('notice') %}

This comment has been minimized.

@stof

stof Feb 11, 2012

Member

@fabpot shouldn't it be flashBag instead of flashes as you renamed it ?

@stof

stof Feb 11, 2012

Member

@fabpot shouldn't it be flashBag instead of flashes as you renamed it ?

This comment has been minimized.

@fabpot

fabpot Feb 11, 2012

Member

fixed

@fabpot

fabpot Feb 11, 2012

Member

fixed

+ <div class="flash-notice">
+ {{ app.session.flashes.get('notice') }}
+ </div>
+ {% endif %}
+
+ Again you can process all flash messages in one go with
+
+ {% for type, flashMessage in app.session.flashes.all() %}
+ <div class="flash-{{ type }}">
+ {{ flashMessage }}
+ </div>
+ {% endforeach %}
+
+* Session storage drivers
+
+ Session storage drivers should inherit from
+ `Symfony\Component\HttpFoundation\Session\Storage\AbstractSessionStorage`
+ and no longer should implement `read()`, `write()`, `remove()` which were removed from the
+ `SessionStorageInterface`.
+
+ Any session storage driver that wants to use custom save handlers should
+ implement `Symfony\Component\HttpFoundation\Session\Storage\SaveHandlerInterface`
@@ -3,7 +3,8 @@
namespace Symfony\Bridge\Doctrine\HttpFoundation;
use Doctrine\DBAL\Platforms\MySqlPlatform;
-use Symfony\Component\HttpFoundation\SessionStorage\NativeSessionStorage;
+use Symfony\Component\HttpFoundation\Session\Storage\AbstractSessionStorage;
+use Symfony\Component\HttpFoundation\Session\Storage\SessionSaveHandlerInterface;
use Doctrine\DBAL\Driver\Connection;
/**
@@ -12,39 +13,30 @@
* @author Fabien Potencier <fabien@symfony.com>
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
-class DbalSessionStorage extends NativeSessionStorage
+class DbalSessionStorage extends AbstractSessionStorage implements SessionSaveHandlerInterface
{
+ /**
+ * @var Connection
+ */
private $con;
+
+ /**
+ * @var string
+ */
private $tableName;
+ /**
+ *
+ * @param Connection $con An instance of Connection.
+ * @param string $tableName Table name.
+ * @param array $options Session configuration options
+ */
public function __construct(Connection $con, $tableName = 'sessions', array $options = array())
{
- parent::__construct($options);
-
$this->con = $con;
$this->tableName = $tableName;
- }
- /**
- * Starts the session.
- */
- public function start()
- {
- if (self::$sessionStarted) {
- return;
- }
-
- // use this object as the session handler
- session_set_save_handler(
- array($this, 'sessionOpen'),
- array($this, 'sessionClose'),
- array($this, 'sessionRead'),
- array($this, 'sessionWrite'),
- array($this, 'sessionDestroy'),
- array($this, 'sessionGC')
- );
-
- parent::start();
+ parent::__construct($options);
}
/**
@@ -55,7 +47,7 @@ public function start()
*
* @return Boolean true, if the session was opened, otherwise an exception is thrown
*/
- public function sessionOpen($path = null, $name = null)
+ public function openSession($path = null, $name = null)
{
return true;
}
@@ -65,7 +57,7 @@ public function sessionOpen($path = null, $name = null)
*
* @return Boolean true, if the session was closed, otherwise false
*/
- public function sessionClose()
+ public function closeSession()
{
// do nothing
return true;
@@ -80,7 +72,7 @@ public function sessionClose()
*
* @throws \RuntimeException If the session cannot be destroyed
*/
- public function sessionDestroy($id)
+ public function destroySession($id)
{
try {
$this->con->executeQuery("DELETE FROM {$this->tableName} WHERE sess_id = :id", array(
@@ -102,7 +94,7 @@ public function sessionDestroy($id)
*
* @throws \RuntimeException If any old sessions cannot be cleaned
*/
- public function sessionGC($lifetime)
+ public function gcSession($lifetime)
{
try {
$this->con->executeQuery("DELETE FROM {$this->tableName} WHERE sess_time < :time", array(
@@ -124,7 +116,7 @@ public function sessionGC($lifetime)
*
* @throws \RuntimeException If the session cannot be read
*/
- public function sessionRead($id)
+ public function readSession($id)
{
try {
$data = $this->con->executeQuery("SELECT sess_data FROM {$this->tableName} WHERE sess_id = :id", array(
@@ -140,7 +132,7 @@ public function sessionRead($id)
return '';
} catch (\PDOException $e) {
- throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e);
+ throw new \RuntimeException(sprintf('PDOException was thrown when trying to read the session data: %s', $e->getMessage()), 0, $e);
}
}
@@ -154,7 +146,7 @@ public function sessionRead($id)
*
* @throws \RuntimeException If the session data cannot be written
*/
- public function sessionWrite($id, $data)
+ public function writeSession($id, $data)
{
$platform = $this->con->getDatabasePlatform();
@@ -181,7 +173,7 @@ public function sessionWrite($id, $data)
$this->createNewSession($id, $data);
}
} catch (\PDOException $e) {
- throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e);
+ throw new \RuntimeException(sprintf('PDOException was thrown when trying to write the session data: %s', $e->getMessage()), 0, $e);
}
return true;
@@ -167,7 +167,7 @@ private function addSessionSection(ArrayNodeDefinition $rootNode)
->canBeUnset()
->children()
->booleanNode('auto_start')->defaultFalse()->end()
- ->scalarNode('storage_id')->defaultValue('session.storage.native')->end()
+ ->scalarNode('storage_id')->defaultValue('session.storage.native_file')->end()
->scalarNode('name')->end()
->scalarNode('lifetime')->end()
->scalarNode('path')->end()
@@ -301,7 +301,7 @@ private function registerSessionConfiguration(array $config, ContainerBuilder $c
$this->addClassesToCompile(array(
'Symfony\\Bundle\\FrameworkBundle\\EventListener\\SessionListener',
- 'Symfony\\Component\\HttpFoundation\\SessionStorage\\SessionStorageInterface',
+ 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\SessionStorageInterface',

This comment has been minimized.

@stof

stof Feb 11, 2012

Member

shouldn't you add the abstract storage too ?

@stof

stof Feb 11, 2012

Member

shouldn't you add the abstract storage too ?

This comment has been minimized.

@fabpot

fabpot Feb 11, 2012

Member

added. thanks.

@fabpot

fabpot Feb 11, 2012

Member

added. thanks.

$container->getDefinition('session')->getClass(),
));
@@ -69,7 +69,6 @@ public function onKernelResponse(FilterResponseEvent $event)
if ($session = $event->getRequest()->getSession()) {
$session->save();
- $session->close();
$params = session_get_cookie_params();
@@ -5,22 +5,31 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
- <parameter key="session.class">Symfony\Component\HttpFoundation\Session</parameter>
- <parameter key="session.storage.native.class">Symfony\Component\HttpFoundation\SessionStorage\NativeSessionStorage</parameter>
- <parameter key="session.storage.filesystem.class">Symfony\Component\HttpFoundation\SessionStorage\FilesystemSessionStorage</parameter>
+ <parameter key="session.class">Symfony\Component\HttpFoundation\Session\Session</parameter>
+ <parameter key="session.flashbag.class">Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag</parameter>
+ <parameter key="session.attribute_bag.class">Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag</parameter>
+ <parameter key="session.storage.native_file.class">Symfony\Component\HttpFoundation\Session\Storage\NativeFileSessionStorage</parameter>
+ <parameter key="session.storage.mock_file.class">Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage</parameter>
<parameter key="session_listener.class">Symfony\Bundle\FrameworkBundle\EventListener\SessionListener</parameter>
</parameters>
<services>
<service id="session" class="%session.class%">
<argument type="service" id="session.storage" />
+ <argument type="service" id="session.attribute_bag" />
+ <argument type="service" id="session.flash_bag" />
</service>
- <service id="session.storage.native" class="%session.storage.native.class%" public="false">
+ <service id="session.flash_bag" class="%session.flashbag.class%" public="false" />
+
+ <service id="session.attribute_bag" class="%session.attribute_bag.class%" public="false" />
+
+ <service id="session.storage.mock_file" class="%session.storage.mock_file.class%" public="false">
+ <argument>%kernel.cache_dir%/sessions</argument>
<argument>%session.storage.options%</argument>
</service>
- <service id="session.storage.filesystem" class="%session.storage.filesystem.class%" public="false">
+ <service id="session.storage.native_file" class="%session.storage.native_file.class%" public="false">
<argument>%kernel.cache_dir%/sessions</argument>
<argument>%session.storage.options%</argument>
</service>
@@ -29,5 +38,9 @@
<tag name="kernel.event_subscriber" />
<argument type="service" id="service_container" />
</service>
+
+ <!-- for BC -->
+ <service id="session.storage.native" alias="session.storage.native_file" />
+ <service id="session.storage.filesystem" alias="session.storage.mock_file" />
</services>
</container>
@@ -79,7 +79,7 @@ public function getRequest()
/**
* Returns the current session.
*
- * @return Symfony\Component\HttpFoundation\Session|void The session
+ * @return Symfony\Component\HttpFoundation\Session\Session|void The session
*/
public function getSession()
{
@@ -48,17 +48,17 @@ public function get($name, $default = null)
public function getFlash($name, $default = null)
{
- return $this->session->getFlash($name, $default);
+ return $this->session->getFlashBag()->get($name);
}
public function getFlashes()
{
- return $this->session->getFlashes();
+ return $this->session->getFlashBag()->all();
}
public function hasFlash($name)
{
- return $this->session->hasFlash($name);
+ return $this->session->getFlashBag()->has($name);
}
/**
Oops, something went wrong.