Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 31 commits into from Feb 11, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
39288bc
[HttpFoundation] Added AttributesInterface and AttributesBagInterface…
Nov 22, 2011
c969423
[HttpFoundation] Added FlashBagInterface and concrete implementation.
Nov 24, 2011
3a263dc
[HttpFoundation] Introduced session storage base class and interfaces.
Nov 29, 2011
57ef984
[HttpFoundation] Added unit and functional testing session storage ob…
Dec 3, 2011
85b5c43
[HttpFoundation] Added drivers for PHP native session save handlers, …
Dec 4, 2011
e185c8d
[HttpFoundation] Refactored component for session workflow.
Dec 8, 2011
669bc96
[HttpFoundation] Added pure Memcache, Memcached and Null storage driv…
Dec 11, 2011
7aaf024
[FrameworkBundle] Refactored code for changes to HttpFoundation compo…
Dec 15, 2011
1ed6ee3
[DoctribeBridge][SecurityBundle][WebProfiler] Refactor code for HttpF…
Dec 24, 2011
9dd4dbe
Documentation, changelogs and coding standards.
Dec 24, 2011
f98f9ae
[HttpFoundation] Refactor for DRY code.
Jan 2, 2012
398acc9
[HttpFoundation] Reworked flashes to maintain same behaviour as in Sy…
Feb 2, 2012
f9951a3
Fixed formatting.
Feb 2, 2012
d64939a
[DoctrineBridge] Refactored driver for changed interface.
Feb 2, 2012
4683915
[HttpFoundation] Free bags from session storage and move classes to t…
Feb 8, 2012
27530cb
[HttpFoundation] Moved session related classes to own sub-namespace.
Feb 8, 2012
5b7ef11
[HttpFoundation] Simplify session storage class names now we have a s…
Feb 9, 2012
dad60ef
[HttpFoundation] Add back get defaults and small clean-up.
Feb 10, 2012
0d2745f
[HttpFoundation] Remove constants from FlashBagInterface
Feb 10, 2012
7878a0a
[HttpFoundation] renamed pop() to all() and getAll() to all()
fabpot Feb 11, 2012
0494250
removed unused use statements
fabpot Feb 11, 2012
91f4f8a
[HttpFoundation] changed default flash bag to auto-expires to keep BC
fabpot Feb 11, 2012
74ccf70
reverted 5b7ef116507fa05e1042065f61a50733c19116cb (Simplify session
fabpot Feb 11, 2012
93d81a1
[HttpFoundation] removed configuration for session storages in sessio…
fabpot Feb 11, 2012
146a502
[FrameworkBundle] added some service aliases to avoid some BC breaks
fabpot Feb 11, 2012
0f6c50a
[HttpFoundation] added some method for a better BC
fabpot Feb 11, 2012
282d3ae
updated CHANGELOG for 2.1
fabpot Feb 11, 2012
8a01dd5
renamed getFlashes() to getFlashBag() to avoid clashes
fabpot Feb 11, 2012
b8df162
Correct instanceof condition.
Feb 11, 2012
c59d880
Docblocks.
Feb 11, 2012
cb6fdb1
[HttpFoundation] removed Session::close()
fabpot Feb 11, 2012
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions CHANGELOG-2.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
77 changes: 72 additions & 5 deletions UPGRADE-2.1.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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; ?>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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') %}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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`
58 changes: 25 additions & 33 deletions src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -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);
}

/**
Expand All @@ -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;
}
Expand All @@ -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;
Expand All @@ -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(
Expand All @@ -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(
Expand All @@ -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(
Expand All @@ -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);
}
}

Expand All @@ -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();

Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't you add the abstract storage too ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added. thanks.

$container->getDefinition('session')->getClass(),
));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ public function onKernelResponse(FilterResponseEvent $event)

if ($session = $event->getRequest()->getSession()) {
$session->save();
$session->close();

$params = session_get_cookie_params();

Expand Down
23 changes: 18 additions & 5 deletions src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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>
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down
Loading