diff --git a/CHANGELOG-2.1.md b/CHANGELOG-2.1.md
index bdce39f9250a5..e797ba7d5cb9d 100644
--- a/CHANGELOG-2.1.md
+++ b/CHANGELOG-2.1.md
@@ -33,6 +33,11 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c
* [BC BREAK] assets_base_urls and base_urls merging strategy has changed
* changed the default profiler storage to use the filesystem instead of SQLite
* added support for placeholders in route defaults and requirements (replaced by the value set in the service container)
+ * [BC BREAK] changed `session.xml` service name `session.storage.native` to `session.storage.native_file`
+ * added new session storage drivers to session.xml: `session.storage.native_memcache`, `session.storage.native_memcached`,
+ `session.storage.native_sqlite`, `session.storage.null`, `session.storage.memcache`,
+ and `session.storage.memcached`. Added `session.storage.functional_test.file` service for functional session testing.
+ * removed `session.storage.filesystem` service.
### SecurityBundle
@@ -143,6 +148,33 @@ 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 flash messages out of the `Session` class and into `FlashBagInterface`.
+ Flashes are now stored as a bucket of messages per `$type` so there can be multiple messages per type.
+ There are four interface constants for type, `FlashBagInterface::INFO`, `FlashBagInterface::NOTICE`,
+ `FlashBagInterface::WARNING` and `FlashBagInterface::ERROR`.
+ * Flash messages are expired when retrieved (with $clear = true) set. This makes the implementation
+ more flexible and removed some dependencies in the Session management cycle.
+ * [BC BREAK] Removed the following methods from the Session class: `setFlashes()`
+ `setFlash()`, `hasFlash()`, `removeFlash()`, and `clearFlashes()`.
+ * [BC BREAK] Changed `getFlash($clear=false)` now returns flash messages for display, and added
+ `addFlash($message, $type)` to add flash messages.
+ `getFlashes()` now returns the `FlashBagInterface` for which there which can be used for deeper
+ manipulation of the flash message collection.
+ * `Session` object takes two additional object in the constructor: `AttributeBagInterface` and
+ `FlashBagInterface` after the `SessionStorageInterface`.
+ * Added `AbstractSessionStorage` base class for session storage drivers.
+ * Added `SessionSaveHandler` interface which storage drivers should implement after inheriting from
+ `AbstractSessionStorage` when writing custom session save handlers.
+ * [BC BREAK] `SessionStorageInterface` methods removed: `write()`, `read()` and `remove()`. Added
+ `getAttributes()`, `getFlashes()`.
+ * Moved attribute storage to `AttributeBagInterface`.
+ * Added `AttributeBag` to replicate attributes storage behaviour from 2.0.x
+ * Added `NamespacedAttributeBag` for namespace session attributes.
+ * Session now implements `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 `FunctionalTestFileSessionStorage` for functional testing instead.
### HttpKernel
@@ -169,7 +201,7 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c
### Serializer
- * [BC BREAK] convert the `item` XML tag to an array
+ * [BC BREAK] convert the `item` XML tag to an array
``` xml
diff --git a/UPGRADE-2.1.md b/UPGRADE-2.1.md
index a1560677f991e..69e9e2d7067ac 100644
--- a/UPGRADE-2.1.md
+++ b/UPGRADE-2.1.md
@@ -11,7 +11,7 @@ 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.
-* moved management of the locale from the Session class to the Request class
+* [HttpFoundation] - moved management of the locale from the Session class to the Request class
Configuring the default locale:
@@ -40,3 +40,63 @@ UPGRADE FROM 2.0 to 2.1
Before: $session->getLocale()
After: $request->getLocale()
+
+* [HttpFoundation] Flash Messages. Moved to own bucket and returns and array based on type.
+
+ Before (PHP):
+
+ hasFlash('notice')): ?>
+
+ getFlash('notice') ?>
+
+
+
+ After (PHP):
+
+ flashGet(Symfony\Component\HttpFoundation\FlashBag::NOTICE) as $notice): ?>
+
+
+
+
+
+.. note::
+
+ You can of course declare `` at the beginning
+ of the PHP template so you can use the shortcut `FlashBag::NOTICE`.
+
+ Before (Twig):
+
+ {% if app.session.hasFlash('notice') %}
+
+ {{ app.session.flash('notice') }}
+
+ {% endif %}
+
+ After (Twig):
+
+ {% for flashMessage in app.session.flashGet(constant(Symfony\Component\HttpFoundation\FlashBag::NOTICE)) %}
+
+ {{ flashMessage }}
+
+ {% endforeach %}
+
+* [HttpFoundation] Session object now requires two additional constructor arguments but will default to
+ sensible defaults for convenience. The methods, `setFlashes()`, `setFlash()`, `hasFlash()`,
+ `removeFlash()`, and `clearFlashes()` have all been removed from the `Session` object.
+ You may use `addFlash()` to add flashes. `getFlash()` now returns an array for display.
+ `getFlashes()` returns the FlashBagInterface if you need to deeply manipulate the flash message
+ container.
+
+* [HttpFoundation] Session storage drivers should inherit from
+ `Symfony\Component\HttpFoundation\SessionStorage\AbstractSessionStorage`
+ and no longer should implement `read()`, `write()`, `remove()` which were removed from the
+ `SessionStorageInterface`.
+
+* [HttpFoundation] Any session storage drive that wants to use custom save handlers should
+ implement `Symfony\Component\HttpFoundation\SessionStorage\SessionSaveHandlerInterface`
+
+* [FrameworkBundle] The service session.storage.native is now called `session.storage.native_file`
+
+* [FrameworkBundle] The service `session.storage.filesystem` is deprecated and should be replaced
+ `session.storage.native_file`
+
diff --git a/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionStorage.php b/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionStorage.php
index 6fa324f85e5ec..694eae34eb895 100644
--- a/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionStorage.php
+++ b/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionStorage.php
@@ -3,7 +3,10 @@
namespace Symfony\Bridge\Doctrine\HttpFoundation;
use Doctrine\DBAL\Platforms\MySqlPlatform;
-use Symfony\Component\HttpFoundation\SessionStorage\NativeSessionStorage;
+use Symfony\Component\HttpFoundation\AttributeBagInterface;
+use Symfony\Component\HttpFoundation\FlashBagInterface;
+use Symfony\Component\HttpFoundation\SessionStorage\AbstractSessionStorage;
+use Symfony\Component\HttpFoundation\SessionStorage\SessionSaveHandlerInterface;
use Doctrine\DBAL\Driver\Connection;
/**
@@ -12,39 +15,32 @@
* @author Fabien Potencier
* @author Johannes M. Schmitt
*/
-class DbalSessionStorage extends NativeSessionStorage
+class DbalSessionStorage extends AbstractSessionStorage implements SessionSaveHandlerInterface
{
+ /**
+ * @var Connection
+ */
private $con;
+
+ /**
+ * @var string
+ */
private $tableName;
- public function __construct(Connection $con, $tableName = 'sessions', array $options = array())
+ /**
+ *
+ * @param Connection $con An instance of Connection.
+ * @param string $tableName Table name.
+ * @param array $options Session configuration options
+ * @param AttributeBagInterface $attributes An AttributeBagInterface instance, (defaults null for default AttributeBag)
+ * @param FlashBagInterface $flashes A FlashBagInterface instance (defaults null for default FlashBag)
+ */
+ public function __construct(Connection $con, $tableName = 'sessions', array $options = array(), AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null)
{
- 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($attributes, $flashes, $options);
}
/**
@@ -102,7 +98,7 @@ public function sessionDestroy($id)
*
* @throws \RuntimeException If any old sessions cannot be cleaned
*/
- public function sessionGC($lifetime)
+ public function sessionGc($lifetime)
{
try {
$this->con->executeQuery("DELETE FROM {$this->tableName} WHERE sess_time < :time", array(
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
index 5762fb4c337be..8875a8677184b 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
@@ -162,7 +162,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()
diff --git a/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php b/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php
index 7d6ac49a069c2..243f4441e7478 100644
--- a/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php
+++ b/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php
@@ -69,10 +69,7 @@ public function onKernelResponse(FilterResponseEvent $event)
if ($session = $event->getRequest()->getSession()) {
$session->save();
- $session->close();
-
$params = session_get_cookie_params();
-
$event->getResponse()->headers->setCookie(new Cookie(session_name(), session_id(), 0 === $params['lifetime'] ? 0 : time() + $params['lifetime'], $params['path'], $params['domain'], $params['secure'], $params['httponly']));
}
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml
index c3c2eba0c5605..0a66e4518c0b4 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml
@@ -6,8 +6,19 @@
Symfony\Component\HttpFoundation\Session
- Symfony\Component\HttpFoundation\SessionStorage\NativeSessionStorage
- Symfony\Component\HttpFoundation\SessionStorage\FilesystemSessionStorage
+ Symfony\Component\HttpFoundation\FlashBag
+ Symfony\Component\HttpFoundation\AttributeBag
+ Symfony\Component\HttpFoundation\SessionStorage\NativeFileSessionStorage
+ Symfony\Component\HttpFoundation\SessionStorage\NullSessionStorage
+ Symfony\Component\HttpFoundation\SessionStorage\NativeMemcacheSessionStorage
+ Symfony\Component\HttpFoundation\SessionStorage\NativeMemcachedSessionStorage
+ Symfony\Component\HttpFoundation\SessionStorage\NativeSqliteSessionStorage
+ Symfony\Component\HttpFoundation\SessionStorage\MemcacheSessionStorage
+ Symfony\Component\HttpFoundation\SessionStorage\MemcachedSessionStorage
+ Symfony\Component\HttpFoundation\SessionStorage\FunctionalTestFileSessionStorage
+ Memcache
+ Memcached
+
Symfony\Bundle\FrameworkBundle\EventListener\SessionListener
@@ -16,13 +27,65 @@
-
- %session.storage.options%
+
+
+
+
+
+
+ %kernel.cache_dir%/sessions
+
+
-
+
%kernel.cache_dir%/sessions
%session.storage.options%
+
+
+
+
+
+ tcp://127.0.0.1:11211?persistent=0
+ %session.storage.options%
+
+
+
+
+
+ 127.0.0.1:11211
+ %session.storage.options%
+
+
+
+
+
+
+ tcp://127.0.0.1:11211?persistent=0
+ %session.storage.options%
+
+
+
+
+
+
+ tcp://127.0.0.1:11211?persistent=0
+ %session.storage.options%
+
+
+
+
+
+ %kernel.cache_dir%/sf2_sqlite_sess.db
+ %session.storage.options%
+
+
+
+
+
+ %session.storage.options%
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/SessionHelper.php b/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/SessionHelper.php
index 041d9ddff9918..162f1a3f9a08a 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/SessionHelper.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/SessionHelper.php
@@ -13,6 +13,7 @@
use Symfony\Component\Templating\Helper\Helper;
use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\FlashBagInterface;
/**
* SessionHelper provides read-only access to the session attributes.
@@ -46,19 +47,19 @@ public function get($name, $default = null)
return $this->session->get($name, $default);
}
- public function getFlash($name, $default = null)
+ public function getFlashes($type)
{
- return $this->session->getFlash($name, $default);
+ return $this->session->getFlashes()->get($type);
}
- public function getFlashes()
+ public function getAllFlashes()
{
- return $this->session->getFlashes();
+ return $this->session->getFlashes()->all();
}
- public function hasFlash($name)
+ public function hasFlashes($type)
{
- return $this->session->hasFlash($name);
+ return $this->session->getFlashes()->has($type);
}
/**
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php
index 01361f2280e97..3ab6488493024 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php
@@ -20,7 +20,7 @@
),
'session' => array(
'auto_start' => true,
- 'storage_id' => 'session.storage.native',
+ 'storage_id' => 'session.storage.native_file',
'name' => '_SYMFONY',
'lifetime' => 86400,
'path' => '/',
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml
index 2d6a06047e68d..e46a476a96910 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml
@@ -12,7 +12,7 @@
-
+
loader.foo
loader.bar
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml
index 3c7db0ee49dc6..126baf2d4e90b 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml
@@ -14,7 +14,7 @@ framework:
type: xml
session:
auto_start: true
- storage_id: session.storage.native
+ storage_id: session.storage.native_file
name: _SYMFONY
lifetime: 86400
path: /
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
index 90f5738737e26..b8555ae213644 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
@@ -78,7 +78,7 @@ public function testSession()
$this->assertTrue($container->hasDefinition('session'), '->registerSessionConfiguration() loads session.xml');
$this->assertEquals('fr', $container->getParameter('kernel.default_locale'));
$this->assertTrue($container->getDefinition('session_listener')->getArgument(1));
- $this->assertEquals('session.storage.native', (string) $container->getAlias('session.storage'));
+ $this->assertEquals('session.storage.native_file', (string) $container->getAlias('session.storage'));
$options = $container->getParameter('session.storage.options');
$this->assertEquals('_SYMFONY', $options['name']);
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/EventListener/TestSessionListenerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/EventListener/TestSessionListenerTest.php
index 7c656ba7e0bc9..b2f2751414cb9 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/EventListener/TestSessionListenerTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/EventListener/TestSessionListenerTest.php
@@ -32,7 +32,7 @@ class TestSessionListenerTest extends \PHPUnit_Framework_TestCase
public function setUp()
{
$this->listener = new TestSessionListener($this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'));
- $this->session = $this->getSession();
+ $this->session = $this->getSession();
}
protected function tearDown()
@@ -41,20 +41,6 @@ protected function tearDown()
$this->session = null;
}
- public function testShouldSaveMasterRequestSession()
- {
- $this->sessionMustBeSaved();
-
- $this->filterResponse(new Request());
- }
-
- public function testShouldNotSaveSubRequestSession()
- {
- $this->sessionMustNotBeSaved();
-
- $this->filterResponse(new Request(), HttpKernelInterface::SUB_REQUEST);
- }
-
public function testDoesNotDeleteCookieIfUsingSessionLifetime()
{
$params = session_get_cookie_params();
@@ -80,22 +66,33 @@ private function filterResponse(Request $request, $type = HttpKernelInterface::M
return $response;
}
+ public function testShouldSaveMasterRequestSession()
+ {
+ $this->sessionMustBeSaved();
+ $this->filterResponse(new Request());
+ }
+
+ public function testShouldNotSaveSubRequestSession()
+ {
+ $this->sessionMustNotBeSaved();
+ $this->filterResponse(new Request(), HttpKernelInterface::SUB_REQUEST);
+ }
+
private function sessionMustNotBeSaved()
{
- $this->session->expects($this->never())
- ->method('save');
+ $this->session->expects($this->never())->method('save');
}
private function sessionMustBeSaved()
{
- $this->session->expects($this->once())
- ->method('save');
+ $this->session->expects($this->once())->method('save');
}
private function getSession()
{
return $this->getMockBuilder('Symfony\Component\HttpFoundation\Session')
- ->disableOriginalConstructor()
- ->getMock();
+ ->disableOriginalConstructor()
+ ->getMock();
}
}
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/SessionHelperTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/SessionHelperTest.php
index ee400cd7dc135..a27012af135dc 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/SessionHelperTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/SessionHelperTest.php
@@ -15,6 +15,8 @@
use Symfony\Component\HttpFoundation\Session;
use Symfony\Component\HttpFoundation\SessionStorage\ArraySessionStorage;
use Symfony\Bundle\FrameworkBundle\Templating\Helper\SessionHelper;
+use Symfony\Component\HttpFoundation\FlashBag;
+use Symfony\Component\HttpFoundation\AttributeBag;
class SessionHelperTest extends \PHPUnit_Framework_TestCase
{
@@ -24,9 +26,9 @@ public function setUp()
{
$this->request = new Request();
- $session = new Session(new ArraySessionStorage());
+ $session = new Session(new ArraySessionStorage(new AttributeBag(), new FlashBag()));
$session->set('foobar', 'bar');
- $session->setFlash('foo', 'bar');
+ $session->getFlashes()->add('bar', FlashBag::NOTICE);
$this->request->setSession($session);
}
@@ -40,14 +42,12 @@ public function testFlash()
{
$helper = new SessionHelper($this->request);
- $this->assertTrue($helper->hasFlash('foo'));
+ $this->assertTrue($helper->hasFlashes(FlashBag::NOTICE));
- $this->assertEquals('bar', $helper->getFlash('foo'));
- $this->assertEquals('foo', $helper->getFlash('bar', 'foo'));
+ $this->assertEquals(array('bar'), $helper->getFlashes(FlashBag::NOTICE));
- $this->assertNull($helper->getFlash('foobar'));
+ $this->assertEquals(array(FlashBag::NOTICE => array('bar')), $helper->getAllFlashes());
- $this->assertEquals(array('foo' => 'bar'), $helper->getFlashes());
}
public function testGet()
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/PhpEngineTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/PhpEngineTest.php
index cefc38b0efa59..4da48ea6d8636 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/PhpEngineTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/PhpEngineTest.php
@@ -19,6 +19,8 @@
use Symfony\Component\Templating\TemplateNameParser;
use Symfony\Bundle\FrameworkBundle\Templating\GlobalVariables;
use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
+use Symfony\Component\HttpFoundation\FlashBag;
+use Symfony\Component\HttpFoundation\AttributeBag;
class PhpEngineTest extends TestCase
{
@@ -64,7 +66,7 @@ protected function getContainer()
{
$container = new Container();
$request = new Request();
- $session = new Session(new ArraySessionStorage());
+ $session = new Session(new ArraySessionStorage(new AttributeBag(), new FlashBag()));
$request->setSession($session);
$container->set('request', $request);
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/config/framework.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/config/framework.yml
index 18ed6f4d87199..4a7520946bba5 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/config/framework.yml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/config/framework.yml
@@ -10,7 +10,7 @@ framework:
default_locale: en
session:
auto_start: true
- storage_id: session.storage.filesystem
+ storage_id: session.storage.functional_test.file
services:
logger: { class: Symfony\Component\HttpKernel\Log\NullLogger }
diff --git a/src/Symfony/Bundle/TwigBundle/Tests/TwigEngineTest.php b/src/Symfony/Bundle/TwigBundle/Tests/TwigEngineTest.php
index 77b79dd545fb5..34561faa08dbb 100644
--- a/src/Symfony/Bundle/TwigBundle/Tests/TwigEngineTest.php
+++ b/src/Symfony/Bundle/TwigBundle/Tests/TwigEngineTest.php
@@ -13,6 +13,8 @@
use Symfony\Bundle\TwigBundle\TwigEngine;
use Symfony\Component\DependencyInjection\Container;
+use Symfony\Component\HttpFoundation\FlashBag;
+use Symfony\Component\HttpFoundation\AttributeBag;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session;
use Symfony\Component\HttpFoundation\SessionStorage\ArraySessionStorage;
@@ -71,7 +73,7 @@ protected function getContainer()
{
$container = new Container();
$request = new Request();
- $session = new Session(new ArraySessionStorage());
+ $session = new Session(new ArraySessionStorage(new AttributeBag(), new FlashBag()));
$request->setSession($session);
$container->set('request', $request);
diff --git a/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php b/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php
index 3bbffd595612b..812e77eb0c6d9 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php
+++ b/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php
@@ -71,11 +71,6 @@ public function onKernelResponse(FilterResponseEvent $event)
}
if ($response->headers->has('X-Debug-Token') && $response->isRedirect() && $this->interceptRedirects) {
- if (null !== $session = $request->getSession()) {
- // keep current flashes for one more request
- $session->setFlashes($session->getFlashes());
- }
-
$response->setContent($this->templating->render('WebProfilerBundle:Profiler:toolbar_redirect.html.twig', array('location' => $response->headers->get('Location'))));
$response->setStatusCode(200);
$response->headers->remove('Location');
diff --git a/src/Symfony/Component/HttpFoundation/AttributeBag.php b/src/Symfony/Component/HttpFoundation/AttributeBag.php
new file mode 100644
index 0000000000000..32934aa16a6b8
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/AttributeBag.php
@@ -0,0 +1,129 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * This class relates to session attribute storage
+ */
+class AttributeBag implements AttributeBagInterface
+{
+ /**
+ * @var boolean
+ */
+ private $initialized = false;
+
+ /**
+ * @var string
+ */
+ private $storageKey;
+
+ /**
+ * @var array
+ */
+ protected $attributes = array();
+
+ /**
+ * Constructor.
+ *
+ * @param type $storageKey The key used to store flashes in the session.
+ */
+ public function __construct($storageKey = '_sf2_attributes')
+ {
+ $this->storageKey = $storageKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function initialize(array &$attributes)
+ {
+ if ($this->initialized) {
+ return;
+ }
+
+ $this->attributes = &$attributes;
+ $this->initialized = true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getStorageKey()
+ {
+ return $this->storageKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function has($name)
+ {
+ return array_key_exists($name, $this->attributes);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get($name, $default = null)
+ {
+ return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set($name, $value)
+ {
+ $this->attributes[$name] = $value;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function replace(array $attributes)
+ {
+ $this->attributes = array();
+ foreach ($attributes as $key => $value) {
+ $this->set($key, $value);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function remove($name)
+ {
+ $retval = null;
+ if (array_key_exists($name, $this->attributes)) {
+ $retval = $this->attributes[$name];
+ unset($this->attributes[$name]);
+ }
+
+ return $retval;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ $this->attributes = array();
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/AttributeBagInterface.php b/src/Symfony/Component/HttpFoundation/AttributeBagInterface.php
new file mode 100644
index 0000000000000..371ddf6c09356
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/AttributeBagInterface.php
@@ -0,0 +1,39 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+use Symfony\Component\HttpFoundation\AttributeBagInterface;
+use Symfony\Component\HttpFoundation\SessionStorage\AttributeInterface;
+
+/**
+ * Attributes store.
+ *
+ * @author Drak
+ *
+ * @api
+ */
+interface AttributeBagInterface extends AttributeInterface
+{
+ /**
+ * Initializes the AttributeBag
+ *
+ * @param array $attributes
+ */
+ function initialize(array &$attributes);
+
+ /**
+ * Gets the storage key for this bag.
+ *
+ * @return string
+ */
+ function getStorageKey();
+}
diff --git a/src/Symfony/Component/HttpFoundation/FlashBag.php b/src/Symfony/Component/HttpFoundation/FlashBag.php
new file mode 100644
index 0000000000000..fb568fba7efb3
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/FlashBag.php
@@ -0,0 +1,147 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * FlashBag flash message container.
+ *
+ * @author Drak
+ */
+class FlashBag implements FlashBagInterface
+{
+ /**
+ * Flash messages.
+ *
+ * @var array
+ */
+ private $flashes = array();
+
+ /**
+ * @var boolean
+ */
+ private $initialized = false;
+
+ /**
+ * The storage key for flashes in the session
+ *
+ * @var string
+ */
+ private $storageKey;
+
+ /**
+ * Constructor.
+ *
+ * @param type $storageKey The key used to store flashes in the session.
+ */
+ public function __construct($storageKey = '_sf2_flashes')
+ {
+ $this->storageKey = $storageKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function initialize(array &$flashes)
+ {
+ if ($this->initialized) {
+ return;
+ }
+
+ $this->flashes = &$flashes;
+ $this->initialized = true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function add($message, $type = self::NOTICE)
+ {
+ $this->flashes[$type][] = $message;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get($type, $clear = false)
+ {
+ if (!$this->has($type)) {
+ return array();
+ }
+
+ return $clear ? $this->clear($type) : $this->flashes[$type];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set($type, array $array)
+ {
+ $this->flashes[$type] = $array;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function has($type)
+ {
+ return array_key_exists($type, $this->flashes);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getTypes()
+ {
+ return array_keys($this->flashes);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all($clear = false)
+ {
+ return $clear ? $this->clearAll() : $this->flashes;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear($type)
+ {
+ $return = array();
+ if (isset($this->flashes[$type])) {
+ $return = $this->flashes[$type];
+ unset($this->flashes[$type]);
+ }
+
+ return $return;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clearAll()
+ {
+ $return = $this->flashes;
+ $this->flashes = array();
+
+ return $return;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getStorageKey()
+ {
+ return $this->storageKey;
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/FlashBagInterface.php b/src/Symfony/Component/HttpFoundation/FlashBagInterface.php
new file mode 100644
index 0000000000000..3c245f5d74f4c
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/FlashBagInterface.php
@@ -0,0 +1,106 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * FlashBagInterface.
+ *
+ * @author Drak
+ */
+interface FlashBagInterface
+{
+ const INFO = 'info';
+ const NOTICE = 'notice';
+ const WARNING = 'warning';
+ const ERROR = 'error';
+
+ /**
+ * Initializes the FlashBag.
+ *
+ * @param array &$flashes
+ */
+ function initialize(array &$flashes);
+
+ /**
+ * Adds a flash to the stack for a given type.
+ *
+ * @param string $message
+ * @param string $type
+ */
+ function add($message, $type = self::NOTICE);
+
+ /**
+ * Gets flash messages for a given type.
+ *
+ * @param string $type Message category type.
+ * @param boolean $clear Clear the messages after return (default false).
+ *
+ * @return array
+ */
+ function get($type, $clear = false);
+
+ /**
+ * Sets an array of flash messages for a given type.
+ *
+ * @param string $type
+ * @param array $array
+ */
+ function set($type, array $array);
+
+ /**
+ * Has flash messages for a given type?
+ *
+ * @param string $type
+ *
+ * @return boolean
+ */
+ function has($type);
+
+ /**
+ * Returns a list of all defined types.
+ *
+ * @return array
+ */
+ function getTypes();
+
+ /**
+ * Gets all flash messages.
+ *
+ * @param boolean $clear Clear the messages after return (default false).
+ *
+ * @return array
+ */
+ function all($clear = false);
+
+ /**
+ * Clears flash messages for a given type.
+ *
+ * @param string $type
+ *
+ * @return array Returns an array of what was just cleared.
+ */
+ function clear($type);
+
+ /**
+ * Clears all flash messages.
+ *
+ * @return array Array of arrays or array if none.
+ */
+ function clearAll();
+
+ /**
+ * Gets the storage key for this bag.
+ *
+ * @return string
+ */
+ function getStorageKey();
+}
\ No newline at end of file
diff --git a/src/Symfony/Component/HttpFoundation/NamespacedAttributeBag.php b/src/Symfony/Component/HttpFoundation/NamespacedAttributeBag.php
new file mode 100644
index 0000000000000..b175b5f233184
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/NamespacedAttributeBag.php
@@ -0,0 +1,181 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * This class provides structured storage of session attributes using
+ * a name spacing character in the key.
+ *
+ * @author Drak
+ */
+class NamespacedAttributeBag extends AttributeBag
+{
+ /**
+ * Namespace character.
+ *
+ * @var string
+ */
+ private $namespaceCharacter;
+
+ /**
+ * Constructor.
+ *
+ * @param type $storageKey Session storage key.
+ * @param type $namespaceCharacter Namespace character to use in keys.
+ */
+ public function __construct($storageKey = '_sf2_attributes', $namespaceCharacter = '/')
+ {
+ $this->namespaceCharacter = $namespaceCharacter;
+ parent::__construct($storageKey);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function has($name)
+ {
+ $attributes = $this->resolveAttributePath($name);
+ $name = $this->resolveKey($name);
+
+ return array_key_exists($name, $attributes);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get($name, $default = null)
+ {
+ $attributes = $this->resolveAttributePath($name);
+ $name = $this->resolveKey($name);
+
+ return array_key_exists($name, $attributes) ? $attributes[$name] : $default;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set($name, $value)
+ {
+ $attributes = & $this->resolveAttributePath($name, true);
+ $name = $this->resolveKey($name);
+ $attributes[$name] = $value;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function replace(array $attributes)
+ {
+ $this->attributes = array();
+ foreach ($attributes as $key => $value) {
+ $this->set($key, $value);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function remove($name)
+ {
+ $retval = null;
+ $attributes = & $this->resolveAttributePath($name);
+ $name = $this->resolveKey($name);
+ if (array_key_exists($name, $attributes)) {
+ $retval = $attributes[$name];
+ unset($attributes[$name]);
+ }
+
+ return $retval;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ $this->attributes = array();
+ }
+
+ /**
+ * Resolves a path in attributes property and returns it as a reference.
+ *
+ * This method allows structured namespacing of session attributes.
+ *
+ * @param string $name Key name
+ * @param boolean $writeContext Write context, default false
+ *
+ * @return array
+ */
+ protected function &resolveAttributePath($name, $writeContext = false)
+ {
+ $array = & $this->attributes;
+ $name = (strpos($name, $this->namespaceCharacter) === 0) ? substr($name, 1) : $name;
+
+ // Check if there is anything to do, else return
+ if (!$name) {
+ return $array;
+ }
+
+ $parts = explode($this->namespaceCharacter, $name);
+ if (count($parts) < 2) {
+ if (!$writeContext) {
+ return $array;
+ }
+
+ $array[$parts[0]] = array();
+
+ return $array;
+ }
+
+ unset($parts[count($parts)-1]);
+
+ foreach ($parts as $part) {
+ if (!array_key_exists($part, $array)) {
+ if (!$writeContext) {
+ return $array;
+ }
+
+ $array[$part] = array();
+ }
+
+ $array = & $array[$part];
+ }
+
+ return $array;
+ }
+
+ /**
+ * Resolves the key from the name.
+ *
+ * This is the last part in a dot separated string.
+ *
+ * @param string $name
+ *
+ * @return string
+ */
+ protected function resolveKey($name)
+ {
+ if (strpos($name, $this->namespaceCharacter) !== false) {
+ $name = substr($name, strrpos($name, $this->namespaceCharacter)+1, strlen($name));
+ }
+
+ return $name;
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php
index d029901b54f0d..42ce428867269 100644
--- a/src/Symfony/Component/HttpFoundation/Request.php
+++ b/src/Symfony/Component/HttpFoundation/Request.php
@@ -75,62 +75,62 @@ class Request
* @var string
*/
protected $content;
-
+
/**
* @var string
*/
protected $languages;
-
+
/**
* @var string
*/
protected $charsets;
-
+
/**
* @var string
*/
protected $acceptableContentTypes;
-
+
/**
* @var string
*/
protected $pathInfo;
-
+
/**
* @var string
*/
protected $requestUri;
-
+
/**
* @var string
*/
protected $baseUrl;
-
+
/**
* @var string
*/
protected $basePath;
-
+
/**
* @var string
*/
protected $method;
-
+
/**
* @var string
*/
protected $format;
-
+
/**
- * @var \Symfony\Component\HttpFoundation\Session
+ * @var \Symfony\Component\HttpFoundation\SessionInterface
*/
protected $session;
-
+
/**
* @var string
*/
protected $locale;
-
+
/**
* @var string
*/
@@ -461,7 +461,7 @@ public function get($key, $default = null, $deep = false)
/**
* Gets the Session.
*
- * @return Session|null The session
+ * @return SessionInterface|null The session
*
* @api
*/
@@ -499,11 +499,11 @@ public function hasSession()
/**
* Sets the Session.
*
- * @param Session $session The Session
+ * @param SessionInterface $session The Session
*
* @api
*/
- public function setSession(Session $session)
+ public function setSession(SessionInterface $session)
{
$this->session = $session;
}
@@ -956,16 +956,16 @@ public function setRequestFormat($format)
*
* @api
*/
- public function getContentType()
+ public function getContentType()
{
return $this->getFormat($this->server->get('CONTENT_TYPE'));
}
/**
* Sets the default locale.
- *
- * @param string $locale
- *
+ *
+ * @param string $locale
+ *
* @api
*/
public function setDefaultLocale($locale)
@@ -975,9 +975,9 @@ public function setDefaultLocale($locale)
/**
* Sets the locale.
- *
- * @param string $locale
- *
+ *
+ * @param string $locale
+ *
* @api
*/
public function setLocale($locale)
@@ -987,7 +987,7 @@ public function setLocale($locale)
/**
* Get the locale.
- *
+ *
* @return string
*/
public function getLocale()
@@ -1233,8 +1233,8 @@ protected function prepareRequestUri()
/**
* Prepares the base URL.
- *
- * @return string
+ *
+ * @return string
*/
protected function prepareBaseUrl()
{
@@ -1373,8 +1373,8 @@ static protected function initializeFormats()
/**
* Sets the default PHP locale.
- *
- * @param string $locale
+ *
+ * @param string $locale
*/
private function setPhpDefaultLocale($locale)
{
diff --git a/src/Symfony/Component/HttpFoundation/Session.php b/src/Symfony/Component/HttpFoundation/Session.php
index 721a6c7240b99..3b8c0cd0d50fb 100644
--- a/src/Symfony/Component/HttpFoundation/Session.php
+++ b/src/Symfony/Component/HttpFoundation/Session.php
@@ -12,36 +12,33 @@
namespace Symfony\Component\HttpFoundation;
use Symfony\Component\HttpFoundation\SessionStorage\SessionStorageInterface;
+use Symfony\Component\HttpFoundation\FlashBagInterface;
/**
* Session.
*
* @author Fabien Potencier
+ * @author Drak
*
* @api
*/
-class Session implements \Serializable
+class Session implements SessionInterface
{
+ /**
+ * Storage driver.
+ *
+ * @var SessionStorageInterface
+ */
protected $storage;
- protected $started;
- protected $attributes;
- protected $flashes;
- protected $oldFlashes;
- protected $closed;
/**
* Constructor.
*
- * @param SessionStorageInterface $storage A SessionStorageInterface instance
+ * @param SessionStorageInterface $storage A SessionStorageInterface instance.
*/
public function __construct(SessionStorageInterface $storage)
{
$this->storage = $storage;
- $this->flashes = array();
- $this->oldFlashes = array();
- $this->attributes = array();
- $this->started = false;
- $this->closed = false;
}
/**
@@ -51,23 +48,7 @@ public function __construct(SessionStorageInterface $storage)
*/
public function start()
{
- if (true === $this->started) {
- return;
- }
-
$this->storage->start();
-
- $attributes = $this->storage->read('_symfony2');
-
- if (isset($attributes['attributes'])) {
- $this->attributes = $attributes['attributes'];
- $this->flashes = $attributes['flashes'];
-
- // flag current flash messages to be removed at shutdown
- $this->oldFlashes = $this->flashes;
- }
-
- $this->started = true;
}
/**
@@ -81,14 +62,14 @@ public function start()
*/
public function has($name)
{
- return array_key_exists($name, $this->attributes);
+ return $this->storage->getAttributes()->has($name);
}
/**
* Returns an attribute.
*
- * @param string $name The attribute name
- * @param mixed $default The default value
+ * @param string $name The attribute name
+ * @param mixed $default The default value
*
* @return mixed
*
@@ -96,7 +77,7 @@ public function has($name)
*/
public function get($name, $default = null)
{
- return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
+ return $this->storage->getAttributes()->get($name, $default);
}
/**
@@ -109,11 +90,7 @@ public function get($name, $default = null)
*/
public function set($name, $value)
{
- if (false === $this->started) {
- $this->start();
- }
-
- $this->attributes[$name] = $value;
+ $this->storage->getAttributes()->set($name, $value);
}
/**
@@ -125,7 +102,7 @@ public function set($name, $value)
*/
public function all()
{
- return $this->attributes;
+ return $this->storage->getAttributes()->all();
}
/**
@@ -137,11 +114,7 @@ public function all()
*/
public function replace(array $attributes)
{
- if (false === $this->started) {
- $this->start();
- }
-
- $this->attributes = $attributes;
+ $this->storage->getAttributes()->replace($attributes);
}
/**
@@ -153,13 +126,7 @@ public function replace(array $attributes)
*/
public function remove($name)
{
- if (false === $this->started) {
- $this->start();
- }
-
- if (array_key_exists($name, $this->attributes)) {
- unset($this->attributes[$name]);
- }
+ return $this->storage->getAttributes()->remove($name);
}
/**
@@ -169,12 +136,7 @@ public function remove($name)
*/
public function clear()
{
- if (false === $this->started) {
- $this->start();
- }
-
- $this->attributes = array();
- $this->flashes = array();
+ $this->storage->getAttributes()->clear();
}
/**
@@ -184,7 +146,6 @@ public function clear()
*/
public function invalidate()
{
- $this->clear();
$this->storage->regenerate(true);
}
@@ -199,160 +160,82 @@ public function migrate()
$this->storage->regenerate();
}
+ /**
+ * {@inheritdoc}
+ */
+ public function save()
+ {
+ $this->storage->save();
+ }
+
/**
* Returns the session ID
*
- * @return mixed The session ID
+ * @return mixed The session ID
*
* @api
*/
public function getId()
{
- if (false === $this->started) {
- $this->start();
- }
-
return $this->storage->getId();
}
/**
- * Gets the flash messages.
+ * Implements the \Serialize interface.
*
- * @return array
+ * @return SessionStorageInterface
*/
- public function getFlashes()
+ public function serialize()
{
- return $this->flashes;
+ return serialize($this->storage);
}
/**
- * Sets the flash messages.
+ * Implements the \Serialize interface.
*
- * @param array $values
+ * @throws \InvalidArgumentException If the passed string does not unserialize to an instance of SessionStorageInterface
*/
- public function setFlashes($values)
+ public function unserialize($serialized)
{
- if (false === $this->started) {
- $this->start();
+ $storage = unserialize($serialized);
+ if (!$storage instanceof SessionStorageInterface) {
+ throw new \InvalidArgumentException('Serialized data did not return a valid instance of SessionStorageInterface');
}
- $this->flashes = $values;
- $this->oldFlashes = array();
+ $this->storage = $storage;
}
/**
- * Gets a flash message.
- *
- * @param string $name
- * @param string|null $default
+ * Gets the flash messages driver.
*
- * @return string
+ * @return FlashBagInterface
*/
- public function getFlash($name, $default = null)
+ public function getFlashes()
{
- return array_key_exists($name, $this->flashes) ? $this->flashes[$name] : $default;
+ return $this->storage->getFlashes();
}
/**
- * Sets a flash message.
+ * Adds a flash to the stack for a given type.
*
- * @param string $name
- * @param string $value
+ * @param string $message
+ * @param string $type
*/
- public function setFlash($name, $value)
+ public function addFlash($message, $type = FlashBagInterface::NOTICE)
{
- if (false === $this->started) {
- $this->start();
- }
-
- $this->flashes[$name] = $value;
- unset($this->oldFlashes[$name]);
+ $this->storage->getFlashes()->add($message, $type);
}
/**
- * Checks whether a flash message exists.
+ * Gets flash messages for a given type.
*
- * @param string $name
+ * @param string $type Message category type.
+ * @param boolean $clear Clear the messages after get (default true).
*
- * @return Boolean
- */
- public function hasFlash($name)
- {
- if (false === $this->started) {
- $this->start();
- }
-
- return array_key_exists($name, $this->flashes);
- }
-
- /**
- * Removes a flash message.
- *
- * @param string $name
- */
- public function removeFlash($name)
- {
- if (false === $this->started) {
- $this->start();
- }
-
- unset($this->flashes[$name]);
- }
-
- /**
- * Removes the flash messages.
- */
- public function clearFlashes()
- {
- if (false === $this->started) {
- $this->start();
- }
-
- $this->flashes = array();
- $this->oldFlashes = array();
- }
-
- public function save()
- {
- if (false === $this->started) {
- $this->start();
- }
-
- $this->flashes = array_diff_key($this->flashes, $this->oldFlashes);
-
- $this->storage->write('_symfony2', array(
- 'attributes' => $this->attributes,
- 'flashes' => $this->flashes,
- ));
- }
-
- /**
- * This method should be called when you don't want the session to be saved
- * when the Session object is garbaged collected (useful for instance when
- * you want to simulate the interaction of several users/sessions in a single
- * PHP process).
+ * @return array
*/
- public function close()
- {
- $this->closed = true;
- }
-
- public function __destruct()
- {
- if (true === $this->started && !$this->closed) {
- $this->save();
- }
- }
-
- public function serialize()
- {
- return serialize($this->storage);
- }
-
- public function unserialize($serialized)
+ public function getFlash($type, $clear = true)
{
- $this->storage = unserialize($serialized);
- $this->attributes = array();
- $this->started = false;
+ return $this->storage->getFlashes()->get($type, $clear);
}
}
diff --git a/src/Symfony/Component/HttpFoundation/SessionInterface.php b/src/Symfony/Component/HttpFoundation/SessionInterface.php
new file mode 100644
index 0000000000000..fec128f6943ca
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/SessionInterface.php
@@ -0,0 +1,79 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+use Symfony\Component\HttpFoundation\SessionStorage\AttributeInterface;
+use Symfony\Component\HttpFoundation\FlashBagInterface;
+
+/**
+ * Interface for the session.
+ *
+ * @author Drak
+ */
+interface SessionInterface extends AttributeInterface, \Serializable
+{
+ /**
+ * Starts the session storage.
+ *
+ * @throws \RutimeException If session fails to start.
+ */
+ function start();
+
+ /**
+ * Invalidates the current session.
+ *
+ * @return boolean True if session invalidated, false if error.
+ */
+ function invalidate();
+
+ /**
+ * Migrates the current session to a new session id while maintaining all
+ * session attributes.
+ *
+ * @return boolean True if session migrated, false if error.
+ */
+ function migrate();
+
+ /**
+ * Force the session to be saved.
+ *
+ * This method is generally not required for real sessions as
+ * the session will be automatically saved at the end of
+ * code execution.
+ */
+ function save();
+
+ /**
+ * Gets the flash messages driver.
+ *
+ * @return FlashBagInterface
+ */
+ function getFlashes();
+
+ /**
+ * Adds a flash to the stack for a given type.
+ *
+ * @param string $message
+ * @param string $type
+ */
+ function addFlash($message, $type = FlashBagInterface::NOTICE);
+
+ /**
+ * Gets flash messages for a given type.
+ *
+ * @param string $type Message category type.
+ * @param boolean $clear Clear the messages after get (default true).
+ *
+ * @return array
+ */
+ function getFlash($type, $clear = true);
+}
diff --git a/src/Symfony/Component/HttpFoundation/SessionStorage/AbstractSessionStorage.php b/src/Symfony/Component/HttpFoundation/SessionStorage/AbstractSessionStorage.php
new file mode 100644
index 0000000000000..a3055706d0e2b
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/SessionStorage/AbstractSessionStorage.php
@@ -0,0 +1,285 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\SessionStorage;
+
+use Symfony\Component\HttpFoundation\FlashBag;
+use Symfony\Component\HttpFoundation\FlashBagInterface;
+use Symfony\Component\HttpFoundation\AttributeBag;
+use Symfony\Component\HttpFoundation\AttributeBagInterface;
+
+/**
+ * This provides a base class for session attribute storage.
+ *
+ * @author Drak
+ */
+abstract class AbstractSessionStorage implements SessionStorageInterface
+{
+ /**
+ * @var \Symfony\Component\HttpFoundation\FlashBagInterface
+ */
+ protected $flashBag;
+
+ /**
+ * @var \Symfony\Component\HttpFoundation\AttributeBagInterface
+ */
+ protected $attributeBag;
+
+ /**
+ * @var array
+ */
+ protected $options;
+
+ /**
+ * @var boolean
+ */
+ protected $started = false;
+
+ /**
+ * Constructor.
+ *
+ * Depending on how you want the storage driver to behave you probably
+ * want top override this constructor entirely.
+ *
+ * List of options for $options array with their defaults from
+ * See session.* for values at http://www.php.net/manual/en/ini.list.php
+ * But we omit 'session.` from the beginning of the keys.
+ *
+ * auto_start, "0"
+ * cookie_domain, ""
+ * cookie_httponly, ""
+ * cookie_lifetime, "0"
+ * cookie_path, "/"
+ * cookie_secure, ""
+ * entropy_file, ""
+ * entropy_length, "0"
+ * gc_divisor, "100"
+ * gc_maxlifetime, "1440"
+ * gc_probability, "1"
+ * hash_bits_per_character, "4"
+ * hash_function, "0"
+ * name, "PHPSESSID"
+ * referer_check, ""
+ * save_path, ""
+ * serialize_handler, "php"
+ * use_cookies, "1"
+ * use_only_cookies, "1"
+ * use_trans_sid, "0"
+ *
+ * @param AttributeBagInterface $attributes An AttributeBagInterface instance, (defaults null for default AttributeBag)
+ * @param FlashBagInterface $flashes A FlashBagInterface instance (defaults null for default FlashBag)
+ * @param array $options Session configuration options.
+ */
+ public function __construct(AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null, array $options = array())
+ {
+ $this->attributeBag = $attributes ? $attributes : new AttributeBag();
+ $this->flashBag = $flashes ? $flashes : new FlashBag();
+ $this->setOptions($options);
+ $this->registerSaveHandlers();
+ $this->registerShutdownFunction();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFlashes()
+ {
+ if (!$this->started) {
+ $this->start();
+ }
+
+ return $this->flashBag;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getAttributes()
+ {
+ if (!$this->started) {
+ $this->start();
+ }
+
+ return $this->attributeBag;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function start()
+ {
+ if ($this->started) {
+ // Nothing to do as the session is already started.
+ return;
+ }
+
+ // generate random session ID
+ if (!session_id()) {
+ session_id($this->generateSessionId());
+ }
+
+ // start the session
+ if (!session_start()) {
+ throw new \RuntimeException('Failed to start the session');
+ }
+
+ // after starting the session, PHP retrieves the session from whatever handlers were set
+ // either PHP's internal, or the ones we set using sssion_set_save_handler(). PHP takes
+ // the return value from the sessionRead() handler and populates $_SESSION with it automatically.
+ $key = $this->attributeBag->getStorageKey();
+ $_SESSION[$key] = isset($_SESSION[$key]) ? $_SESSION[$key] : array();
+ $this->attributeBag->initialize($_SESSION[$key]);
+
+ $key = $this->flashBag->getStorageKey();
+ $_SESSION[$key] = isset($_SESSION[$key]) ? $_SESSION[$key] : array();
+ $this->flashBag->initialize($_SESSION[$key]);
+
+ $this->started = true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getId()
+ {
+ if (!$this->started) {
+ return ''; // returning empty is consistent with session_id() behaviour
+ }
+
+ return session_id();
+ }
+
+ /**
+ * Regenerates the session.
+ *
+ * This method will regenerate the session ID and optionally
+ * destroy the old ID. Session regeneration should be done
+ * periodically and for example, should be done when converting
+ * an anonymous session to a logged in user session.
+ *
+ * @param boolean $destroy
+ *
+ * @return boolean Returns true on success or false on failure.
+ */
+ public function regenerate($destroy = false)
+ {
+ return session_regenerate_id($destroy);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function save()
+ {
+ session_write_close();
+ }
+
+ /**
+ * Sets the session.* ini variables.
+ *
+ * Note we omit 'session.' from the beginning of the keys.
+ *
+ * @param array $options
+ */
+ protected function setOptions(array $options)
+ {
+ $cookieDefaults = session_get_cookie_params();
+ $this->options = array_merge(array(
+ 'lifetime' => $cookieDefaults['lifetime'],
+ 'path' => $cookieDefaults['path'],
+ 'domain' => $cookieDefaults['domain'],
+ 'secure' => $cookieDefaults['secure'],
+ 'httponly' => isset($cookieDefaults['httponly']) ? $cookieDefaults['httponly'] : false,
+ ), $options);
+
+ // Unless session.cache_limiter has been set explicitly, disable it
+ // because this is managed by HeaderBag directly (if used).
+ if (!array_key_exists('session.cache_limiter', $this->options)) {
+ $this->options['session.cache_limiter'] = 0;
+ }
+
+ // See session.* for values at http://www.php.net/manual/en/ini.list.php
+ foreach ($this->options as $key => $value) {
+ ini_set('session.'.$key, $value);
+ }
+ }
+
+ /**
+ * Registers this storage device for PHP session handling.
+ *
+ * PHP requires session save handlers to be set, either it's own, or custom ones.
+ * There are some defaults set automatically when PHP starts, but these can be overriden
+ * using this command if you need anything other than PHP's default handling.
+ *
+ * When the session starts, PHP will call the sessionRead() handler which should return an array
+ * of any session attributes. PHP will then populate these into $_SESSION.
+ *
+ * When PHP shuts down, the sessionWrite() handler is called and will pass the $_SESSION contents
+ * to be stored.
+ *
+ * When a session is specifically destroyed, PHP will call the sessionDestroy() handler with the
+ * session ID. This happens when the session is regenerated for example and th handler
+ * MUST delete the session by ID from the persistent storage immediately.
+ *
+ * PHP will call sessionGc() from time to time to expire any session records according to the
+ * set max lifetime of a session. This routine should delete all records from persistent
+ * storage which were last accessed longer than the $lifetime.
+ *
+ * PHP sessionOpen() and sessionClose() are pretty much redundant and can just return true.
+ *
+ * NOTE:
+ *
+ * To use PHP native save handlers, override this method using ini_set with
+ * session.save_handlers and session.save_path e.g.
+ *
+ * ini_set('session.save_handlers', 'files');
+ * ini_set('session.save_path', /tmp');
+ *
+ * @see http://php.net/manual/en/function.session-set-save-handler.php
+ * @see SessionSaveHandlerInterface
+ */
+ protected function registerSaveHandlers()
+ {
+ // note this can be reset to PHP's control using ini_set('session.save_handler', 'files');
+ // so long as ini_set() is called before the session is started.
+ if ($this instanceof SessionSaveHandlerInterface) {
+ session_set_save_handler(
+ array($this, 'sessionOpen'),
+ array($this, 'sessionClose'),
+ array($this, 'sessionRead'),
+ array($this, 'sessionWrite'),
+ array($this, 'sessionDestroy'),
+ array($this, 'sessionGc')
+ );
+ }
+ }
+
+ /**
+ * Registers PHP shutdown function.
+ *
+ * This method is required to avoid strange issues when using PHP objects as
+ * session save handlers.
+ */
+ protected function registerShutdownFunction()
+ {
+ register_shutdown_function('session_write_close');
+ }
+
+ /**
+ * Generates a session ID.
+ *
+ * @return string
+ */
+ protected function generateSessionId()
+ {
+ return sha1(uniqid(mt_rand(), true));
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/SessionStorage/ArraySessionStorage.php b/src/Symfony/Component/HttpFoundation/SessionStorage/ArraySessionStorage.php
index aeda2d3c7e3a9..f2b3d1ae72456 100644
--- a/src/Symfony/Component/HttpFoundation/SessionStorage/ArraySessionStorage.php
+++ b/src/Symfony/Component/HttpFoundation/SessionStorage/ArraySessionStorage.php
@@ -11,57 +11,88 @@
namespace Symfony\Component\HttpFoundation\SessionStorage;
+use Symfony\Component\HttpFoundation\AttributeBagInterface;
+use Symfony\Component\HttpFoundation\FlashBagInterface;
+
/**
* ArraySessionStorage mocks the session for unit tests.
*
- * When doing functional testing, you should use FilesystemSessionStorage instead.
+ * No PHP session is actually started since a session can be initialized
+ * and shutdown only once per PHP execution cycle.
+ *
+ * When doing functional testing, you should use FunctionalTestFileSessionStorage instead.
*
* @author Fabien Potencier
* @author Bulat Shakirzyanov
+ * @author Drak
*/
-
-class ArraySessionStorage implements SessionStorageInterface
+class ArraySessionStorage extends AbstractSessionStorage
{
/**
- * Storage data.
- *
+ * @var string
+ */
+ protected $sessionId;
+
+ /**
* @var array
*/
- private $data = array();
+ private $attributes = array();
/**
- * {@inheritdoc}
+ * @var array
*/
- public function read($key, $default = null)
- {
- return array_key_exists($key, $this->data) ? $this->data[$key] : $default;
- }
+ private $flashes = array();
/**
- * {@inheritdoc}
+ * Constructor.
+ *
+ * There is no option to set session options here because this object does not start the PHP session.
+ * This constructor is for easy testing, simply use `$storage = new AttributeBag()` unless you require
+ * specific implementations of Bag interfaces.
+ *
+ * @param AttributeBagInterface $attributes AttributeBagInterface, defaults to null for AttributeBag default
+ * @param FlashBagInterface $flashes FlashBagInterface, defaults to null for FlashBag default
*/
- public function regenerate($destroy = false)
+ public function __construct(AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null)
{
- if ($destroy) {
- $this->data = array();
- }
-
- return true;
+ $this->attributeBag = $attributes ? $attributes : new AttributeBag();
+ $this->flashBag = $flashes ? $flashes : new FlashBag();
}
/**
* {@inheritdoc}
*/
- public function remove($key)
+ public function start()
{
- unset($this->data[$key]);
+ if ($this->started) {
+ return;
+ }
+
+ $this->started = true;
+ $this->attributeBag->initialize($this->attributes);
+ $this->flashBag->initialize($this->flashes);
+ $this->sessionId = $this->generateSessionId();
+ session_id($this->sessionId);
}
/**
* {@inheritdoc}
*/
- public function start()
+ public function regenerate($destroy = false)
{
+ if (!$this->started) {
+ $this->start();
+ }
+
+ if ($destroy) {
+ $this->attributeBag->clear();
+ $this->flashBag->clearAll();
+ }
+
+ $this->sessionId = $this->generateSessionId();
+ session_id($this->sessionId);
+
+ return true;
}
/**
@@ -69,13 +100,18 @@ public function start()
*/
public function getId()
{
+ if (!$this->started) {
+ return '';
+ }
+
+ return $this->sessionId;
}
/**
* {@inheritdoc}
*/
- public function write($key, $data)
+ public function save()
{
- $this->data[$key] = $data;
+ // nothing to do since we don't persist the session data
}
-}
+}
\ No newline at end of file
diff --git a/src/Symfony/Component/HttpFoundation/SessionStorage/AttributeInterface.php b/src/Symfony/Component/HttpFoundation/SessionStorage/AttributeInterface.php
new file mode 100644
index 0000000000000..176cb8bc8b0a2
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/SessionStorage/AttributeInterface.php
@@ -0,0 +1,73 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\SessionStorage;
+
+/**
+ * Interface for the session.
+ *
+ * @author Drak
+ */
+interface AttributeInterface
+{
+ /**
+ * Checks if an attribute is defined.
+ *
+ * @param string $name The attribute name
+ *
+ * @return Boolean true if the attribute is defined, false otherwise
+ */
+ function has($name);
+
+ /**
+ * Returns an attribute.
+ *
+ * @param string $name The attribute name
+ * @param mixed $default The default value if not found.
+ *
+ * @return mixed
+ */
+ function get($name, $default = null);
+
+ /**
+ * Sets an attribute.
+ *
+ * @param string $name
+ * @param mixed $value
+ */
+ function set($name, $value);
+
+ /**
+ * Returns attributes.
+ *
+ * @return array Attributes
+ */
+ function all();
+
+ /**
+ * Sets attributes.
+ *
+ * @param array $attributes Attributes
+ */
+ function replace(array $attributes);
+
+ /**
+ * Removes an attribute.
+ *
+ * @param string $name
+ */
+ function remove($name);
+
+ /**
+ * Clears all attributes.
+ */
+ function clear();
+}
diff --git a/src/Symfony/Component/HttpFoundation/SessionStorage/FilesystemSessionStorage.php b/src/Symfony/Component/HttpFoundation/SessionStorage/FilesystemSessionStorage.php
deleted file mode 100644
index 55f626e72e7ef..0000000000000
--- a/src/Symfony/Component/HttpFoundation/SessionStorage/FilesystemSessionStorage.php
+++ /dev/null
@@ -1,191 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\HttpFoundation\SessionStorage;
-
-/**
- * FilesystemSessionStorage simulates sessions for functional tests.
- *
- * This storage does not start the session (session_start())
- * as it is not "available" when running tests on the command line.
- *
- * @author Fabien Potencier
- *
- * @api
- */
-class FilesystemSessionStorage extends NativeSessionStorage
-{
- /**
- * File path.
- *
- * @var string
- */
- private $path;
-
- /**
- * Data.
- *
- * @var array
- */
- private $data;
-
- /**
- * Session started flag.
- *
- * @var boolean
- */
- private $started;
-
- /**
- * Constructor.
- */
- public function __construct($path, array $options = array())
- {
- $this->path = $path;
- $this->started = false;
-
- parent::__construct($options);
- }
-
- /**
- * Starts the session.
- *
- * @api
- */
- public function start()
- {
- if ($this->started) {
- return;
- }
-
- session_set_cookie_params(
- $this->options['lifetime'],
- $this->options['path'],
- $this->options['domain'],
- $this->options['secure'],
- $this->options['httponly']
- );
-
- if (!ini_get('session.use_cookies') && isset($this->options['id']) && $this->options['id'] && $this->options['id'] != session_id()) {
- session_id($this->options['id']);
- }
-
- if (!session_id()) {
- session_id(hash('md5', uniqid(mt_rand(), true)));
- }
-
- $file = $this->path.'/'.session_id().'.session';
-
- $this->data = is_file($file) ? unserialize(file_get_contents($file)) : array();
- $this->started = true;
- }
-
- /**
- * Returns the session ID
- *
- * @return mixed The session ID
- *
- * @throws \RuntimeException If the session was not started yet
- *
- * @api
- */
- public function getId()
- {
- if (!$this->started) {
- throw new \RuntimeException('The session must be started before reading its ID');
- }
-
- return session_id();
- }
-
- /**
- * Reads data from this storage.
- *
- * The preferred format for a key is directory style so naming conflicts can be avoided.
- *
- * @param string $key A unique key identifying your data
- *
- * @return mixed Data associated with the key
- *
- * @throws \RuntimeException If an error occurs while reading data from this storage
- *
- * @api
- */
- public function read($key, $default = null)
- {
- return array_key_exists($key, $this->data) ? $this->data[$key] : $default;
- }
-
- /**
- * Removes data from this storage.
- *
- * The preferred format for a key is directory style so naming conflicts can be avoided.
- *
- * @param string $key A unique key identifying your data
- *
- * @return mixed Data associated with the key
- *
- * @throws \RuntimeException If an error occurs while removing data from this storage
- *
- * @api
- */
- public function remove($key)
- {
- $retval = $this->data[$key];
-
- unset($this->data[$key]);
-
- return $retval;
- }
-
- /**
- * Writes data to this storage.
- *
- * The preferred format for a key is directory style so naming conflicts can be avoided.
- *
- * @param string $key A unique key identifying your data
- * @param mixed $data Data associated with your key
- *
- * @throws \RuntimeException If an error occurs while writing to this storage
- *
- * @api
- */
- public function write($key, $data)
- {
- $this->data[$key] = $data;
-
- if (!is_dir($this->path)) {
- mkdir($this->path, 0777, true);
- }
-
- file_put_contents($this->path.'/'.session_id().'.session', serialize($this->data));
- }
-
- /**
- * Regenerates id that represents this storage.
- *
- * @param Boolean $destroy Destroy session when regenerating?
- *
- * @return Boolean True if session regenerated, false if error
- *
- * @throws \RuntimeException If an error occurs while regenerating this storage
- *
- * @api
- */
- public function regenerate($destroy = false)
- {
- if ($destroy) {
- $this->data = array();
- }
-
- return true;
- }
-}
diff --git a/src/Symfony/Component/HttpFoundation/SessionStorage/FunctionalTestFileSessionStorage.php b/src/Symfony/Component/HttpFoundation/SessionStorage/FunctionalTestFileSessionStorage.php
new file mode 100644
index 0000000000000..e5c0fdb716ed8
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/SessionStorage/FunctionalTestFileSessionStorage.php
@@ -0,0 +1,149 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\SessionStorage;
+
+use Symfony\Component\HttpFoundation\AttributeBagInterface;
+use Symfony\Component\HttpFoundation\FlashBagInterface;
+
+/**
+ * FunctionalTestFileSessionStorage is used to mock sessions for
+ * functional testing when done in a single PHP process.
+ *
+ * No PHP session is actually started since a session can be initialized
+ * and shutdown only once per PHP execution cycle.
+ *
+ * @author Drak
+ */
+class FunctionalTestFileSessionStorage extends ArraySessionStorage
+{
+ /**
+ * @var array
+ */
+ private $data;
+
+ /**
+ * @var string
+ */
+ private $savePath;
+
+ /**
+ * Constructor.
+ *
+ * @param string $savePath Path of directory to save session files.
+ * @param AttributeBagInterface $attributes An AttributeBagInterface instance, (defaults null for default AttributeBag)
+ * @param FlashBagInterface $flashes A FlashBagInterface instance (defaults null for default FlashBag)
+ *
+ * @see AbstractSessionStorage::__construct()
+ */
+ public function __construct($savePath = null, AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null)
+ {
+ if (is_null($savePath)) {
+ $savePath = sys_get_temp_dir();
+ }
+
+ if (!is_dir($savePath)) {
+ mkdir($savePath, 0777, true);
+ }
+
+ $this->savePath = $savePath;
+
+ parent::__construct($attributes, $flashes);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function start()
+ {
+ if ($this->started) {
+ return;
+ }
+
+ if (!ini_get('session.use_cookies') && isset($this->options['id']) && $this->options['id'] && $this->options['id'] != session_id()) {
+ session_id($this->options['id']);
+ }
+
+ if (!session_id()) {
+ session_id($this->generateSessionId());
+ }
+
+ $this->sessionId = session_id();
+
+ $this->read();
+
+ $this->started = true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function regenerate($destroy = false)
+ {
+ parent::regenerate($destroy);
+
+ // bacause we have no GC routines, we can just GC the session now manually
+ $this->destroy();
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getId()
+ {
+ if (!$this->started) {
+ return '';
+ }
+
+ return $this->sessionId;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function save()
+ {
+ file_put_contents($this->getFilePath(), serialize($this->data));
+ }
+
+ public function read()
+ {
+ $filePath = $this->getFilePath();
+ $this->data = is_readable($filePath) && is_file($filePath) ? unserialize(file_get_contents($filePath)) : array();
+
+ $key = $this->attributeBag->getStorageKey();
+ $this->data[$key] = isset($this->data[$key]) ? $this->data[$key] : array();
+ $this->attributeBag->initialize($this->data[$key]);
+
+ $key = $this->flashBag->getStorageKey();
+ $this->data[$key] = isset($this->data[$key]) ? $this->data[$key] : array();
+ $this->flashBag->initialize($this->data[$key]);
+ }
+
+ public function destroy()
+ {
+ if (is_file($this->getFilePath())) {
+ unlink($this->getFilePath());
+ }
+ }
+
+ /**
+ * Calculate path to file.
+ *
+ * @return string File path
+ */
+ protected function getFilePath()
+ {
+ return $this->savePath.'/'.$this->sessionId.'.session';
+ }
+}
\ No newline at end of file
diff --git a/src/Symfony/Component/HttpFoundation/SessionStorage/MemcacheSessionStorage.php b/src/Symfony/Component/HttpFoundation/SessionStorage/MemcacheSessionStorage.php
new file mode 100644
index 0000000000000..99fc10d446258
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/SessionStorage/MemcacheSessionStorage.php
@@ -0,0 +1,137 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\SessionStorage;
+
+use Symfony\Component\HttpFoundation\AttributeBagInterface;
+use Symfony\Component\HttpFoundation\FlashBagInterface;
+
+/**
+ * MemcacheSessionStorage.
+ *
+ * @author Drak
+ */
+class MemcacheSessionStorage extends AbstractSessionStorage implements SessionSaveHandlerInterface
+{
+ /**
+ * Memcache driver.
+ *
+ * @var Memcache
+ */
+ private $memcache;
+
+ /**
+ * Configuration options.
+ *
+ * @var array
+ */
+ private $memcacheOptions;
+
+ /**
+ * Key prefix for shared environments.
+ *
+ * @var string
+ */
+ private $prefix;
+
+ /**
+ * Constructor.
+ *
+ * @param \Memcache $memcache A \Memcache instance
+ * @param array $memcacheOptions An associative array of Memcachge options
+ * @param array $options Session configuration options.
+ * @param AttributeBagInterface $attributes An AttributeBagInterface instance, (defaults null for default AttributeBag)
+ * @param FlashBagInterface $flashes A FlashBagInterface instance (defaults null for default FlashBag)
+ *
+ * @see AbstractSessionStorage::__construct()
+ */
+ public function __construct(\Memcache $memcache, array $memcacheOptions = array(), array $options = array(), AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null)
+ {
+ $this->memcache = $memcache;
+
+ // defaults
+ if (!isset($memcacheOptions['serverpool'])) {
+ $memcacheOptions['serverpool'] = array('host' => '127.0.0.1', 'port' => 11211, 'timeout' => 1, 'persistent' => false, 'weight' => 1);
+ }
+ $memcacheOptions['expiretime'] = isset($memcacheOptions['expiretime']) ? (int)$memcacheOptions['expiretime'] : 86400;
+ $this->prefix = isset($memcachedOptions['prefix']) ? $memcachedOptions['prefix'] : 'sf2s';
+
+ $this->memcacheOptions = $memcacheOptions;
+
+ parent::__construct($attributes, $flashes, $options);
+ }
+
+ protected function addServer(array $server)
+ {
+ if (array_key_exists('host', $server)) {
+ throw new \InvalidArgumentException('host key must be set');
+ }
+ $server['port'] = isset($server['port']) ? (int)$server['port'] : 11211;
+ $server['timeout'] = isset($server['timeout']) ? (int)$server['timeout'] : 1;
+ $server['presistent'] = isset($server['presistent']) ? (bool)$server['presistent'] : false;
+ $server['weight'] = isset($server['weight']) ? (bool)$server['weight'] : 1;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sessionOpen($savePath, $sessionName)
+ {
+ foreach ($this->memcacheOptions['serverpool'] as $server) {
+ $this->addServer($server);
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sessionClose()
+ {
+ return $this->memcache->close();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sessionRead($sessionId)
+ {
+ $result = $this->memcache->get($this->prefix.$sessionId);
+
+ return ($result) ? $result : '';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sessionWrite($sessionId, $data)
+ {
+ return $this->memcache->set($this->prefix.$sessionId, $data, $this->memcacheOptions['expiretime']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sessionDestroy($sessionId)
+ {
+ return $this->memcache->delete($this->prefix.$sessionId);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sessionGc($lifetime)
+ {
+ // not required here because memcache will auto expire the records anyhow.
+ return true;
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/SessionStorage/MemcachedSessionStorage.php b/src/Symfony/Component/HttpFoundation/SessionStorage/MemcachedSessionStorage.php
new file mode 100644
index 0000000000000..d54a9139d1665
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/SessionStorage/MemcachedSessionStorage.php
@@ -0,0 +1,138 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\SessionStorage;
+
+use Symfony\Component\HttpFoundation\AttributeBagInterface;
+use Symfony\Component\HttpFoundation\FlashBagInterface;
+
+/**
+ * MemcachedSessionStorage.
+ *
+ * @author Drak
+ */
+class MemcachedSessionStorage extends AbstractSessionStorage implements SessionSaveHandlerInterface
+{
+ /**
+ * Memcached driver.
+ *
+ * @var Memcached
+ */
+ private $memcached;
+
+ /**
+ * Configuration options.
+ *
+ * @var array
+ */
+ private $memcachedOptions;
+
+ /**
+ * Constructor.
+ *
+ * @param \Memcached $memcached A \Memcached instance
+ * @param array $memcachedOptions An associative array of Memcached options
+ * @param array $options Session configuration options.
+ * @param AttributeBagInterface $attributes An AttributeBagInterface instance, (defaults null for default AttributeBag)
+ * @param FlashBagInterface $flashes A FlashBagInterface instance (defaults null for default FlashBag)
+ *
+ * @see AbstractSessionStorage::__construct()
+ */
+ public function __construct(\Memcached $memcache, array $memcachedOptions = array(), array $options = array(), AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null)
+ {
+ $this->memcached = $memcached;
+
+ // defaults
+ if (!isset($memcachedOptions['serverpool'])) {
+ $memcachedOptions['serverpool'] = array('host' => '127.0.0.1', 'port' => 11211, 'timeout' => 1, 'persistent' => false, 'weight' => 1);
+ }
+ $memcachedOptions['expiretime'] = isset($memcachedOptions['expiretime']) ? (int)$memcachedOptions['expiretime'] : 86400;
+
+ $this->memcached->setOption(\Memcached::OPT_PREFIX_KEY, isset($memcachedOptions['prefix']) ? $memcachedOption['prefix'] : 'sf2s');
+
+ $this->memcacheOptions = $memcachedOptions;
+
+ parent::__construct($attributes, $flashes, $options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sessionOpen($savePath, $sessionName)
+ {
+ foreach ($this->memcachedOptions['serverpool'] as $server) {
+ $this->addServer($server);
+ }
+
+ return true;
+ }
+
+ /**
+ * Close session.
+ *
+ * @return boolean
+ */
+ public function sessionClose()
+ {
+ return $this->memcached->close();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sessionRead($sessionId)
+ {
+ $result = $this->memcached->get($this->prefix.$sessionId);
+
+ return $result ? $result : '';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sessionWrite($sessionId, $data)
+ {
+ return $this->memcached->set($this->prefix.$sessionId, $data, false, $this->memcachedOptions['expiretime']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sessionDestroy($sessionId)
+ {
+ return $this->memcached->delete($this->prefix.$sessionId);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sessionGc($lifetime)
+ {
+ // not required here because memcached will auto expire the records anyhow.
+ return true;
+ }
+
+ /**
+ * Adds a server to the memcached handler.
+ *
+ * @param array $server
+ */
+ protected function addServer(array $server)
+ {
+ if (array_key_exists('host', $server)) {
+ throw new \InvalidArgumentException('host key must be set');
+ }
+ $server['port'] = isset($server['port']) ? (int)$server['port'] : 11211;
+ $server['timeout'] = isset($server['timeout']) ? (int)$server['timeout'] : 1;
+ $server['presistent'] = isset($server['presistent']) ? (bool)$server['presistent'] : false;
+ $server['weight'] = isset($server['weight']) ? (bool)$server['weight'] : 1;
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/SessionStorage/NativeFileSessionStorage.php b/src/Symfony/Component/HttpFoundation/SessionStorage/NativeFileSessionStorage.php
new file mode 100644
index 0000000000000..9297e4b7182b2
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/SessionStorage/NativeFileSessionStorage.php
@@ -0,0 +1,64 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\SessionStorage;
+
+use Symfony\Component\HttpFoundation\AttributeBagInterface;
+use Symfony\Component\HttpFoundation\FlashBagInterface;
+
+/**
+ * NativeFileSessionStorage.
+ *
+ * Native session handler using PHP's built in file storage.
+ *
+ * @author Drak
+ */
+class NativeFileSessionStorage extends AbstractSessionStorage
+{
+ /**
+ * @var string
+ */
+ private $savePath;
+
+ /**
+ * Constructor.
+ *
+ * @param string $savePath Path of directory to save session files.
+ * @param array $options Session configuration options.
+ * @param AttributeBagInterface $attributes An AttributeBagInterface instance, (defaults null for default AttributeBag)
+ * @param FlashBagInterface $flashes A FlashBagInterface instance (defaults null for default FlashBag)
+ *
+ * @see AbstractSessionStorage::__construct()
+ */
+ public function __construct($savePath = null, array $options = array(), AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null)
+ {
+ if (is_null($savePath)) {
+ $savePath = sys_get_temp_dir();
+ }
+
+ if (!is_dir($savePath)) {
+ mkdir($savePath, 0777, true);
+ }
+
+ $this->savePath = $savePath;
+
+ parent::__construct($attributes, $flashes, $options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function registerSaveHandlers()
+ {
+ ini_set('session.save_handlers', 'files');
+ ini_set('session.save_path', $this->savePath);
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/SessionStorage/NativeMemcacheSessionStorage.php b/src/Symfony/Component/HttpFoundation/SessionStorage/NativeMemcacheSessionStorage.php
new file mode 100644
index 0000000000000..42e10a9128c01
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/SessionStorage/NativeMemcacheSessionStorage.php
@@ -0,0 +1,59 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\SessionStorage;
+
+use Symfony\Component\HttpFoundation\AttributeBagInterface;
+use Symfony\Component\HttpFoundation\FlashBagInterface;
+
+/**
+ * NativeMemcacheSessionStorage.
+ *
+ * Session based on native PHP memcache database handler.
+ *
+ * @author Drak
+ */
+class NativeMemcacheSessionStorage extends AbstractSessionStorage
+{
+ /**
+ * @var string
+ */
+ private $savePath;
+
+ /**
+ * Constructor.
+ *
+ * @param string $savePath Path of memcache server.
+ * @param array $options Session configuration options.
+ * @param AttributeBagInterface $attributes An AttributeBagInterface instance, (defaults null for default AttributeBag)
+ * @param FlashBagInterface $flashes A FlashBagInterface instance (defaults null for default FlashBag)
+ *
+ * @see AbstractSessionStorage::__construct()
+ */
+ public function __construct($savePath = 'tcp://127.0.0.1:11211?persistent=0', array $options = array(), AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null)
+ {
+ if (!session_module_name('memcache')) {
+ throw new \RuntimeException('PHP does not have "memcache" session module registered');
+ }
+
+ $this->savePath = $savePath;
+ parent::__construct($attributes, $flashes, $options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function registerSaveHandlers()
+ {
+ ini_set('session.save_handlers', 'memcache');
+ ini_set('session.save_path', $this->savePath);
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/SessionStorage/NativeMemcachedSessionStorage.php b/src/Symfony/Component/HttpFoundation/SessionStorage/NativeMemcachedSessionStorage.php
new file mode 100644
index 0000000000000..7b8f86529631b
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/SessionStorage/NativeMemcachedSessionStorage.php
@@ -0,0 +1,59 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\SessionStorage;
+
+use Symfony\Component\HttpFoundation\AttributeBagInterface;
+use Symfony\Component\HttpFoundation\FlashBagInterface;
+
+/**
+ * NativeMemcachedSessionStorage.
+ *
+ * Session based on native PHP memcached database handler.
+ *
+ * @author Drak
+ */
+class NativeMemcachedSessionStorage extends AbstractSessionStorage
+{
+ /**
+ * @var string
+ */
+ private $savePath;
+
+ /**
+ * Constructor.
+ *
+ * @param string $savePath Comma separated list of servers: e.g. memcache1.example.com:11211,memcache2.example.com:11211
+ * @param array $options Session configuration options.
+ * @param AttributeBagInterface $attributes An AttributeBagInterface instance, (defaults null for default AttributeBag)
+ * @param FlashBagInterface $flashes A FlashBagInterface instance (defaults null for defaul FlashBag)
+ *
+ * @see AbstractSessionStorage::__construct()
+ */
+ public function __construct($savePath = '127.0.0.1:11211', array $options = array(), AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null)
+ {
+ if (!session_module_name('memcached')) {
+ throw new \RuntimeException('PHP does not have "memcached" session module registered');
+ }
+
+ $this->savePath = $savePath;
+ parent::__construct($attributes, $flashes, $options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function registerSaveHandlers()
+ {
+ ini_set('session.save_handlers', 'memcached');
+ ini_set('session.save_path', $this->savePath);
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/SessionStorage/NativeSessionStorage.php b/src/Symfony/Component/HttpFoundation/SessionStorage/NativeSessionStorage.php
deleted file mode 100644
index b759f7411a0a4..0000000000000
--- a/src/Symfony/Component/HttpFoundation/SessionStorage/NativeSessionStorage.php
+++ /dev/null
@@ -1,180 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\HttpFoundation\SessionStorage;
-
-/**
- * NativeSessionStorage.
- *
- * @author Fabien Potencier
- *
- * @api
- */
-class NativeSessionStorage implements SessionStorageInterface
-{
- static protected $sessionIdRegenerated = false;
- static protected $sessionStarted = false;
-
- protected $options;
-
- /**
- * Available options:
- *
- * * name: The cookie name (null [omitted] by default)
- * * id: The session id (null [omitted] by default)
- * * lifetime: Cookie lifetime
- * * path: Cookie path
- * * domain: Cookie domain
- * * secure: Cookie secure
- * * httponly: Cookie http only
- *
- * The default values for most options are those returned by the session_get_cookie_params() function
- *
- * @param array $options An associative array of session options
- */
- public function __construct(array $options = array())
- {
- $cookieDefaults = session_get_cookie_params();
-
- $this->options = array_merge(array(
- 'lifetime' => $cookieDefaults['lifetime'],
- 'path' => $cookieDefaults['path'],
- 'domain' => $cookieDefaults['domain'],
- 'secure' => $cookieDefaults['secure'],
- 'httponly' => isset($cookieDefaults['httponly']) ? $cookieDefaults['httponly'] : false,
- ), $options);
-
- // Skip setting new session name if user don't want it
- if (isset($this->options['name'])) {
- session_name($this->options['name']);
- }
- }
-
- /**
- * Starts the session.
- *
- * @api
- */
- public function start()
- {
- if (self::$sessionStarted) {
- return;
- }
-
- session_set_cookie_params(
- $this->options['lifetime'],
- $this->options['path'],
- $this->options['domain'],
- $this->options['secure'],
- $this->options['httponly']
- );
-
- // disable native cache limiter as this is managed by HeaderBag directly
- session_cache_limiter(false);
-
- if (!ini_get('session.use_cookies') && isset($this->options['id']) && $this->options['id'] && $this->options['id'] != session_id()) {
- session_id($this->options['id']);
- }
-
- session_start();
-
- self::$sessionStarted = true;
- }
-
- /**
- * {@inheritDoc}
- *
- * @api
- */
- public function getId()
- {
- if (!self::$sessionStarted) {
- throw new \RuntimeException('The session must be started before reading its ID');
- }
-
- return session_id();
- }
-
- /**
- * Reads data from this storage.
- *
- * The preferred format for a key is directory style so naming conflicts can be avoided.
- *
- * @param string $key A unique key identifying your data
- * @param string $default Default value
- *
- * @return mixed Data associated with the key
- *
- * @api
- */
- public function read($key, $default = null)
- {
- return array_key_exists($key, $_SESSION) ? $_SESSION[$key] : $default;
- }
-
- /**
- * Removes data from this storage.
- *
- * The preferred format for a key is directory style so naming conflicts can be avoided.
- *
- * @param string $key A unique key identifying your data
- *
- * @return mixed Data associated with the key
- *
- * @api
- */
- public function remove($key)
- {
- $retval = null;
-
- if (isset($_SESSION[$key])) {
- $retval = $_SESSION[$key];
- unset($_SESSION[$key]);
- }
-
- return $retval;
- }
-
- /**
- * Writes data to this storage.
- *
- * The preferred format for a key is directory style so naming conflicts can be avoided.
- *
- * @param string $key A unique key identifying your data
- * @param mixed $data Data associated with your key
- *
- * @api
- */
- public function write($key, $data)
- {
- $_SESSION[$key] = $data;
- }
-
- /**
- * Regenerates id that represents this storage.
- *
- * @param Boolean $destroy Destroy session when regenerating?
- *
- * @return Boolean True if session regenerated, false if error
- *
- * @api
- */
- public function regenerate($destroy = false)
- {
- if (self::$sessionIdRegenerated) {
- return;
- }
-
- session_regenerate_id($destroy);
-
- self::$sessionIdRegenerated = true;
- }
-}
diff --git a/src/Symfony/Component/HttpFoundation/SessionStorage/NativeSqliteSessionStorage.php b/src/Symfony/Component/HttpFoundation/SessionStorage/NativeSqliteSessionStorage.php
new file mode 100644
index 0000000000000..724f5f1b4d2e5
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/SessionStorage/NativeSqliteSessionStorage.php
@@ -0,0 +1,59 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\SessionStorage;
+
+use Symfony\Component\HttpFoundation\AttributeBagInterface;
+use Symfony\Component\HttpFoundation\FlashBagInterface;
+
+/**
+ * NativeSqliteSessionStorage.
+ *
+ * Session based on native PHP sqlite database handler.
+ *
+ * @author Drak
+ */
+class NativeSqliteSessionStorage extends AbstractSessionStorage
+{
+ /**
+ * @var string
+ */
+ private $dbPath;
+
+ /**
+ * Constructor.
+ *
+ * @param string $dbPath Path to SQLite database file.
+ * @param array $options Session configuration options.
+ * @param AttributeBagInterface $attributes An AttributeBagInterface instance, (defaults null for default AttributeBag)
+ * @param FlashBagInterface $flashes A FlashBagInterface instance (defaults null for defaul FlashBag)
+ *
+ * @see AbstractSessionStorage::__construct()
+ */
+ public function __construct($dbPath, array $options = array(), AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null)
+ {
+ if (!session_module_name('sqlite')) {
+ throw new \RuntimeException('PHP does not have "sqlite" session module registered');
+ }
+
+ $this->dbPath = $dbPath;
+ parent::__construct($attribubtes, $flashes, $options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function registerSaveHandlers()
+ {
+ ini_set('session.save_handlers', 'sqlite');
+ ini_set('session.save_path', $this->dbPath);
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/SessionStorage/NullSessionStorage.php b/src/Symfony/Component/HttpFoundation/SessionStorage/NullSessionStorage.php
new file mode 100644
index 0000000000000..1fa9a34750011
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/SessionStorage/NullSessionStorage.php
@@ -0,0 +1,74 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\SessionStorage;
+
+/**
+ * NullSessionStorage.
+ *
+ * Can be used in unit testing or in a sitation where persisted sessions are not desired.
+ *
+ * @author Drak
+ *
+ * @api
+ */
+class NullSessionStorage extends AbstractSessionStorage implements SessionSaveHandlerInterface
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function sessionOpen($savePath, $sessionName)
+ {
+ return true;
+ }
+
+ /**
+ * Close session.
+ *
+ * @return boolean
+ */
+ public function sessionClose()
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sessionRead($sessionId)
+ {
+ return '';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sessionWrite($sessionId, $data)
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sessionDestroy($sessionId)
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sessionGc($lifetime)
+ {
+ return true;
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/SessionStorage/PdoSessionStorage.php b/src/Symfony/Component/HttpFoundation/SessionStorage/PdoSessionStorage.php
index ee2641a98afd5..7bbf5699d92bc 100644
--- a/src/Symfony/Component/HttpFoundation/SessionStorage/PdoSessionStorage.php
+++ b/src/Symfony/Component/HttpFoundation/SessionStorage/PdoSessionStorage.php
@@ -11,24 +11,27 @@
namespace Symfony\Component\HttpFoundation\SessionStorage;
+use Symfony\Component\HttpFoundation\AttributeBagInterface;
+use Symfony\Component\HttpFoundation\FlashBagInterface;
+
/**
* PdoSessionStorage.
*
* @author Fabien Potencier
* @author Michael Williams
*/
-class PdoSessionStorage extends NativeSessionStorage
+class PdoSessionStorage extends AbstractSessionStorage implements SessionSaveHandlerInterface
{
/**
* PDO instance.
- *
+ *
* @var \PDO
*/
- private $db;
-
+ private $pdo;
+
/**
* Database options.
- *
+ *
* @var array
*/
private $dbOptions;
@@ -36,59 +39,35 @@ class PdoSessionStorage extends NativeSessionStorage
/**
* Constructor.
*
- * @param \PDO $db A PDO instance
- * @param array $options An associative array of session options
- * @param array $dbOptions An associative array of DB options
+ *
+ * @param \PDO $pdo A \PDO instance
+ * @param array $dbOptions An associative array of DB options
+ * @param array $options Session configuration options
+ * @param AttributeBagInterface $attributes An AttributeBagInterface instance, (defaults null for default AttributeBag)
+ * @param FlashBagInterface $flashes A FlashBagInterface instance (defaults null for defaul FlashBag)
*
* @throws \InvalidArgumentException When "db_table" option is not provided
*
- * @see NativeSessionStorage::__construct()
+ * @see AbstractSessionStorage::__construct()
*/
- public function __construct(\PDO $db, array $options = array(), array $dbOptions = array())
+ public function __construct(\PDO $pdo, array $dbOptions = array(), array $options = array(), AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null)
{
if (!array_key_exists('db_table', $dbOptions)) {
throw new \InvalidArgumentException('You must provide the "db_table" option for a PdoSessionStorage.');
}
- $this->db = $db;
+ $this->pdo = $pdo;
$this->dbOptions = array_merge(array(
'db_id_col' => 'sess_id',
'db_data_col' => 'sess_data',
'db_time_col' => 'sess_time',
), $dbOptions);
- parent::__construct($options);
- }
-
- /**
- * 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($attributes, $flashes, $options);
}
/**
- * Opens a session.
- *
- * @param string $path (ignored)
- * @param string $name (ignored)
- *
- * @return Boolean true, if the session was opened, otherwise an exception is thrown
+ * {@inheritdoc}
*/
public function sessionOpen($path = null, $name = null)
{
@@ -96,22 +75,15 @@ public function sessionOpen($path = null, $name = null)
}
/**
- * Closes a session.
- *
- * @return Boolean true, if the session was closed, otherwise false
+ * {@inheritdoc}
*/
public function sessionClose()
{
- // do nothing
return true;
}
/**
- * Destroys a session.
- *
- * @param string $id A session ID
- *
- * @return Boolean true, if the session was destroyed, otherwise an exception is thrown
+ * {@inheritdoc}
*
* @throws \RuntimeException If the session cannot be destroyed
*/
@@ -125,7 +97,7 @@ public function sessionDestroy($id)
$sql = "DELETE FROM $dbTable WHERE $dbIdCol = :id";
try {
- $stmt = $this->db->prepare($sql);
+ $stmt = $this->pdo->prepare($sql);
$stmt->bindParam(':id', $id, \PDO::PARAM_STR);
$stmt->execute();
} catch (\PDOException $e) {
@@ -136,15 +108,11 @@ public function sessionDestroy($id)
}
/**
- * Cleans up old sessions.
- *
- * @param int $lifetime The lifetime of a session
- *
- * @return Boolean true, if old sessions have been cleaned, otherwise an exception is thrown
+ * {@inheritdoc}
*
* @throws \RuntimeException If any old sessions cannot be cleaned
*/
- public function sessionGC($lifetime)
+ public function sessionGc($lifetime)
{
// get table/column
$dbTable = $this->dbOptions['db_table'];
@@ -154,7 +122,7 @@ public function sessionGC($lifetime)
$sql = "DELETE FROM $dbTable WHERE $dbTimeCol < (:time - $lifetime)";
try {
- $stmt = $this->db->prepare($sql);
+ $stmt = $this->pdo->prepare($sql);
$stmt->bindValue(':time', time(), \PDO::PARAM_INT);
$stmt->execute();
} catch (\PDOException $e) {
@@ -165,11 +133,7 @@ public function sessionGC($lifetime)
}
/**
- * Reads a session.
- *
- * @param string $id A session ID
- *
- * @return string The session data if the session was read or created, otherwise an exception is thrown
+ * {@inheritdoc}
*
* @throws \RuntimeException If the session cannot be read
*/
@@ -183,7 +147,7 @@ public function sessionRead($id)
try {
$sql = "SELECT $dbDataCol FROM $dbTable WHERE $dbIdCol = :id";
- $stmt = $this->db->prepare($sql);
+ $stmt = $this->pdo->prepare($sql);
$stmt->bindParam(':id', $id, \PDO::PARAM_STR, 255);
$stmt->execute();
@@ -205,12 +169,7 @@ public function sessionRead($id)
}
/**
- * Writes session data.
- *
- * @param string $id A session ID
- * @param string $data A serialized chunk of session data
- *
- * @return Boolean true, if the session was written, otherwise an exception is thrown
+ * {@inheritdoc}
*
* @throws \RuntimeException If the session data cannot be written
*/
@@ -222,7 +181,7 @@ public function sessionWrite($id, $data)
$dbIdCol = $this->dbOptions['db_id_col'];
$dbTimeCol = $this->dbOptions['db_time_col'];
- $sql = ('mysql' === $this->db->getAttribute(\PDO::ATTR_DRIVER_NAME))
+ $sql = ('mysql' === $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME))
? "INSERT INTO $dbTable ($dbIdCol, $dbDataCol, $dbTimeCol) VALUES (:id, :data, :time) "
."ON DUPLICATE KEY UPDATE $dbDataCol = VALUES($dbDataCol), $dbTimeCol = CASE WHEN $dbTimeCol = :time THEN (VALUES($dbTimeCol) + 1) ELSE VALUES($dbTimeCol) END"
: "UPDATE $dbTable SET $dbDataCol = :data, $dbTimeCol = :time WHERE $dbIdCol = :id";
@@ -230,7 +189,7 @@ public function sessionWrite($id, $data)
try {
//session data can contain non binary safe characters so we need to encode it
$encoded = base64_encode($data);
- $stmt = $this->db->prepare($sql);
+ $stmt = $this->pdo->prepare($sql);
$stmt->bindParam(':id', $id, \PDO::PARAM_STR);
$stmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
$stmt->bindValue(':time', time(), \PDO::PARAM_INT);
@@ -253,7 +212,7 @@ public function sessionWrite($id, $data)
*
* @param string $id
* @param string $data
- *
+ *
* @return boolean True.
*/
private function createNewSession($id, $data = '')
@@ -268,7 +227,7 @@ private function createNewSession($id, $data = '')
//session data can contain non binary safe characters so we need to encode it
$encoded = base64_encode($data);
- $stmt = $this->db->prepare($sql);
+ $stmt = $this->pdo->prepare($sql);
$stmt->bindParam(':id', $id, \PDO::PARAM_STR);
$stmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
$stmt->bindValue(':time', time(), \PDO::PARAM_INT);
diff --git a/src/Symfony/Component/HttpFoundation/SessionStorage/SessionSaveHandlerInterface.php b/src/Symfony/Component/HttpFoundation/SessionStorage/SessionSaveHandlerInterface.php
new file mode 100644
index 0000000000000..95ed8e5055dba
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/SessionStorage/SessionSaveHandlerInterface.php
@@ -0,0 +1,152 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\SessionStorage;
+
+/**
+ * Session Savehandler Interface.
+ *
+ * This interface is for implementing methods required for the
+ * session_set_save_handler() function.
+ *
+ * @see http://php.net/session_set_save_handler
+ *
+ * These are methods called by PHP when the session is started
+ * and closed and for various house-keeping tasks required
+ * by session management.
+ *
+ * PHP requires session save handlers. There are some defaults set automatically
+ * when PHP starts, but these can be overriden using this command if you need anything
+ * other than PHP's default handling.
+ *
+ * When the session starts, PHP will call the sessionRead() handler which should return a string
+ * extactly as stored (which will have been encoded by PHP using a special session serializer
+ * session_decode() which is different to the serialize() function. PHP will then populate these
+ * into $_SESSION.
+ *
+ * When PHP shuts down, the sessionWrite() handler is called and will pass the $_SESSION contents
+ * to be stored. Again PHP will automatically serialize these itself using session_encode()
+ *
+ * When a session is specifically destroyed, PHP will call the sessionDestroy() handler with the
+ * session ID. This happens when the session is regenerated for example and th handler
+ * MUST delete the session by ID from the persistent storage immediately.
+ *
+ * PHP will call sessionGc() from time to time to expire any session records according to the
+ * set max lifetime of a session. This routine should delete all records from persistent
+ * storage which were last accessed longer than the $lifetime.
+ *
+ * PHP sessionOpen() and sessionClose() are pretty much redundant and can return true.
+ *
+ * @author Drak
+ */
+interface SessionSaveHandlerInterface
+{
+ /**
+ * Open session.
+ *
+ * This method is for internal use by PHP and must not be called manually.
+ *
+ * @param string $savePath Save path.
+ * @param string $sessionName Session Name.
+ *
+ * @throws \RuntimeException If something goes wrong starting the session.
+ *
+ * @return boolean
+ */
+ function sessionOpen($savePath, $sessionName);
+
+ /**
+ * Close session.
+ *
+ * This method is for internal use by PHP and must not be called manually.
+ *
+ * @return boolean
+ */
+ function sessionClose();
+
+ /**
+ * Read session.
+ *
+ * This method is for internal use by PHP and must not be called manually.
+ *
+ * This method is called by PHP itself when the session is started.
+ * This method should retrieve the session data from storage by the
+ * ID provided by PHP. Return the string directly as is from storage.
+ * If the record was not found you must return an empty string.
+ *
+ * The returned data will be automatically unserialized by PHP using a
+ * special unserializer method session_decode() and the result will be used
+ * to populate the $_SESSION superglobal. This is done automatically and
+ * is not configurable.
+ *
+ * @param string $sessionId Session ID.
+ *
+ * @throws \RuntimeException On fatal error but not "record not found".
+ *
+ * @return string String as stored in persistent storage or empty string in all other cases.
+ */
+ function sessionRead($sessionId);
+
+ /**
+ * Commit session to storage.
+ *
+ * This method is for internal use by PHP and must not be called manually.
+ *
+ * PHP will call this method when the session is closed. It sends
+ * the session ID and the contents of $_SESSION to be saved in a lightweight
+ * serialized format (which PHP does automatically using session_encode()
+ * which should be stored exactly as is given in $data.
+ *
+ * Note this method is normally called by PHP after the output buffers
+ * have been closed.
+ *
+ * @param string $sessionId Session ID.
+ * @param string $data Session serialized data to save.
+ *
+ * @throws \RuntimeException On fatal error.
+ *
+ * @return boolean
+ */
+ function sessionWrite($sessionId, $data);
+
+ /**
+ * Destroys this session.
+ *
+ * This method is for internal use by PHP and must not be called manually.
+ *
+ * PHP will call this method when the session data associated
+ * with the session ID provided needs to be immediately
+ * deleted from the permanent storage.
+ *
+ * @param string $sessionId Session ID.
+ *
+ * @throws \RuntimeException On fatal error.
+ *
+ * @return boolean
+ */
+ function sessionDestroy($sessionId);
+
+ /**
+ * Garbage collection for storage.
+ *
+ * This method is for internal use by PHP and must not be called manually.
+ *
+ * This method is called by PHP periodically and passes the maximum
+ * time a session can exist for before being deleted from permanent storage.
+ *
+ * @param integer $lifetime Max lifetime in seconds to keep sessions stored.
+ *
+ * @throws \RuntimeException On fatal error.
+ *
+ * @return boolean
+ */
+ function sessionGc($lifetime);
+}
diff --git a/src/Symfony/Component/HttpFoundation/SessionStorage/SessionStorageInterface.php b/src/Symfony/Component/HttpFoundation/SessionStorage/SessionStorageInterface.php
index b61a2557b27c7..afde7702112a0 100644
--- a/src/Symfony/Component/HttpFoundation/SessionStorage/SessionStorageInterface.php
+++ b/src/Symfony/Component/HttpFoundation/SessionStorage/SessionStorageInterface.php
@@ -11,10 +11,14 @@
namespace Symfony\Component\HttpFoundation\SessionStorage;
+use Symfony\Component\HttpFoundation\FlashBagInterface;
+use Symfony\Component\HttpFoundation\AttributeBagInterface;
+
/**
* SessionStorageInterface.
*
* @author Fabien Potencier
+ * @author Drak
*
* @api
*/
@@ -23,6 +27,8 @@ interface SessionStorageInterface
/**
* Starts the session.
*
+ * @throws \RuntimeException If something goes wrong starting the session.
+ *
* @api
*/
function start();
@@ -30,68 +36,51 @@ function start();
/**
* Returns the session ID
*
- * @return mixed The session ID
- *
- * @throws \RuntimeException If the session was not started yet
+ * @return mixed The session ID or false if the session has not started.
*
* @api
*/
function getId();
/**
- * Reads data from this storage.
+ * Regenerates id that represents this storage.
*
- * The preferred format for a key is directory style so naming conflicts can be avoided.
+ * This method must invoke session_regenerate_id($destroy) unless
+ * this interface is used for a storage object designed for unit
+ * or functional testing where a real PHP session would interfere
+ * with testing.
*
- * @param string $key A unique key identifying your data
+ * @param Boolean $destroy Destroy session when regenerating?
*
- * @return mixed Data associated with the key
+ * @return Boolean True if session regenerated, false if error
*
- * @throws \RuntimeException If an error occurs while reading data from this storage
+ * @throws \RuntimeException If an error occurs while regenerating this storage
*
* @api
*/
- function read($key);
+ function regenerate($destroy = false);
/**
- * Removes data from this storage.
- *
- * The preferred format for a key is directory style so naming conflicts can be avoided.
- *
- * @param string $key A unique key identifying your data
+ * Force the session to be saved.
*
- * @return mixed Data associated with the key
- *
- * @throws \RuntimeException If an error occurs while removing data from this storage
- *
- * @api
+ * This method must invoke session_write_close() unless this interface is
+ * used for a storage object design for unit or functional testing where
+ * a real PHP session would interfere with testing, in which case it
+ * it should actually persist the session data if required.
*/
- function remove($key);
+ function save();
/**
- * Writes data to this storage.
+ * Gets the FlashBagInterface driver.
*
- * The preferred format for a key is directory style so naming conflicts can be avoided.
- *
- * @param string $key A unique key identifying your data
- * @param mixed $data Data associated with your key
- *
- * @throws \RuntimeException If an error occurs while writing to this storage
- *
- * @api
+ * @return FlashBagInterface
*/
- function write($key, $data);
+ function getFlashes();
/**
- * Regenerates id that represents this storage.
- *
- * @param Boolean $destroy Destroy session when regenerating?
+ * Gets the AttributeBagInterface driver.
*
- * @return Boolean True if session regenerated, false if error
- *
- * @throws \RuntimeException If an error occurs while regenerating this storage
- *
- * @api
+ * @return AttributeBagInterface
*/
- function regenerate($destroy = false);
+ function getAttributes();
}
diff --git a/tests/Symfony/Tests/Component/HttpFoundation/AttributeBagTest.php b/tests/Symfony/Tests/Component/HttpFoundation/AttributeBagTest.php
new file mode 100644
index 0000000000000..7bda14a25146f
--- /dev/null
+++ b/tests/Symfony/Tests/Component/HttpFoundation/AttributeBagTest.php
@@ -0,0 +1,157 @@
+
+ */
+class AttributeBagTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @var array
+ */
+ private $array;
+
+ /**
+ * @var AttributeBag
+ */
+ private $bag;
+
+ protected function setUp()
+ {
+ $this->array = array(
+ 'hello' => 'world',
+ 'always' => 'be happy',
+ 'user.login' => 'drak',
+ 'csrf.token' => array(
+ 'a' => '1234',
+ 'b' => '4321',
+ ),
+ 'category' => array(
+ 'fishing' => array(
+ 'first' => 'cod',
+ 'second' => 'sole')
+ ),
+ );
+ $this->bag = new AttributeBag('_sf2');
+ $this->bag->initialize($this->array);
+ }
+
+ protected function tearDown()
+ {
+ $this->bag = null;
+ $this->array = array();
+ }
+
+ public function testInitialize()
+ {
+ $bag = new AttributeBag();
+ $bag->initialize($this->array);
+ $this->assertEquals($this->array, $this->bag->all());
+ $array = array('should' => 'not stick');
+ $bag->initialize($array);
+
+ // should have remained the same
+ $this->assertEquals($this->array, $this->bag->all());
+ }
+
+ public function testGetStorageKey()
+ {
+ $this->assertEquals('_sf2', $this->bag->getStorageKey());
+ $attributeBag = new AttributeBag('test');
+ $this->assertEquals('test', $attributeBag->getStorageKey());
+ }
+
+ /**
+ * @dataProvider attributesProvider
+ */
+ public function testHas($key, $value, $exists)
+ {
+ $this->assertEquals($exists, $this->bag->has($key));
+ }
+
+ /**
+ * @dataProvider attributesProvider
+ */
+ public function testGet($key, $value, $expected)
+ {
+ $this->assertEquals($value, $this->bag->get($key));
+ }
+
+ public function testGetDefaults()
+ {
+ $this->assertNull($this->bag->get('user2.login'));
+ $this->assertEquals('default', $this->bag->get('user2.login', 'default'));
+ }
+
+ /**
+ * @dataProvider attributesProvider
+ */
+ public function testSet($key, $value, $expected)
+ {
+ $this->bag->set($key, $value);
+ $this->assertEquals($value, $this->bag->get($key));
+ }
+
+ public function testAll()
+ {
+ $this->assertEquals($this->array, $this->bag->all());
+
+ $this->bag->set('hello', 'fabien');
+ $array = $this->array;
+ $array['hello'] = 'fabien';
+ $this->assertEquals($array, $this->bag->all());
+ }
+
+ public function testReplace()
+ {
+ $array = array();
+ $array['name'] = 'jack';
+ $array['foo.bar'] = 'beep';
+ $this->bag->replace($array);
+ $this->assertEquals($array, $this->bag->all());
+ $this->assertNull($this->bag->get('hello'));
+ $this->assertNull($this->bag->get('always'));
+ $this->assertNull($this->bag->get('user.login'));
+ }
+
+ public function testRemove()
+ {
+ $this->assertEquals('world', $this->bag->get('hello'));
+ $this->bag->remove('hello');
+ $this->assertNull($this->bag->get('hello'));
+
+ $this->assertEquals('be happy', $this->bag->get('always'));
+ $this->bag->remove('always');
+ $this->assertNull($this->bag->get('always'));
+
+ $this->assertEquals('drak', $this->bag->get('user.login'));
+ $this->bag->remove('user.login');
+ $this->assertNull($this->bag->get('user.login'));
+ }
+
+ public function testClear()
+ {
+ $this->bag->clear();
+ $this->assertEquals(array(), $this->bag->all());
+ }
+
+ public function attributesProvider()
+ {
+ return array(
+ array('hello', 'world', true),
+ array('always', 'be happy', true),
+ array('user.login', 'drak', true),
+ array('csrf.token', array('a' => '1234', 'b' => '4321'), true),
+ array('category', array('fishing' => array('first' => 'cod', 'second' => 'sole')), true),
+ array('user2.login', null, false),
+ array('never', null, false),
+ array('bye', null, false),
+ array('bye/for/now', null, false),
+ );
+ }
+}
diff --git a/tests/Symfony/Tests/Component/HttpFoundation/FlashBagTest.php b/tests/Symfony/Tests/Component/HttpFoundation/FlashBagTest.php
new file mode 100644
index 0000000000000..ebfb6f19bc2c6
--- /dev/null
+++ b/tests/Symfony/Tests/Component/HttpFoundation/FlashBagTest.php
@@ -0,0 +1,130 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Tests\Component\HttpFoundation;
+
+use Symfony\Component\HttpFoundation\FlashBag;
+use Symfony\Component\HttpFoundation\FlashBagInterface;
+
+/**
+ * FlashBagTest
+ *
+ * @author Drak
+ */
+class FlashBagTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @var \Symfony\Component\HttpFoundation\FlashBagInterface
+ */
+ private $bag;
+
+ /**
+ * @var array
+ */
+ protected $array = array();
+
+ public function setUp()
+ {
+ parent::setUp();
+ $this->bag = new FlashBag();
+ $this->array = array(FlashBag::NOTICE => array('A previous flash message'));
+ $this->bag->initialize($this->array);
+ }
+
+ public function tearDown()
+ {
+ $this->bag = null;
+ parent::tearDown();
+ }
+
+ public function testInitialize()
+ {
+ $bag = new FlashBag();
+ $bag->initialize($this->array);
+ $this->assertEquals($this->array, $this->bag->all());
+ $array = array('should' => 'not stick');
+ $bag->initialize($array);
+
+ // should have remained the same
+ $this->assertEquals($this->array, $this->bag->all());
+ }
+
+ public function testAdd()
+ {
+ $this->bag->add('Something new', FlashBag::NOTICE);
+ $this->bag->add('Smile, it might work next time', FlashBag::ERROR);
+ $this->assertEquals(array('A previous flash message', 'Something new'), $this->bag->get(FlashBag::NOTICE));
+ $this->assertEquals(array('Smile, it might work next time'), $this->bag->get(FlashBag::ERROR));
+ }
+
+ public function testGet()
+ {
+ $this->assertEquals(array('A previous flash message'), $this->bag->get(FlashBag::NOTICE));
+ $this->assertEquals(array('A previous flash message'), $this->bag->get(FlashBag::NOTICE, true));
+ $this->assertFalse($this->bag->has(FlashBag::NOTICE));
+ $this->assertEquals(array(), $this->bag->get('non_existing_type'));
+ }
+
+ public function testSet()
+ {
+ $this->bag->set(FlashBag::NOTICE, array('Foo', 'Bar'));
+ $this->assertEquals(array('Foo', 'Bar'), $this->bag->get(FlashBag::NOTICE));
+ }
+
+ public function testHas()
+ {
+ $this->assertFalse($this->bag->has('nothing'));
+ $this->assertTrue($this->bag->has(FlashBag::NOTICE));
+ }
+
+ public function testGetTypes()
+ {
+ $this->assertEquals(array(FlashBag::NOTICE), $this->bag->getTypes());
+ }
+
+ public function testAll()
+ {
+ $this->bag->set(FlashBag::NOTICE, array('Foo'));
+ $this->bag->set(FlashBag::ERROR, array('Bar'));
+ $this->assertEquals(array(
+ FlashBag::NOTICE => array('Foo'),
+ FlashBag::ERROR => array('Bar')),
+ $this->bag->all()
+ );
+ $this->assertTrue($this->bag->has(FlashBag::NOTICE));
+ $this->assertTrue($this->bag->has(FlashBag::ERROR));
+ $this->assertEquals(array(
+ FlashBag::NOTICE => array('Foo'),
+ FlashBag::ERROR => array('Bar')),
+ $this->bag->all(true)
+ );
+ $this->assertFalse($this->bag->has(FlashBag::NOTICE));
+ $this->assertFalse($this->bag->has(FlashBag::ERROR));
+ $this->assertEquals(array(), $this->bag->all());
+ }
+
+ public function testClear()
+ {
+ $this->assertTrue($this->bag->has(FlashBag::NOTICE));
+ $this->bag->clear(FlashBag::NOTICE);
+ $this->assertFalse($this->bag->has(FlashBag::NOTICE));
+ }
+
+ public function testClearAll()
+ {
+ $this->assertTrue($this->bag->has(FlashBag::NOTICE));
+ $this->bag->add('Smile, it might work next time', FlashBag::ERROR);
+ $this->assertTrue($this->bag->has(FlashBag::ERROR));
+ $this->bag->clearAll();
+ $this->assertFalse($this->bag->has(FlashBag::NOTICE));
+ $this->assertFalse($this->bag->has(FlashBag::ERROR));
+ }
+}
\ No newline at end of file
diff --git a/tests/Symfony/Tests/Component/HttpFoundation/NamespacedAttributeBagTest.php b/tests/Symfony/Tests/Component/HttpFoundation/NamespacedAttributeBagTest.php
new file mode 100644
index 0000000000000..48623a6b22e12
--- /dev/null
+++ b/tests/Symfony/Tests/Component/HttpFoundation/NamespacedAttributeBagTest.php
@@ -0,0 +1,162 @@
+
+ */
+class NamespacedAttributeBagTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @var array
+ */
+ private $array;
+
+ /**
+ * @var NamespacedAttributeBag
+ */
+ private $bag;
+
+ protected function setUp()
+ {
+ $this->array = array(
+ 'hello' => 'world',
+ 'always' => 'be happy',
+ 'user.login' => 'drak',
+ 'csrf.token' => array(
+ 'a' => '1234',
+ 'b' => '4321',
+ ),
+ 'category' => array(
+ 'fishing' => array(
+ 'first' => 'cod',
+ 'second' => 'sole')
+ ),
+ );
+ $this->bag = new NamespacedAttributeBag('_sf2', '/');
+ $this->bag->initialize($this->array);
+ }
+
+ protected function tearDown()
+ {
+ $this->bag = null;
+ $this->array = array();
+ }
+
+ public function testInitialize()
+ {
+ $bag = new NamespacedAttributeBag();
+ $bag->initialize($this->array);
+ $this->assertEquals($this->array, $this->bag->all());
+ $array = array('should' => 'not stick');
+ $bag->initialize($array);
+
+ // should have remained the same
+ $this->assertEquals($this->array, $this->bag->all());
+ }
+
+ public function testGetStorageKey()
+ {
+ $this->assertEquals('_sf2', $this->bag->getStorageKey());
+ $attributeBag = new NamespacedAttributeBag('test');
+ $this->assertEquals('test', $attributeBag->getStorageKey());
+ }
+
+ /**
+ * @dataProvider attributesProvider
+ */
+ public function testHas($key, $value, $exists)
+ {
+ $this->assertEquals($exists, $this->bag->has($key));
+ }
+
+ /**
+ * @dataProvider attributesProvider
+ */
+ public function testGet($key, $value, $expected)
+ {
+ $this->assertEquals($value, $this->bag->get($key));
+ }
+
+ public function testGetDefaults()
+ {
+ $this->assertNull($this->bag->get('user2.login'));
+ $this->assertEquals('default', $this->bag->get('user2.login', 'default'));
+ }
+
+ /**
+ * @dataProvider attributesProvider
+ */
+ public function testSet($key, $value, $expected)
+ {
+ $this->bag->set($key, $value);
+ $this->assertEquals($value, $this->bag->get($key));
+ }
+
+ public function testAll()
+ {
+ $this->assertEquals($this->array, $this->bag->all());
+
+ $this->bag->set('hello', 'fabien');
+ $array = $this->array;
+ $array['hello'] = 'fabien';
+ $this->assertEquals($array, $this->bag->all());
+ }
+
+ public function testReplace()
+ {
+ $array = array();
+ $array['name'] = 'jack';
+ $array['foo.bar'] = 'beep';
+ $this->bag->replace($array);
+ $this->assertEquals($array, $this->bag->all());
+ $this->assertNull($this->bag->get('hello'));
+ $this->assertNull($this->bag->get('always'));
+ $this->assertNull($this->bag->get('user.login'));
+ }
+
+ public function testRemove()
+ {
+ $this->assertEquals('world', $this->bag->get('hello'));
+ $this->bag->remove('hello');
+ $this->assertNull($this->bag->get('hello'));
+
+ $this->assertEquals('be happy', $this->bag->get('always'));
+ $this->bag->remove('always');
+ $this->assertNull($this->bag->get('always'));
+
+ $this->assertEquals('drak', $this->bag->get('user.login'));
+ $this->bag->remove('user.login');
+ $this->assertNull($this->bag->get('user.login'));
+ }
+
+ public function testClear()
+ {
+ $this->bag->clear();
+ $this->assertEquals(array(), $this->bag->all());
+ }
+
+ public function attributesProvider()
+ {
+ return array(
+ array('hello', 'world', true),
+ array('always', 'be happy', true),
+ array('user.login', 'drak', true),
+ array('csrf.token', array('a' => '1234', 'b' => '4321'), true),
+ array('csrf.token/a', '1234', true),
+ array('csrf.token/b', '4321', true),
+ array('category', array('fishing' => array('first' => 'cod', 'second' => 'sole')), true),
+ array('category/fishing', array('first' => 'cod', 'second' => 'sole'), true),
+ array('category/fishing/first', 'cod', true),
+ array('category/fishing/second', 'sole', true),
+ array('user2.login', null, false),
+ array('never', null, false),
+ array('bye', null, false),
+ array('bye/for/now', null, false),
+ );
+ }
+}
diff --git a/tests/Symfony/Tests/Component/HttpFoundation/RequestTest.php b/tests/Symfony/Tests/Component/HttpFoundation/RequestTest.php
index cde875a4c24a3..e1f610e0db7b3 100644
--- a/tests/Symfony/Tests/Component/HttpFoundation/RequestTest.php
+++ b/tests/Symfony/Tests/Component/HttpFoundation/RequestTest.php
@@ -13,10 +13,10 @@
use Symfony\Component\HttpFoundation\SessionStorage\ArraySessionStorage;
-
use Symfony\Component\HttpFoundation\Session;
-
use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\FlashBag;
+use Symfony\Component\HttpFoundation\AttributeBag;
class RequestTest extends \PHPUnit_Framework_TestCase
{
@@ -835,7 +835,7 @@ public function testHasSession()
$request = new Request;
$this->assertFalse($request->hasSession());
- $request->setSession(new Session(new ArraySessionStorage()));
+ $request->setSession(new Session(new ArraySessionStorage(new AttributeBag(), new FlashBag())));
$this->assertTrue($request->hasSession());
}
@@ -846,7 +846,7 @@ public function testHasPreviousSession()
$this->assertFalse($request->hasPreviousSession());
$request->cookies->set(session_name(), 'foo');
$this->assertFalse($request->hasPreviousSession());
- $request->setSession(new Session(new ArraySessionStorage()));
+ $request->setSession(new Session(new ArraySessionStorage(new AttributeBag(), new FlashBag())));
$this->assertTrue($request->hasPreviousSession());
}
diff --git a/tests/Symfony/Tests/Component/HttpFoundation/SessionStorage/ArraySessionStorageTest.php b/tests/Symfony/Tests/Component/HttpFoundation/SessionStorage/ArraySessionStorageTest.php
new file mode 100644
index 0000000000000..6e7b921ea76bb
--- /dev/null
+++ b/tests/Symfony/Tests/Component/HttpFoundation/SessionStorage/ArraySessionStorageTest.php
@@ -0,0 +1,90 @@
+
+ */
+class ArraySessionStorageTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @var ArraySessionStorage
+ */
+ private $storage;
+
+ /**
+ * @var FlashBag
+ */
+ private $flashBag;
+
+ /**
+ * @var AttributeBag
+ */
+ private $attributeBag;
+
+ private $attributes;
+ private $flashes;
+
+ protected function setUp()
+ {
+ $this->attributes = array('foo' => 'bar');
+ $this->flashes = array('notice' => 'hello');
+ $this->flashBag = new FlashBag();
+ $this->flashBag->initialize($this->flashes);
+ $this->attributeBag = new AttributeBag();
+ $this->attributeBag->initialize($this->attributes);
+ $this->storage = new ArraySessionStorage($this->attributeBag, $this->flashBag);
+ }
+
+ protected function tearDown()
+ {
+ $this->flashBag = null;
+ $this->attributesBag = null;
+ $this->storage = null;
+ }
+
+ public function testStart()
+ {
+ $this->assertEquals('', $this->storage->getId());
+ $this->storage->start();
+ $id = $this->storage->getId();
+ $this->assertNotEquals('', $id);
+ $this->storage->start();
+ $this->assertEquals($id, $this->storage->getId());
+ }
+
+ public function testRegenerateDestroy()
+ {
+ $this->storage->start();
+ $id = $this->storage->getId();
+ $this->storage->regenerate(true);
+ $this->assertNotEquals($id, $this->storage->getId());
+ $this->assertEquals(array(), $this->storage->getAttributes()->all());
+ $this->assertEquals(array(), $this->storage->getFlashes()->all());
+ }
+
+ public function testRegenerate()
+ {
+ $this->storage->start();
+ $id = $this->storage->getId();
+ $this->storage->regenerate();
+ $this->assertNotEquals($id, $this->storage->getId());
+
+ $this->assertEquals($this->attributes, $this->storage->getAttributes()->all());
+ $this->assertEquals($this->flashes, $this->storage->getFlashes()->all());
+ }
+
+ public function testGetId()
+ {
+ $this->assertEquals('', $this->storage->getId());
+ $this->storage->start();
+ $this->assertNotEquals('', $this->storage->getId());
+ }
+}
diff --git a/tests/Symfony/Tests/Component/HttpFoundation/SessionStorage/FilesystemSessionStorageTest.php b/tests/Symfony/Tests/Component/HttpFoundation/SessionStorage/FilesystemSessionStorageTest.php
deleted file mode 100644
index 060cb0e913e6e..0000000000000
--- a/tests/Symfony/Tests/Component/HttpFoundation/SessionStorage/FilesystemSessionStorageTest.php
+++ /dev/null
@@ -1,104 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Tests\Component\HttpFoundation\SessionStorage;
-
-use Symfony\Component\HttpFoundation\SessionStorage\FilesystemSessionStorage;
-
-class FilesystemSessionStorageTest extends \PHPUnit_Framework_TestCase
-{
- private $path;
-
- protected function setUp()
- {
- $this->path = sys_get_temp_dir().'/sf2/session_test';
- if (!file_exists($this->path)) {
- mkdir($this->path, 0777, true);
- }
- }
-
- protected function tearDown()
- {
- array_map('unlink', glob($this->path.'/*.session'));
- rmdir($this->path);
-
- $this->path = null;
- }
-
- public function testMultipleInstances()
- {
- $storage1 = new FilesystemSessionStorage($this->path);
- $storage1->start();
- $storage1->write('foo', 'bar');
-
- $storage2 = new FilesystemSessionStorage($this->path);
- $storage2->start();
- $this->assertEquals('bar', $storage2->read('foo'), 'values persist between instances');
- }
-
- public function testGetIdThrowsErrorBeforeStart()
- {
- $this->setExpectedException('RuntimeException');
-
- $storage = new FilesystemSessionStorage($this->path);
- $storage->getId();
- }
-
- public function testGetIdWorksAfterStart()
- {
- $storage = new FilesystemSessionStorage($this->path);
- $storage->start();
- $storage->getId();
- }
-
- public function testGetIdSetByOptions()
- {
- $previous = ini_get('session.use_cookies');
-
- ini_set('session.use_cookies', false);
-
- $storage = new FilesystemSessionStorage($this->path, array('id' => 'symfony2-sessionId'));
- $storage->start();
-
- $this->assertEquals('symfony2-sessionId', $storage->getId());
-
- ini_set('session.use_cookies', $previous);
- }
-
- public function testRemoveVariable()
- {
- $storage = new FilesystemSessionStorage($this->path);
- $storage->start();
-
- $storage->write('foo', 'bar');
-
- $this->assertEquals('bar', $storage->read('foo'));
-
- $storage->remove('foo', 'bar');
-
- $this->assertNull($storage->read('foo'));
- }
-
- public function testRegenerate()
- {
- $storage = new FilesystemSessionStorage($this->path);
- $storage->start();
- $storage->write('foo', 'bar');
-
- $storage->regenerate();
-
- $this->assertEquals('bar', $storage->read('foo'));
-
- $storage->regenerate(true);
-
- $this->assertNull($storage->read('foo'));
- }
-}
diff --git a/tests/Symfony/Tests/Component/HttpFoundation/SessionTest.php b/tests/Symfony/Tests/Component/HttpFoundation/SessionTest.php
index 8318101e665d1..179c6fa553e94 100644
--- a/tests/Symfony/Tests/Component/HttpFoundation/SessionTest.php
+++ b/tests/Symfony/Tests/Component/HttpFoundation/SessionTest.php
@@ -12,6 +12,10 @@
namespace Symfony\Tests\Component\HttpFoundation;
use Symfony\Component\HttpFoundation\Session;
+use Symfony\Component\HttpFoundation\FlashBag;
+use Symfony\Component\HttpFoundation\FlashBagInterface;
+use Symfony\Component\HttpFoundation\AttributeBag;
+use Symfony\Component\HttpFoundation\AttributeBagInterface;
use Symfony\Component\HttpFoundation\SessionStorage\ArraySessionStorage;
/**
@@ -19,117 +23,150 @@
*
* @author Fabien Potencier
* @author Robert Schönthal
+ * @author Drak
*/
class SessionTest extends \PHPUnit_Framework_TestCase
{
+ /**
+ * @var \Symfony\Component\HttpFoundation\SessionStorage\SessionStorageInterface
+ */
protected $storage;
+
+ /**
+ * @var \Symfony\Component\HttpFoundation\SessionInterface
+ */
protected $session;
+ /**
+ * @var \Symfony\Component\HttpFoundation\FlashBagInterface
+ */
+ protected $flashBag;
+
+ /**
+ * @var \Symfony\Component\HttpFoundation\AttributeBagInterface
+ */
+ protected $attributeBag;
+
public function setUp()
{
- $this->storage = new ArraySessionStorage();
- $this->session = $this->getSession();
+ $this->flashBag = new FlashBag();
+ $this->attributesBag = new AttributeBag();
+ $this->storage = new ArraySessionStorage($this->attributesBag, $this->flashBag);
+ $this->session = new Session($this->storage);
}
protected function tearDown()
{
$this->storage = null;
+ $this->flashBag = null;
+ $this->attributesBag = null;
$this->session = null;
}
- public function testFlash()
+ public function test__Constructor()
{
- $this->session->clearFlashes();
-
- $this->assertSame(array(), $this->session->getFlashes());
-
- $this->assertFalse($this->session->hasFlash('foo'));
-
- $this->session->setFlash('foo', 'bar');
-
- $this->assertTrue($this->session->hasFlash('foo'));
- $this->assertSame('bar', $this->session->getFlash('foo'));
-
- $this->session->removeFlash('foo');
-
- $this->assertFalse($this->session->hasFlash('foo'));
-
- $flashes = array('foo' => 'bar', 'bar' => 'foo');
-
- $this->session->setFlashes($flashes);
-
- $this->assertSame($flashes, $this->session->getFlashes());
+ // This tests the defaults on the Session object constructor
+ $storage = new ArraySessionStorage($this->attributesBag, $this->flashBag);
+ $session = new Session($storage);
+ $this->assertSame($this->flashBag, $storage->getFlashes());
}
- public function testFlashesAreFlushedWhenNeeded()
+ public function testStart()
{
- $this->session->setFlash('foo', 'bar');
- $this->session->save();
-
- $this->session = $this->getSession();
- $this->assertTrue($this->session->hasFlash('foo'));
- $this->session->save();
+ $this->assertEquals('', $this->storage->getId());
+ $this->session->start();
+ $this->assertNotEquals('', $this->storage->getId());
+ }
- $this->session = $this->getSession();
- $this->assertFalse($this->session->hasFlash('foo'));
+ public function testGetFlashes()
+ {
+ $this->assertTrue($this->session->getFlashes() instanceof FlashBagInterface);
}
- public function testAll()
+ public function testGet()
{
- $this->assertFalse($this->session->has('foo'));
+ // tests defaults
$this->assertNull($this->session->get('foo'));
+ $this->assertEquals(1, $this->session->get('foo', 1));
+ }
- $this->session->set('foo', 'bar');
-
- $this->assertTrue($this->session->has('foo'));
- $this->assertSame('bar', $this->session->get('foo'));
-
- $this->session = $this->getSession();
-
- $this->session->remove('foo');
- $this->session->set('foo', 'bar');
-
- $this->session->remove('foo');
-
- $this->assertFalse($this->session->has('foo'));
-
- $attrs = array('foo' => 'bar', 'bar' => 'foo');
-
- $this->session = $this->getSession();
+ /**
+ * @dataProvider setProvider
+ */
+ public function testSet($key, $value)
+ {
+ $this->session->set($key, $value);
+ $this->assertEquals($value, $this->session->get($key));
+ }
- $this->session->replace($attrs);
+ public function testReplace()
+ {
+ $this->session->replace(array('happiness' => 'be good', 'symfony' => 'awesome'));
+ $this->assertEquals(array('happiness' => 'be good', 'symfony' => 'awesome'), $this->session->all());
+ $this->session->replace(array());
+ $this->assertEquals(array(), $this->session->all());
+ }
- $this->assertSame($attrs, $this->session->all());
+ /**
+ * @dataProvider setProvider
+ */
+ public function testAll($key, $value, $result)
+ {
+ $this->session->set($key, $value);
+ $this->assertEquals($result, $this->session->all());
+ }
+ /**
+ * @dataProvider setProvider
+ */
+ public function testClear($key, $value)
+ {
+ $this->session->set('hi', 'fabien');
+ $this->session->set($key, $value);
$this->session->clear();
-
- $this->assertSame(array(), $this->session->all());
+ $this->assertEquals(array(), $this->session->all());
}
- public function testMigrateAndInvalidate()
+ public function setProvider()
{
- $this->session->set('foo', 'bar');
- $this->session->setFlash('foo', 'bar');
-
- $this->assertSame('bar', $this->session->get('foo'));
- $this->assertSame('bar', $this->session->getFlash('foo'));
-
- $this->session->migrate();
+ return array(
+ array('foo', 'bar', array('foo' => 'bar')),
+ array('foo.bar', 'too much beer', array('foo.bar' => 'too much beer')),
+ array('great', 'symfony2 is great', array('great' => 'symfony2 is great')),
+ );
+ }
- $this->assertSame('bar', $this->session->get('foo'));
- $this->assertSame('bar', $this->session->getFlash('foo'));
+ /**
+ * @dataProvider setProvider
+ */
+ public function testRemove($key, $value)
+ {
+ $this->session->set('hi.world', 'have a nice day');
+ $this->session->set($key, $value);
+ $this->session->remove($key);
+ $this->assertEquals(array('hi.world' => 'have a nice day'), $this->session->all());
+ }
- $this->session = $this->getSession();
+ public function testInvalidate()
+ {
+ $this->session->set('invalidate', 123);
+ $this->session->getFlashes()->add('OK');
$this->session->invalidate();
+ $this->assertEquals(array(), $this->session->all());
+ $this->assertEquals(array(), $this->session->getFlashes()->all());
+ }
- $this->assertSame(array(), $this->session->all());
- $this->assertSame(array(), $this->session->getFlashes());
+ public function testMigrate()
+ {
+ $this->session->set('migrate', 321);
+ $this->session->getFlashes()->add('OK');
+ $this->session->migrate();
+ $this->assertEquals(321, $this->session->get('migrate'));
+ $this->assertEquals(array('OK'), $this->session->getFlashes()->get(FlashBag::NOTICE));
}
public function testSerialize()
{
- $this->session = new Session($this->storage);
-
$compare = serialize($this->storage);
$this->assertSame($compare, $this->session->serialize());
@@ -142,91 +179,36 @@ public function testSerialize()
$this->assertEquals($_storage->getValue($this->session), $this->storage, 'storage match');
}
- public function testSave()
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testUnserializeException()
{
- $this->storage = new ArraySessionStorage();
- $this->session = new Session($this->storage);
- $this->session->set('foo', 'bar');
-
- $this->session->save();
- $compare = array('_symfony2' => array('attributes' => array('foo' => 'bar'), 'flashes' => array()));
-
- $r = new \ReflectionObject($this->storage);
- $p = $r->getProperty('data');
- $p->setAccessible(true);
-
- $this->assertSame($p->getValue($this->storage), $compare);
+ $serialized = serialize(new \ArrayObject());
+ $this->session->unserialize($serialized);
}
- public function testGetId()
- {
- $this->assertNull($this->session->getId());
- }
- public function testStart()
+ public function testGetId()
{
+ $this->assertEquals('', $this->session->getId());
$this->session->start();
-
- $this->assertSame(array(), $this->session->getFlashes());
- $this->assertSame(array(), $this->session->all());
+ $this->assertNotEquals('', $this->session->getId());
}
- public function testSavedOnDestruct()
+ public function flashAdd()
{
- $this->session->set('foo', 'bar');
-
- $this->session->__destruct();
-
- $expected = array(
- 'attributes'=>array('foo'=>'bar'),
- 'flashes'=>array(),
- );
- $saved = $this->storage->read('_symfony2');
- $this->assertSame($expected, $saved);
- }
-
- public function testSavedOnDestructAfterManualSave()
- {
- $this->session->set('foo', 'nothing');
- $this->session->save();
- $this->session->set('foo', 'bar');
-
- $this->session->__destruct();
-
- $expected = array(
- 'attributes'=>array('foo'=>'bar'),
- 'flashes'=>array(),
- );
- $saved = $this->storage->read('_symfony2');
- $this->assertSame($expected, $saved);
- }
-
- public function testStorageRegenerate()
- {
- $this->storage->write('foo', 'bar');
-
- $this->assertTrue($this->storage->regenerate());
-
- $this->assertEquals('bar', $this->storage->read('foo'));
-
- $this->assertTrue($this->storage->regenerate(true));
-
- $this->assertNull($this->storage->read('foo'));
- }
-
- public function testStorageRemove()
- {
- $this->storage->write('foo', 'bar');
-
- $this->assertEquals('bar', $this->storage->read('foo'));
-
- $this->storage->remove('foo');
-
- $this->assertNull($this->storage->read('foo'));
+ $this->session->addFlash('Hello world', FlashBag::NOTICE);
+ $this->session->addFlash('Bye bye cruel world', FlashBag::NOTICE);
+ $this->assertEquals(array('Hello world', 'Bye by cruel world'), $this->session->getFlash(FlashBag::NOTICE));
}
- protected function getSession()
+ public function flashGet()
{
- return new Session($this->storage);
+ $this->session->addFlash('Hello world', FlashBag::NOTICE);
+ $this->session->addFlash('Bye bye cruel world', FlashBag::NOTICE);
+ $this->assertEquals(array('Hello world', 'Bye by cruel world'), $this->session->getFlash(FlashBag::NOTICE), true);
+ $this->assertEquals(array('Hello world', 'Bye by cruel world'), $this->session->getFlash(FlashBag::NOTICE));
+ $this->assertEquals(array(), $this->session->getFlash(FlashBag::NOTICE));
}
}
diff --git a/tests/Symfony/Tests/Component/Security/Http/Firewall/ContextListenerTest.php b/tests/Symfony/Tests/Component/Security/Http/Firewall/ContextListenerTest.php
index dfbcd79e7faf9..60c6fe8b8c9cb 100644
--- a/tests/Symfony/Tests/Component/Security/Http/Firewall/ContextListenerTest.php
+++ b/tests/Symfony/Tests/Component/Security/Http/Firewall/ContextListenerTest.php
@@ -2,6 +2,8 @@
namespace Symfony\Test\Component\Security\Http\Firewall;
+use Symfony\Component\HttpFoundation\FlashBag;
+use Symfony\Component\HttpFoundation\AttributeBag;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session;
@@ -63,7 +65,7 @@ public function testOnKernelResponseWillRemoveSession()
protected function runSessionOnKernelResponse($newToken, $original = null)
{
- $session = new Session(new ArraySessionStorage());
+ $session = new Session(new ArraySessionStorage(new AttributeBag(), new FlashBag()));
if ($original !== null) {
$session->set('_security_session', $original);