diff --git a/CHANGELOG-2.1.md b/CHANGELOG-2.1.md
index 35cbb4f96e..02ff0a5301 100644
--- a/CHANGELOG-2.1.md
+++ b/CHANGELOG-2.1.md
@@ -224,6 +224,34 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c
* removed the ContentTypeMimeTypeGuesser class as it is deprecated and never used on PHP 5.3
* added ResponseHeaderBag::makeDisposition() (implements RFC 6266)
* made mimetype to extension conversion configurable
+ * [BC BREAK] Moved all session related classes and interfaces into own namespace, as
+ `Symfony\Component\HttpFoudation\Session` and renamed classes accordingly.
+ * Added `FlashBag`. Flashes expire when retrieved by `get()` or `all()`.
+ This makes the implementation ESI compatible.
+ * Added `AutoExpireFlashBag` (default) to replicate Symfony 2.0.x auto expire behaviour of messages auto expiring
+ after one page page load. Messages must be retrived by `get()` or `all()`.
+ * [BC BREAK] Removed the `close()` method from the Session class
+ * Deprecated the following methods from the Session class: `setFlash()`, `setFlashes()`
+ `getFlash()`, `hasFlash()`, and `removeFlash()`. Use `getFlashBag() instead which returns a `FlashBagInterface`.
+ * `Session->clear()` now only clears session attributes as before it cleared flash messages and
+ attributes. `Session->getFlashBag()->all()` clears flashes now.
+ * Added `Symfony\Component\HttpFoundation\Session\Storage\AbstractSessionStorage` base class for
+ session storage drivers.
+ * Added `Symfony\Component\HttpFoundation\Session\Storage\SessionSaveHandlerInterface` interface
+ which storage drivers should implement after inheriting from
+ `Symfony\Component\HttpFoundation\Session\Storage\AbstractSessionStorage` when writing custom session save handlers.
+ * [BC BREAK] `SessionStorageInterface` methods removed: `write()`, `read()` and `remove()`. Added
+ `getBag()`, `registerBag()`.
+ * Moved attribute storage to `Symfony\Component\HttpFoundation\Attribute\AttributeBagInterface`.
+ * Added `Symfony\Component\HttpFoundation\Attribute\AttributeBag` to replicate attributes storage
+ behaviour from 2.0.x (default).
+ * Added `Symfony\Component\HttpFoundation\Attribute\NamespacedAttributeBag` for namespace session attributes.
+ * Session now implements `Symfony\Component\HttpFoundation\Session\SessionInterface` making
+ implementation customizable and portable.
+ * [BC BREAK] Removed `NativeSessionStorage` and replaced with `NativeFileSessionStorage`.
+ * Added session storage drivers for PHP native Memcache, Memcached and SQLite session save handlers.
+ * Added session storage drivers for custom Memcache, Memcached and Null session save handlers.
+ * Removed `FilesystemSessionStorage`, use `MockFileSessionStorage` for functional testing instead.
### HttpKernel
diff --git a/UPGRADE-2.1.md b/UPGRADE-2.1.md
index b94b511d79..862806138b 100644
--- a/UPGRADE-2.1.md
+++ b/UPGRADE-2.1.md
@@ -1,6 +1,8 @@
UPGRADE FROM 2.0 to 2.1
=======================
+### General
+
* assets_base_urls and base_urls merging strategy has changed
Unlike most configuration blocks, successive values for
@@ -11,6 +13,8 @@ UPGRADE FROM 2.0 to 2.1
and/or share a common base configuration (i.e. ``config.yml``), merging
could yield a set of base URL's for multiple environments.
+### [HttpFoundation]
+
* moved management of the locale from the Session class to the Request class
Configuring the default locale:
@@ -28,17 +32,20 @@ UPGRADE FROM 2.0 to 2.1
Retrieving the locale from a Twig template:
- Before: `{{ app.request.session.locale }}` or `{{ app.session.locale }}`
+ Before: `{{ app.request.session.locale }}` or `{{ app.session.locale }}`
+
After: `{{ app.request.locale }}`
Retrieving the locale from a PHP template:
- Before: `$view['session']->getLocale()`
+ Before: `$view['session']->getLocale()`
+
After: `$view['request']->getLocale()`
Retrieving the locale from PHP code:
- Before: `$session->getLocale()`
+ Before: `$session->getLocale()`
+
After: `$request->getLocale()`
* Method `equals` of `Symfony\Component\Security\Core\User\UserInterface` has
@@ -134,7 +141,7 @@ UPGRADE FROM 2.0 to 2.1
* The strategy for generating the HTML attributes "id" and "name"
of choices in a choice field has changed
-
+
Instead of appending the choice value, a generated integer is now appended
by default. Take care if your Javascript relies on that. If you can
guarantee that your choice values only contain ASCII letters, digits,
@@ -144,7 +151,7 @@ UPGRADE FROM 2.0 to 2.1
* The strategy for generating the HTML attributes "value" of choices in a
choice field has changed
-
+
Instead of using the choice value, a generated integer is now stored.
Again, take care if your Javascript reads this value. If your choice field
is a non-expanded single-choice field, or if the choices are guaranteed not
@@ -248,3 +255,63 @@ UPGRADE FROM 2.0 to 2.1
{
return isset($options['widget']) && 'single_text' === $options['widget'] ? 'text' : 'choice';
}
+
+* Flash Messages now returns and array based on type (the old method are still available but deprecated)
+
+ Before (PHP):
+
+ hasFlash('notice')): ?>
+
+ getFlash('notice') ?>
+
+
+
+ After (PHP):
+
+ getFlashBag()->has('notice')): ?>
+
+ getFlashBag()->get('notice') ?>
+
+
+
+ If you wanted to process all flash types you could also make use of the `getFlashBag()->all()` API:
+
+ getFlashBag()->all() as $type => $flash): ?>
+
+
+
+
+
+ Before (Twig):
+
+ {% if app.session.hasFlash('notice') %}
+
+ {{ app.session.flash('notice') }}
+
+ {% endif %}
+
+ After (Twig):
+
+ {% if app.session.flashes.has('notice') %}
+
+ {{ app.session.flashes.get('notice') }}
+
+ {% endif %}
+
+ Again you can process all flash messages in one go with
+
+ {% for type, flashMessage in app.session.flashes.all() %}
+
+ {{ flashMessage }}
+
+ {% endforeach %}
+
+* Session storage drivers
+
+ Session storage drivers should inherit from
+ `Symfony\Component\HttpFoundation\Session\Storage\AbstractSessionStorage`
+ and no longer should implement `read()`, `write()`, `remove()` which were removed from the
+ `SessionStorageInterface`.
+
+ Any session storage driver that wants to use custom save handlers should
+ implement `Symfony\Component\HttpFoundation\Session\Storage\SaveHandlerInterface`
diff --git a/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionStorage.php b/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionStorage.php
index 6fa324f85e..cda9dd2a0a 100644
--- a/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionStorage.php
+++ b/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionStorage.php
@@ -3,7 +3,8 @@
namespace Symfony\Bridge\Doctrine\HttpFoundation;
use Doctrine\DBAL\Platforms\MySqlPlatform;
-use Symfony\Component\HttpFoundation\SessionStorage\NativeSessionStorage;
+use Symfony\Component\HttpFoundation\Session\Storage\AbstractSessionStorage;
+use Symfony\Component\HttpFoundation\Session\Storage\SessionSaveHandlerInterface;
use Doctrine\DBAL\Driver\Connection;
/**
@@ -12,39 +13,30 @@ use Doctrine\DBAL\Driver\Connection;
* @author Fabien Potencier
* @author Johannes M. Schmitt
*/
-class DbalSessionStorage extends NativeSessionStorage
+class DbalSessionStorage extends AbstractSessionStorage implements SessionSaveHandlerInterface
{
+ /**
+ * @var Connection
+ */
private $con;
- private $tableName;
-
- public function __construct(Connection $con, $tableName = 'sessions', array $options = array())
- {
- parent::__construct($options);
-
- $this->con = $con;
- $this->tableName = $tableName;
- }
/**
- * Starts the session.
- */
- public function start()
+ * @var string
+ */
+ private $tableName;
+
+ /**
+ *
+ * @param Connection $con An instance of Connection.
+ * @param string $tableName Table name.
+ * @param array $options Session configuration options
+ */
+ public function __construct(Connection $con, $tableName = 'sessions', array $options = array())
{
- if (self::$sessionStarted) {
- return;
- }
+ $this->con = $con;
+ $this->tableName = $tableName;
- // use this object as the session handler
- session_set_save_handler(
- array($this, 'sessionOpen'),
- array($this, 'sessionClose'),
- array($this, 'sessionRead'),
- array($this, 'sessionWrite'),
- array($this, 'sessionDestroy'),
- array($this, 'sessionGC')
- );
-
- parent::start();
+ parent::__construct($options);
}
/**
@@ -55,7 +47,7 @@ class DbalSessionStorage extends NativeSessionStorage
*
* @return Boolean true, if the session was opened, otherwise an exception is thrown
*/
- public function sessionOpen($path = null, $name = null)
+ public function openSession($path = null, $name = null)
{
return true;
}
@@ -65,7 +57,7 @@ class DbalSessionStorage extends NativeSessionStorage
*
* @return Boolean true, if the session was closed, otherwise false
*/
- public function sessionClose()
+ public function closeSession()
{
// do nothing
return true;
@@ -80,7 +72,7 @@ class DbalSessionStorage extends NativeSessionStorage
*
* @throws \RuntimeException If the session cannot be destroyed
*/
- public function sessionDestroy($id)
+ public function destroySession($id)
{
try {
$this->con->executeQuery("DELETE FROM {$this->tableName} WHERE sess_id = :id", array(
@@ -102,7 +94,7 @@ class DbalSessionStorage extends NativeSessionStorage
*
* @throws \RuntimeException If any old sessions cannot be cleaned
*/
- public function sessionGC($lifetime)
+ public function gcSession($lifetime)
{
try {
$this->con->executeQuery("DELETE FROM {$this->tableName} WHERE sess_time < :time", array(
@@ -124,7 +116,7 @@ class DbalSessionStorage extends NativeSessionStorage
*
* @throws \RuntimeException If the session cannot be read
*/
- public function sessionRead($id)
+ public function readSession($id)
{
try {
$data = $this->con->executeQuery("SELECT sess_data FROM {$this->tableName} WHERE sess_id = :id", array(
@@ -140,7 +132,7 @@ class DbalSessionStorage extends NativeSessionStorage
return '';
} catch (\PDOException $e) {
- throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e);
+ throw new \RuntimeException(sprintf('PDOException was thrown when trying to read the session data: %s', $e->getMessage()), 0, $e);
}
}
@@ -154,7 +146,7 @@ class DbalSessionStorage extends NativeSessionStorage
*
* @throws \RuntimeException If the session data cannot be written
*/
- public function sessionWrite($id, $data)
+ public function writeSession($id, $data)
{
$platform = $this->con->getDatabasePlatform();
@@ -181,7 +173,7 @@ class DbalSessionStorage extends NativeSessionStorage
$this->createNewSession($id, $data);
}
} catch (\PDOException $e) {
- throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e);
+ throw new \RuntimeException(sprintf('PDOException was thrown when trying to write the session data: %s', $e->getMessage()), 0, $e);
}
return true;
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
index 5195ff1c2f..6738eb3e4a 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
@@ -167,7 +167,7 @@ class Configuration implements ConfigurationInterface
->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/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
index 2e80aea918..d635a7f598 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -301,7 +301,7 @@ class FrameworkExtension extends Extension
$this->addClassesToCompile(array(
'Symfony\\Bundle\\FrameworkBundle\\EventListener\\SessionListener',
- 'Symfony\\Component\\HttpFoundation\\SessionStorage\\SessionStorageInterface',
+ 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\SessionStorageInterface',
$container->getDefinition('session')->getClass(),
));
diff --git a/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php b/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php
index 7d6ac49a06..d335758b70 100644
--- a/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php
+++ b/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php
@@ -69,7 +69,6 @@ class TestSessionListener implements EventSubscriberInterface
if ($session = $event->getRequest()->getSession()) {
$session->save();
- $session->close();
$params = session_get_cookie_params();
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml
index c3c2eba0c5..31d374aa6b 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml
@@ -5,22 +5,31 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
- Symfony\Component\HttpFoundation\Session
- Symfony\Component\HttpFoundation\SessionStorage\NativeSessionStorage
- Symfony\Component\HttpFoundation\SessionStorage\FilesystemSessionStorage
+ Symfony\Component\HttpFoundation\Session\Session
+ Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag
+ Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag
+ Symfony\Component\HttpFoundation\Session\Storage\NativeFileSessionStorage
+ Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage
Symfony\Bundle\FrameworkBundle\EventListener\SessionListener
+
+
-
+
+
+
+
+
+ %kernel.cache_dir%/sessions
%session.storage.options%
-
+
%kernel.cache_dir%/sessions
%session.storage.options%
@@ -29,5 +38,9 @@
+
+
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Templating/GlobalVariables.php b/src/Symfony/Bundle/FrameworkBundle/Templating/GlobalVariables.php
index 08d32c86cf..f77ca12436 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Templating/GlobalVariables.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Templating/GlobalVariables.php
@@ -79,7 +79,7 @@ class GlobalVariables
/**
* Returns the current session.
*
- * @return Symfony\Component\HttpFoundation\Session|void The session
+ * @return Symfony\Component\HttpFoundation\Session\Session|void The session
*/
public function getSession()
{
diff --git a/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/SessionHelper.php b/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/SessionHelper.php
index 041d9ddff9..c6628172f2 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/SessionHelper.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/SessionHelper.php
@@ -48,17 +48,17 @@ class SessionHelper extends Helper
public function getFlash($name, $default = null)
{
- return $this->session->getFlash($name, $default);
+ return $this->session->getFlashBag()->get($name);
}
public function getFlashes()
{
- return $this->session->getFlashes();
+ return $this->session->getFlashBag()->all();
}
public function hasFlash($name)
{
- return $this->session->hasFlash($name);
+ return $this->session->getFlashBag()->has($name);
}
/**
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 01361f2280..3ab6488493 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 @@ $container->loadFromExtension('framework', array(
),
'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 2d6a06047e..e46a476a96 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 3c7db0ee49..126baf2d4e 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 3da5483944..6aa952e3a6 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
@@ -78,7 +78,7 @@ abstract class FrameworkExtensionTest extends TestCase
$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 7c656ba7e0..fe551ba360 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/EventListener/TestSessionListenerTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/EventListener/TestSessionListenerTest.php
@@ -94,7 +94,7 @@ class TestSessionListenerTest extends \PHPUnit_Framework_TestCase
private function getSession()
{
- return $this->getMockBuilder('Symfony\Component\HttpFoundation\Session')
+ return $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\Session')
->disableOriginalConstructor()
->getMock();
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SessionController.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SessionController.php
index e77f1f1266..b1c4334cde 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SessionController.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SessionController.php
@@ -51,7 +51,7 @@ class SessionController extends ContainerAware
{
$request = $this->container->get('request');
$session = $request->getSession();
- $session->setFlash('notice', $message);
+ $session->getFlashBag()->set('notice', $message);
return new RedirectResponse($this->container->get('router')->generate('session_showflash'));
}
@@ -61,8 +61,8 @@ class SessionController extends ContainerAware
$request = $this->container->get('request');
$session = $request->getSession();
- if ($session->hasFlash('notice')) {
- $output = $session->getFlash('notice');
+ if ($session->getFlashBag()->has('notice')) {
+ $output = $session->getFlashBag()->get('notice');
} else {
$output = 'No flash was set.';
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/config/framework.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/config/framework.yml
index 18ed6f4d87..0cc6b74d30 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/config/framework.yml
+++ b/src/Symfony/Bundle/FrameworkBundle/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.mock_file
services:
logger: { class: Symfony\Component\HttpKernel\Log\NullLogger }
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/SessionHelperTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/SessionHelperTest.php
index ee400cd7dc..ce8c91b173 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/SessionHelperTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/SessionHelperTest.php
@@ -12,8 +12,8 @@
namespace Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper;
use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\Session;
-use Symfony\Component\HttpFoundation\SessionStorage\ArraySessionStorage;
+use Symfony\Component\HttpFoundation\Session\Session;
+use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
use Symfony\Bundle\FrameworkBundle\Templating\Helper\SessionHelper;
class SessionHelperTest extends \PHPUnit_Framework_TestCase
@@ -24,9 +24,9 @@ class SessionHelperTest extends \PHPUnit_Framework_TestCase
{
$this->request = new Request();
- $session = new Session(new ArraySessionStorage());
+ $session = new Session(new MockArraySessionStorage());
$session->set('foobar', 'bar');
- $session->setFlash('foo', 'bar');
+ $session->getFlashBag()->set('notice', 'bar');
$this->request->setSession($session);
}
@@ -40,14 +40,15 @@ class SessionHelperTest extends \PHPUnit_Framework_TestCase
{
$helper = new SessionHelper($this->request);
- $this->assertTrue($helper->hasFlash('foo'));
+ $this->assertTrue($helper->hasFlash('notice'));
- $this->assertEquals('bar', $helper->getFlash('foo'));
- $this->assertEquals('foo', $helper->getFlash('bar', 'foo'));
+ $this->assertEquals('bar', $helper->getFlash('notice'));
+ }
- $this->assertNull($helper->getFlash('foobar'));
-
- $this->assertEquals(array('foo' => 'bar'), $helper->getFlashes());
+ public function testGetFlashes()
+ {
+ $helper = new SessionHelper($this->request);
+ $this->assertEquals(array('notice' => '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 cefc38b0ef..476b398e61 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/PhpEngineTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/PhpEngineTest.php
@@ -14,8 +14,8 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Templating;
use Symfony\Bundle\FrameworkBundle\Templating\PhpEngine;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\Session;
-use Symfony\Component\HttpFoundation\SessionStorage\ArraySessionStorage;
+use Symfony\Component\HttpFoundation\Session\Session;
+use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
use Symfony\Component\Templating\TemplateNameParser;
use Symfony\Bundle\FrameworkBundle\Templating\GlobalVariables;
use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
@@ -64,7 +64,7 @@ class PhpEngineTest extends TestCase
{
$container = new Container();
$request = new Request();
- $session = new Session(new ArraySessionStorage());
+ $session = new Session(new MockArraySessionStorage());
$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 18ed6f4d87..0cc6b74d30 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.mock_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 77b79dd545..f15ef3f945 100644
--- a/src/Symfony/Bundle/TwigBundle/Tests/TwigEngineTest.php
+++ b/src/Symfony/Bundle/TwigBundle/Tests/TwigEngineTest.php
@@ -14,8 +14,8 @@ namespace Symfony\Bundle\TwigBundle\Tests;
use Symfony\Bundle\TwigBundle\TwigEngine;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\Session;
-use Symfony\Component\HttpFoundation\SessionStorage\ArraySessionStorage;
+use Symfony\Component\HttpFoundation\Session\Session;
+use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
use Symfony\Component\Templating\TemplateNameParser;
use Symfony\Bundle\FrameworkBundle\Templating\GlobalVariables;
@@ -71,7 +71,7 @@ class TwigEngineTest extends TestCase
{
$container = new Container();
$request = new Request();
- $session = new Session(new ArraySessionStorage());
+ $session = new Session(new MockArraySessionStorage());
$request->setSession($session);
$container->set('request', $request);
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php b/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php
index d6235025d0..491ca0b6ea 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php
+++ b/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php
@@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag;
/**
* ProfilerController.
@@ -146,9 +147,9 @@ class ProfilerController extends ContainerAware
{
$request = $this->container->get('request');
- if (null !== $session = $request->getSession()) {
- // keep current flashes for one more request
- $session->setFlashes($session->getFlashes());
+ if (null !== $session = $request->getSession() && $session->getFlashBag() instanceof AutoExpireFlashBag) {
+ // keep current flashes for one more request if using AutoExpireFlashBag
+ $session->getFlashBag()->setAll($session->getFlashBag()->peekAll());
}
if (null === $token) {
diff --git a/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php b/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php
index a1b6d31a7c..b9418e6b2a 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php
+++ b/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php
@@ -15,6 +15,7 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Bundle\TwigBundle\TwigEngine;
+use Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag;
/**
* WebDebugToolbarListener injects the Web Debug Toolbar.
@@ -70,9 +71,10 @@ class WebDebugToolbarListener
}
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());
+ $session = $request->getSession();
+ if ($session->getFlashBag() instanceof AutoExpireFlashBag) {
+ // keep current flashes for one more request if using AutoExpireFlashBag
+ $session->getFlashBag()->setAll($session->getFlashBag()->peekAll());
}
$response->setContent($this->templating->render('WebProfilerBundle:Profiler:toolbar_redirect.html.twig', array('location' => $response->headers->get('Location'))));
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php
index b6986176e8..ce75b593dc 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php
+++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php
@@ -177,7 +177,7 @@ class WebDebugToolbarListenerTest extends \PHPUnit_Framework_TestCase
protected function getRequestMock($isXmlHttpRequest = false, $requestFormat = 'html')
{
- $session = $this->getMock('Symfony\Component\HttpFoundation\Session', array(), array(), '', false);
+ $session = $this->getMock('Symfony\Component\HttpFoundation\Session\Session', array(), array(), '', false);
$request = $this->getMock(
'Symfony\Component\HttpFoundation\Request',
array('getSession', 'isXmlHttpRequest', 'getRequestFormat'),
diff --git a/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/SessionCsrfProvider.php b/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/SessionCsrfProvider.php
index d52fcac4b3..5174412a97 100644
--- a/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/SessionCsrfProvider.php
+++ b/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/SessionCsrfProvider.php
@@ -11,7 +11,7 @@
namespace Symfony\Component\Form\Extension\Csrf\CsrfProvider;
-use Symfony\Component\HttpFoundation\Session;
+use Symfony\Component\HttpFoundation\Session\Session;
/**
* This provider uses a Symfony2 Session object to retrieve the user's
diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php
index fc33610906..beab652a42 100644
--- a/src/Symfony/Component/HttpFoundation/Request.php
+++ b/src/Symfony/Component/HttpFoundation/Request.php
@@ -11,6 +11,8 @@
namespace Symfony\Component\HttpFoundation;
+use Symfony\Component\HttpFoundation\Session\SessionInterface;
+
/**
* Request represents an HTTP request.
*
@@ -122,7 +124,7 @@ class Request
protected $format;
/**
- * @var \Symfony\Component\HttpFoundation\Session
+ * @var \Symfony\Component\HttpFoundation\Session\SessionInterface
*/
protected $session;
@@ -466,7 +468,7 @@ class Request
/**
* Gets the Session.
*
- * @return Session|null The session
+ * @return SessionInterface|null The session
*
* @api
*/
@@ -504,11 +506,11 @@ class Request
/**
* 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;
}
diff --git a/src/Symfony/Component/HttpFoundation/Session.php b/src/Symfony/Component/HttpFoundation/Session.php
deleted file mode 100644
index 721a6c7240..0000000000
--- a/src/Symfony/Component/HttpFoundation/Session.php
+++ /dev/null
@@ -1,358 +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;
-
-use Symfony\Component\HttpFoundation\SessionStorage\SessionStorageInterface;
-
-/**
- * Session.
- *
- * @author Fabien Potencier
- *
- * @api
- */
-class Session implements \Serializable
-{
- protected $storage;
- protected $started;
- protected $attributes;
- protected $flashes;
- protected $oldFlashes;
- protected $closed;
-
- /**
- * Constructor.
- *
- * @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;
- }
-
- /**
- * Starts the session storage.
- *
- * @api
- */
- 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;
- }
-
- /**
- * Checks if an attribute is defined.
- *
- * @param string $name The attribute name
- *
- * @return Boolean true if the attribute is defined, false otherwise
- *
- * @api
- */
- public function has($name)
- {
- return array_key_exists($name, $this->attributes);
- }
-
- /**
- * Returns an attribute.
- *
- * @param string $name The attribute name
- * @param mixed $default The default value
- *
- * @return mixed
- *
- * @api
- */
- public function get($name, $default = null)
- {
- return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
- }
-
- /**
- * Sets an attribute.
- *
- * @param string $name
- * @param mixed $value
- *
- * @api
- */
- public function set($name, $value)
- {
- if (false === $this->started) {
- $this->start();
- }
-
- $this->attributes[$name] = $value;
- }
-
- /**
- * Returns attributes.
- *
- * @return array Attributes
- *
- * @api
- */
- public function all()
- {
- return $this->attributes;
- }
-
- /**
- * Sets attributes.
- *
- * @param array $attributes Attributes
- *
- * @api
- */
- public function replace(array $attributes)
- {
- if (false === $this->started) {
- $this->start();
- }
-
- $this->attributes = $attributes;
- }
-
- /**
- * Removes an attribute.
- *
- * @param string $name
- *
- * @api
- */
- public function remove($name)
- {
- if (false === $this->started) {
- $this->start();
- }
-
- if (array_key_exists($name, $this->attributes)) {
- unset($this->attributes[$name]);
- }
- }
-
- /**
- * Clears all attributes.
- *
- * @api
- */
- public function clear()
- {
- if (false === $this->started) {
- $this->start();
- }
-
- $this->attributes = array();
- $this->flashes = array();
- }
-
- /**
- * Invalidates the current session.
- *
- * @api
- */
- public function invalidate()
- {
- $this->clear();
- $this->storage->regenerate(true);
- }
-
- /**
- * Migrates the current session to a new session id while maintaining all
- * session attributes.
- *
- * @api
- */
- public function migrate()
- {
- $this->storage->regenerate();
- }
-
- /**
- * Returns 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.
- *
- * @return array
- */
- public function getFlashes()
- {
- return $this->flashes;
- }
-
- /**
- * Sets the flash messages.
- *
- * @param array $values
- */
- public function setFlashes($values)
- {
- if (false === $this->started) {
- $this->start();
- }
-
- $this->flashes = $values;
- $this->oldFlashes = array();
- }
-
- /**
- * Gets a flash message.
- *
- * @param string $name
- * @param string|null $default
- *
- * @return string
- */
- public function getFlash($name, $default = null)
- {
- return array_key_exists($name, $this->flashes) ? $this->flashes[$name] : $default;
- }
-
- /**
- * Sets a flash message.
- *
- * @param string $name
- * @param string $value
- */
- public function setFlash($name, $value)
- {
- if (false === $this->started) {
- $this->start();
- }
-
- $this->flashes[$name] = $value;
- unset($this->oldFlashes[$name]);
- }
-
- /**
- * Checks whether a flash message exists.
- *
- * @param string $name
- *
- * @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).
- */
- 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)
- {
- $this->storage = unserialize($serialized);
- $this->attributes = array();
- $this->started = false;
- }
-}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php b/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php
new file mode 100644
index 0000000000..2915b03297
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.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\Session\Attribute;
+
+/**
+ * This class relates to session attribute storage
+ */
+class AttributeBag implements AttributeBagInterface
+{
+ private $name = 'attributes';
+
+ /**
+ * @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 getName()
+ {
+ return $this->name;
+ }
+
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function initialize(array &$attributes)
+ {
+ $this->attributes = &$attributes;
+ }
+
+ /**
+ * {@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()
+ {
+ $return = $this->attributes;
+ $this->attributes = array();
+
+ return $return;
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBagInterface.php b/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBagInterface.php
new file mode 100644
index 0000000000..af16d642ef
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBagInterface.php
@@ -0,0 +1,70 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Attribute;
+
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+
+/**
+ * Attributes store.
+ *
+ * @author Drak
+ */
+interface AttributeBagInterface extends SessionBagInterface
+{
+ /**
+ * 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);
+}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php b/src/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php
new file mode 100644
index 0000000000..7a32405096
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php
@@ -0,0 +1,184 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Attribute;
+
+/**
+ * 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()
+ {
+ $return = $this->attributes;
+ $this->attributes = array();
+
+ return $return;
+ }
+
+ /**
+ * 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/Session/Flash/AutoExpireFlashBag.php b/src/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php
new file mode 100644
index 0000000000..1c7b9e9d07
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php
@@ -0,0 +1,174 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Flash;
+
+/**
+ * AutoExpireFlashBag flash message container.
+ *
+ * @author Drak
+ */
+class AutoExpireFlashBag implements FlashBagInterface
+{
+ private $name = 'flashes';
+
+ /**
+ * Flash messages.
+ *
+ * @var array
+ */
+ private $flashes = array();
+
+ /**
+ * 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;
+ $this->flashes = array('display' => array(), 'new' => array());
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function initialize(array &$flashes)
+ {
+ $this->flashes = &$flashes;
+
+ // The logic: messages from the last request will be stored in new, so we move them to previous
+ // This request we will show what is in 'display'. What is placed into 'new' this time round will
+ // be moved to display next time round.
+ $this->flashes['display'] = array_key_exists('new', $this->flashes) ? $this->flashes['new'] : array();
+ $this->flashes['new'] = array();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function peek($type, $default = null)
+ {
+ return $this->has($type) ? $this->flashes['display'][$type] : $default;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function peekAll()
+ {
+ return array_key_exists('display', $this->flashes) ? (array)$this->flashes['display'] : array();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get($type, $default = null)
+ {
+ if (!$this->has($type)) {
+ return $default;
+ }
+
+ $return = null;
+ if (isset($this->flashes['new'][$type])) {
+ unset($this->flashes['new'][$type]);
+ }
+
+ if (isset($this->flashes['display'][$type])) {
+ $return = $this->flashes['display'][$type];
+ unset($this->flashes['display'][$type]);
+ }
+
+ return $return;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all()
+ {
+ $return = $this->flashes['display'];
+ $this->flashes = array('new' => array(), 'display' => array());
+
+ return $return;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setAll(array $messages)
+ {
+ $this->flashes['new'] = $messages;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set($type, $message)
+ {
+ $this->flashes['new'][$type] = $message;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function has($type)
+ {
+ return array_key_exists($type, $this->flashes['display']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function keys()
+ {
+ return array_keys($this->flashes['display']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getStorageKey()
+ {
+ return $this->storageKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ $return = $this->all();
+ $this->flashes = array('display' => array(), 'new' => array());
+
+ return $return;
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php b/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php
new file mode 100644
index 0000000000..cbb77af03d
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php
@@ -0,0 +1,158 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Flash;
+
+/**
+ * FlashBag flash message container.
+ *
+ * @author Drak
+ */
+class FlashBag implements FlashBagInterface
+{
+ private $name = 'flashes';
+
+ /**
+ * Flash messages.
+ *
+ * @var array
+ */
+ private $flashes = array();
+
+ /**
+ * 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 getName()
+ {
+ return $this->name;
+ }
+
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function initialize(array &$flashes)
+ {
+ $this->flashes = &$flashes;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function peek($type, $default = null)
+ {
+ return $this->has($type) ? $this->flashes[$type] : $default;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function peekAll()
+ {
+ return $this->flashes;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get($type, $default = null)
+ {
+ if (!$this->has($type)) {
+ return $default;
+ }
+
+ $return = $this->flashes[$type];
+
+ unset($this->flashes[$type]);
+
+ return $return;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all()
+ {
+ $return = $this->peekAll();
+ $this->flashes = array();
+
+ return $return;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set($type, $message)
+ {
+ $this->flashes[$type] = $message;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setAll(array $messages)
+ {
+ $this->flashes = $messages;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function has($type)
+ {
+ return array_key_exists($type, $this->flashes);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function keys()
+ {
+ return array_keys($this->flashes);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getStorageKey()
+ {
+ return $this->storageKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ return $this->all();
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php b/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php
new file mode 100644
index 0000000000..0c45d74bb7
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php
@@ -0,0 +1,85 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Flash;
+
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+
+/**
+ * FlashBagInterface.
+ *
+ * @author Drak
+ */
+interface FlashBagInterface extends SessionBagInterface
+{
+ /**
+ * Registers a message for a given type.
+ *
+ * @param string $type
+ * @param string $message
+ */
+ function set($type, $message);
+
+ /**
+ * Gets flash message for a given type.
+ *
+ * @param string $type Message category type.
+ * @param string $default Default value if $type doee not exist.
+ *
+ * @return string
+ */
+ function peek($type, $default = null);
+
+ /**
+ * Gets all flash messages.
+ *
+ * @return array
+ */
+ function peekAll();
+
+ /**
+ * Gets and clears flash from the stack.
+ *
+ * @param string $type
+ * @param string $default Default value if $type doee not exist.
+ *
+ * @return string
+ */
+ function get($type, $default = null);
+
+ /**
+ * Gets and clears flashes from the stack.
+ *
+ * @return array
+ */
+ function all();
+
+ /**
+ * Sets all flash messages.
+ */
+ function setAll(array $messages);
+
+ /**
+ * 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 keys();
+}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Session.php b/src/Symfony/Component/HttpFoundation/Session/Session.php
new file mode 100644
index 0000000000..468040e3aa
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Session/Session.php
@@ -0,0 +1,318 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session;
+
+use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface;
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface;
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+
+/**
+ * Session.
+ *
+ * @author Fabien Potencier
+ * @author Drak
+ *
+ * @api
+ */
+class Session implements SessionInterface
+{
+ /**
+ * Storage driver.
+ *
+ * @var SessionStorageInterface
+ */
+ protected $storage;
+
+ /**
+ * Constructor.
+ *
+ * @param SessionStorageInterface $storage A SessionStorageInterface instance.
+ * @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(SessionStorageInterface $storage, AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null)
+ {
+ $this->storage = $storage;
+ $this->registerBag($attributes ?: new AttributeBag());
+ $this->registerBag($flashes ?: new FlashBag());
+ }
+
+ /**
+ * Starts the session storage.
+ *
+ * @return boolean True if session started.
+ *
+ * @api
+ */
+ public function start()
+ {
+ return $this->storage->start();
+ }
+
+ /**
+ * Checks if an attribute is defined.
+ *
+ * @param string $name The attribute name
+ *
+ * @return Boolean true if the attribute is defined, false otherwise
+ *
+ * @api
+ */
+ public function has($name)
+ {
+ return $this->storage->getBag('attributes')->has($name);
+ }
+
+ /**
+ * Returns an attribute.
+ *
+ * @param string $name The attribute name
+ * @param mixed $default The default value
+ *
+ * @return mixed
+ *
+ * @api
+ */
+ public function get($name, $default = null)
+ {
+ return $this->storage->getBag('attributes')->get($name, $default);
+ }
+
+ /**
+ * Sets an attribute.
+ *
+ * @param string $name
+ * @param mixed $value
+ *
+ * @api
+ */
+ public function set($name, $value)
+ {
+ $this->storage->getBag('attributes')->set($name, $value);
+ }
+
+ /**
+ * Returns attributes.
+ *
+ * @return array Attributes
+ *
+ * @api
+ */
+ public function all()
+ {
+ return $this->storage->getBag('attributes')->all();
+ }
+
+ /**
+ * Sets attributes.
+ *
+ * @param array $attributes Attributes
+ *
+ * @api
+ */
+ public function replace(array $attributes)
+ {
+ $this->storage->getBag('attributes')->replace($attributes);
+ }
+
+ /**
+ * Removes an attribute.
+ *
+ * @param string $name
+ *
+ * @api
+ */
+ public function remove($name)
+ {
+ return $this->storage->getBag('attributes')->remove($name);
+ }
+
+ /**
+ * Clears all attributes.
+ *
+ * @api
+ */
+ public function clear()
+ {
+ $this->storage->getBag('attributes')->clear();
+ }
+
+ /**
+ * Invalidates the current session.
+ *
+ * Clears all session attributes and flashes and regenerates the
+ * session and deletes the old session from persistence.
+ *
+ * @return boolean True if session invalidated, false if error.
+ *
+ * @api
+ */
+ public function invalidate()
+ {
+ $this->storage->clear();
+
+ return $this->storage->regenerate(true);
+ }
+
+ /**
+ * Migrates the current session to a new session id while maintaining all
+ * session attributes.
+ *
+ * @param boolean $destroy Whether to delete the old session or leave it to garbage collection.
+ *
+ * @return boolean True if session migrated, false if error
+ *
+ * @api
+ */
+ public function migrate($destroy = false)
+ {
+ return $this->storage->regenerate($destroy);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function save()
+ {
+ $this->storage->save();
+ }
+
+ /**
+ * Returns the session ID
+ *
+ * @return mixed The session ID
+ *
+ * @api
+ */
+ public function getId()
+ {
+ return $this->storage->getId();
+ }
+
+ /**
+ * Implements the \Serialize interface.
+ *
+ * @return SessionStorageInterface
+ */
+ public function serialize()
+ {
+ return serialize($this->storage);
+ }
+
+ /**
+ * Implements the \Serialize interface.
+ *
+ * @throws \InvalidArgumentException If the passed string does not unserialize to an instance of SessionStorageInterface
+ */
+ public function unserialize($serialized)
+ {
+ $storage = unserialize($serialized);
+ if (!$storage instanceof SessionStorageInterface) {
+ throw new \InvalidArgumentException('Serialized data did not return a valid instance of SessionStorageInterface');
+ }
+
+ $this->storage = $storage;
+ }
+
+ /**
+ * Registers a SessionBagInterface with the sessio.
+ *
+ * @param SessionBagInterface $bag
+ */
+ public function registerBag(SessionBagInterface $bag)
+ {
+ $this->storage->registerBag($bag);
+ }
+
+ /**
+ * Get's a bag instance.
+ *
+ * @param string $name
+ *
+ * @return SessionBagInterface
+ */
+ public function getBag($name)
+ {
+ return $this->storage->getBag($name);
+ }
+
+ /**
+ * Gets the flashbag interface.
+ *
+ * @return FlashBagInterface
+ */
+ public function getFlashBag()
+ {
+ return $this->getBag('flashes');
+ }
+
+ // the following methods are kept for compatibility with Symfony 2.0 (they will be removed for Symfony 2.3)
+
+ /**
+ * @deprecated since 2.1, will be removed from 2.3
+ */
+ public function getFlashes()
+ {
+ return $this->getBag('flashes')->all();
+ }
+
+ /**
+ * @deprecated since 2.1, will be removed from 2.3
+ */
+ public function setFlashes($values)
+ {
+ $this->getBag('flashes')->setAll($values);
+ }
+
+ /**
+ * @deprecated since 2.1, will be removed from 2.3
+ */
+ public function getFlash($name, $default = null)
+ {
+ return $this->getBag('flashes')->get($name, $default);
+ }
+
+ /**
+ * @deprecated since 2.1, will be removed from 2.3
+ */
+ public function setFlash($name, $value)
+ {
+ $this->getBag('flashes')->set($name, $value);
+ }
+
+ /**
+ * @deprecated since 2.1, will be removed from 2.3
+ */
+ public function hasFlash($name)
+ {
+ return $this->getBag('flashes')->has($name);
+ }
+
+ /**
+ * @deprecated since 2.1, will be removed from 2.3
+ */
+ public function removeFlash($name)
+ {
+ $this->getBag('flashes')->get($name);
+ }
+
+ /**
+ * @deprecated since 2.1, will be removed from 2.3
+ */
+ public function clearFlashes()
+ {
+ return $this->getBag('flashes')->clear();
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/Session/SessionBagInterface.php b/src/Symfony/Component/HttpFoundation/Session/SessionBagInterface.php
new file mode 100644
index 0000000000..50c2d4bd0c
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Session/SessionBagInterface.php
@@ -0,0 +1,48 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session;
+
+/**
+ * Session Bag store.
+ *
+ * @author Drak
+ */
+interface SessionBagInterface
+{
+ /**
+ * Gets this bag's name
+ *
+ * @return string
+ */
+ function getName();
+
+ /**
+ * Initializes the Bag
+ *
+ * @param array $array
+ */
+ function initialize(array &$array);
+
+ /**
+ * Gets the storage key for this bag.
+ *
+ * @return string
+ */
+ function getStorageKey();
+
+ /**
+ * Clears out data from bag.
+ *
+ * @return mixed Whatever data was contained.
+ */
+ function clear();
+}
diff --git a/src/Symfony/Component/HttpFoundation/Session/SessionInterface.php b/src/Symfony/Component/HttpFoundation/Session/SessionInterface.php
new file mode 100644
index 0000000000..c31da6d165
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Session/SessionInterface.php
@@ -0,0 +1,108 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session;
+
+/**
+ * Interface for the session.
+ *
+ * @author Drak
+ */
+interface SessionInterface extends \Serializable
+{
+ /**
+ * Starts the session storage.
+ *
+ * @throws \RuntimeException 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.
+ *
+ * @param boolean $destroy Whether to delete the old session or leave it to garbage collection.
+ *
+ * @return boolean True if session migrated, false if error.
+ *
+ * @api
+ */
+ function migrate($destroy = false);
+
+ /**
+ * Force the session to be saved and closed.
+ *
+ * This method is generally not required for real sessions as
+ * the session will be automatically saved at the end of
+ * code execution.
+ */
+ function save();
+
+ /**
+ * 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/Session/Storage/AbstractSessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/AbstractSessionStorage.php
new file mode 100644
index 0000000000..c703d09a00
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/AbstractSessionStorage.php
@@ -0,0 +1,339 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage;
+
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+
+/**
+ * This provides a base class for session attribute storage.
+ *
+ * @author Drak
+ */
+abstract class AbstractSessionStorage implements SessionStorageInterface
+{
+ /**
+ * Array of SessionBagInterface
+ *
+ * @var array
+ */
+ protected $bags;
+
+ /**
+ * @var array
+ */
+ protected $options = array();
+
+ /**
+ * @var boolean
+ */
+ protected $started = false;
+
+ /**
+ * @var boolean
+ */
+ protected $closed = 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.
+ * @see http://www.php.net/manual/en/session.configuration.php for options
+ * 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"
+ * upload_progress.enabled, "1"
+ * upload_progress.cleanup, "1"
+ * upload_progress.prefix, "upload_progress_"
+ * upload_progress.name, "PHP_SESSION_UPLOAD_PROGRESS"
+ * upload_progress.freq, "1%"
+ * upload_progress.min-freq, "1"
+ * url_rewriter.tags, "a=href,area=href,frame=src,form=,fieldset="
+ *
+ * @param array $options Session configuration options.
+ */
+ public function __construct(array $options = array())
+ {
+ $this->setOptions($options);
+ $this->registerSaveHandlers();
+ $this->registerShutdownFunction();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function start()
+ {
+ if ($this->started && !$this->closed) {
+ return true;
+ }
+
+ if ($this->options['use_cookies'] && headers_sent()) {
+ throw new \RuntimeException('Failed to start the session because header have already been sent.');
+ }
+
+ // start the session
+ if (!session_start()) {
+ throw new \RuntimeException('Failed to start the session');
+ }
+
+ $this->loadSession();
+
+ $this->started = true;
+ $this->closed = false;
+
+ return 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();
+ $this->closed = true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ // clear out the bags
+ foreach ($this->bags as $bag) {
+ $bag->clear();
+ }
+
+ // clear out the session
+ $_SESSION = array();
+
+ // reconnect the bags to the session
+ $this->loadSession();
+ }
+
+ /**
+ * Register a SessionBagInterface for use.
+ *
+ * @param SessionBagInterface $bag
+ */
+ public function registerBag(SessionBagInterface $bag)
+ {
+ $this->bags[$bag->getName()] = $bag;
+ }
+
+ /**
+ * Gets a bag by name.
+ *
+ * @param string $name
+ *
+ * @return SessionBagInterface
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function getBag($name)
+ {
+ if (!isset($this->bags[$name])) {
+ throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name));
+ }
+
+ if ($this->options['auto_start'] && !$this->started) {
+ $this->start();
+ }
+
+ return $this->bags[$name];
+ }
+
+ /**
+ * Sets session.* ini variables.
+ *
+ * For convenience we omit 'session.' from the beginning of the keys.
+ * Explicitly ignores other ini keys.
+ *
+ * session_get_cookie_params() overrides values.
+ *
+ * @param array $options
+ *
+ * @see http://www.php.net/manual/en/session.configuration.php
+ */
+ protected function setOptions(array $options)
+ {
+ $cookieDefaults = session_get_cookie_params();
+ $this->options = array_merge(array(
+ 'cookie_lifetime' => $cookieDefaults['lifetime'],
+ 'cookie_path' => $cookieDefaults['path'],
+ 'cookie_domain' => $cookieDefaults['domain'],
+ 'cookie_secure' => $cookieDefaults['secure'],
+ 'cookie_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 (!isset($this->options['cache_limiter'])) {
+ $this->options['cache_limiter'] = 0;
+ }
+
+ if (!isset($this->options['auto_start'])) {
+ $this->options['auto_start'] = 0;
+ }
+
+ if (!isset($this->options['use_cookies'])) {
+ $this->options['use_cookies'] = 1;
+ }
+
+ foreach ($this->options as $key => $value) {
+ if (in_array($key, array(
+ 'auto_start', 'cookie_domain', 'cookie_httponly',
+ 'cookie_lifetime', 'cookie_path', 'cookie_secure',
+ 'entropy_file', 'entropy_length', 'gc_divisor',
+ 'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character',
+ 'hash_function', 'name', 'referer_check',
+ 'save_path', 'serialize_handler', 'use_cookies',
+ 'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled',
+ 'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name',
+ 'upload_progress.freq', 'upload_progress.min-freq', 'url_rewriter.tags'))) {
+ 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, 'openSession'),
+ array($this, 'closeSession'),
+ array($this, 'readSession'),
+ array($this, 'writeSession'),
+ array($this, 'destroySession'),
+ array($this, 'gcSession')
+ );
+ }
+ }
+
+ /**
+ * 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');
+ }
+
+ /**
+ * Load the session with attributes.
+ *
+ * After starting the session, PHP retrieves the session from whatever handlers
+ * are set to (either PHP's internal, custom set with session_set_save_handler()).
+ * PHP takes the return value from the sessionRead() handler, unserializes it
+ * and populates $_SESSION with the result automatically.
+ */
+ protected function loadSession(array &$session = null)
+ {
+ if (null === $session) {
+ $session = &$_SESSION;
+ }
+
+ foreach ($this->bags as $bag) {
+ $key = $bag->getStorageKey();
+ $session[$key] = isset($session[$key]) ? $session[$key] : array();
+ $bag->initialize($session[$key]);
+ }
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/MemcacheSessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/MemcacheSessionStorage.php
new file mode 100644
index 0000000000..f5d5910d86
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/MemcacheSessionStorage.php
@@ -0,0 +1,136 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage;
+
+/**
+ * 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.
+ *
+ * @see AbstractSessionStorage::__construct()
+ */
+ public function __construct(\Memcache $memcache, array $memcacheOptions = array(), array $options = array())
+ {
+ $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($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 openSession($savePath, $sessionName)
+ {
+ foreach ($this->memcacheOptions['serverpool'] as $server) {
+ $this->addServer($server);
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function closeSession()
+ {
+ return $this->memcache->close();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function readSession($sessionId)
+ {
+ return $this->memcache->get($this->prefix.$sessionId) ?: '';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function writeSession($sessionId, $data)
+ {
+ return $this->memcache->set($this->prefix.$sessionId, $data, $this->memcacheOptions['expiretime']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function destroySession($sessionId)
+ {
+ return $this->memcache->delete($this->prefix.$sessionId);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function gcSession($lifetime)
+ {
+ // not required here because memcache will auto expire the records anyhow.
+ return true;
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/MemcachedSessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/MemcachedSessionStorage.php
new file mode 100644
index 0000000000..f7041811a3
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/MemcachedSessionStorage.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\Session\Storage;
+
+/**
+ * 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.
+ *
+ * @see AbstractSessionStorage::__construct()
+ */
+ public function __construct(\Memcached $memcache, array $memcachedOptions = array(), array $options = array())
+ {
+ $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($options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function openSession($savePath, $sessionName)
+ {
+ foreach ($this->memcachedOptions['serverpool'] as $server) {
+ $this->addServer($server);
+ }
+
+ return true;
+ }
+
+ /**
+ * Close session.
+ *
+ * @return boolean
+ */
+ public function closeSession()
+ {
+ return $this->memcached->close();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function readSession($sessionId)
+ {
+ return $this->memcached->get($this->prefix.$sessionId) ?: '';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function writeSession($sessionId, $data)
+ {
+ return $this->memcached->set($this->prefix.$sessionId, $data, false, $this->memcachedOptions['expiretime']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function destroySession($sessionId)
+ {
+ return $this->memcached->delete($this->prefix.$sessionId);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function gcSession($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/Session/Storage/MockArraySessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php
new file mode 100644
index 0000000000..2fd1a94c26
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php
@@ -0,0 +1,124 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage;
+
+/**
+ * MockArraySessionStorage mocks the session for unit tests.
+ *
+ * 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 MockFileSessionStorage instead.
+ *
+ * @author Fabien Potencier
+ * @author Bulat Shakirzyanov
+ * @author Drak
+ */
+class MockArraySessionStorage extends AbstractSessionStorage
+{
+ /**
+ * @var string
+ */
+ protected $sessionId;
+
+ /**
+ * @var array
+ */
+ private $sessionData = array();
+
+ public function setSessionData(array $array)
+ {
+ $this->sessionData = $array;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function start()
+ {
+ if ($this->started && !$this->closed) {
+ return true;
+ }
+
+ $this->started = true;
+ $this->loadSession($this->sessionData);
+
+ $this->sessionId = $this->generateSessionId();
+ session_id($this->sessionId);
+
+ return true;
+ }
+
+
+ /**
+ * {@inheritdoc}
+ */
+ public function regenerate($destroy = false)
+ {
+ if ($this->options['auto_start'] && !$this->started) {
+ $this->start();
+ }
+
+ $this->sessionId = $this->generateSessionId();
+ session_id($this->sessionId);
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getId()
+ {
+ if (!$this->started) {
+ return '';
+ }
+
+ return $this->sessionId;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function save()
+ {
+ // nothing to do since we don't persist the session data
+ $this->closed = false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ // clear out the bags
+ foreach ($this->bags as $bag) {
+ $bag->clear();
+ }
+
+ // clear out the session
+ $this->sessionData = array();
+
+ // reconnect the bags to the session
+ $this->loadSession($this->sessionData);
+ }
+
+ /**
+ * Generates a session ID.
+ *
+ * @return string
+ */
+ protected function generateSessionId()
+ {
+ return sha1(uniqid(mt_rand(), true));
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php
new file mode 100644
index 0000000000..094c4d6005
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php
@@ -0,0 +1,136 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage;
+
+/**
+ * MockFileSessionStorage 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 MockFileSessionStorage extends MockArraySessionStorage
+{
+ /**
+ * @var string
+ */
+ private $savePath;
+
+ /**
+ * Constructor.
+ *
+ * @param string $savePath Path of directory to save session files.
+ * @param array $options Session options.
+ *
+ * @see AbstractSessionStorage::__construct()
+ */
+ public function __construct($savePath = null, array $options = array())
+ {
+ if (null === $savePath) {
+ $savePath = sys_get_temp_dir();
+ }
+
+ if (!is_dir($savePath)) {
+ mkdir($savePath, 0777, true);
+ }
+
+ $this->savePath = $savePath;
+
+ parent::__construct($options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function start()
+ {
+ if ($this->started) {
+ return true;
+ }
+
+ if (!session_id()) {
+ session_id($this->generateSessionId());
+ }
+
+ $this->sessionId = session_id();
+
+ $this->read();
+
+ $this->started = true;
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function regenerate($destroy = false)
+ {
+ if ($destroy) {
+ $this->destroy();
+ }
+
+ session_id($this->generateSessionId());
+ $this->sessionId = session_id();
+
+ $this->save();
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getId()
+ {
+ if (!$this->started) {
+ return '';
+ }
+
+ return $this->sessionId;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function save()
+ {
+ file_put_contents($this->getFilePath(), serialize($this->sessionData));
+ }
+
+ private function destroy()
+ {
+ if (is_file($this->getFilePath())) {
+ unlink($this->getFilePath());
+ }
+ }
+
+ /**
+ * Calculate path to file.
+ *
+ * @return string File path
+ */
+ public function getFilePath()
+ {
+ return $this->savePath.'/'.$this->sessionId.'.sess';
+ }
+
+ private function read()
+ {
+ $filePath = $this->getFilePath();
+ $this->sessionData = is_readable($filePath) && is_file($filePath) ? unserialize(file_get_contents($filePath)) : array();
+
+ $this->loadSession($this->sessionData);
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/NativeFileSessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeFileSessionStorage.php
new file mode 100644
index 0000000000..09350e8d21
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeFileSessionStorage.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\Session\Storage;
+
+/**
+ * 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.
+ *
+ * @see AbstractSessionStorage::__construct()
+ */
+ public function __construct($savePath = null, array $options = array())
+ {
+ if (null === $savePath) {
+ $savePath = sys_get_temp_dir();
+ }
+
+ if (!is_dir($savePath)) {
+ mkdir($savePath, 0777, true);
+ }
+
+ $this->savePath = $savePath;
+
+ parent::__construct($options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function registerSaveHandlers()
+ {
+ ini_set('session.save_handler', 'files');
+ ini_set('session.save_path', $this->savePath);
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/NativeMemcacheSessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeMemcacheSessionStorage.php
new file mode 100644
index 0000000000..5572c47812
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeMemcacheSessionStorage.php
@@ -0,0 +1,77 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage;
+
+/**
+ * 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.
+ *
+ * @see AbstractSessionStorage::__construct()
+ */
+ public function __construct($savePath = 'tcp://127.0.0.1:11211?persistent=0', array $options = array())
+ {
+ if (!extension_loaded('memcache')) {
+ throw new \RuntimeException('PHP does not have "memcache" session module registered');
+ }
+
+ $this->savePath = $savePath;
+ parent::__construct($options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function registerSaveHandlers()
+ {
+ ini_set('session.save_handler', 'memcache');
+ ini_set('session.save_path', $this->savePath);
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * Sets any values memcached ini values.
+ *
+ * @see http://www.php.net/manual/en/memcache.ini.php
+ */
+ protected function setOptions(array $options)
+ {
+ foreach ($options as $key => $value) {
+ if (in_array($key, array(
+ 'memcache.allow_failover', 'memcache.max_failover_attempts',
+ 'memcache.chunk_size', 'memcache.default_port', 'memcache.hash_strategy',
+ 'memcache.hash_function', 'memcache.protocol', 'memcache.redundancy',
+ 'memcache.session_redundancy', 'memcache.compress_threshold',
+ 'memcache.lock_timeout'))) {
+ ini_set($key, $value);
+ }
+ }
+
+ parent::setOptions($options);
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/NativeMemcachedSessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeMemcachedSessionStorage.php
new file mode 100644
index 0000000000..50d8d060fd
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeMemcachedSessionStorage.php
@@ -0,0 +1,76 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage;
+
+/**
+ * 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.
+ *
+ * @see AbstractSessionStorage::__construct()
+ */
+ public function __construct($savePath = '127.0.0.1:11211', array $options = array())
+ {
+ if (!extension_loaded('memcached')) {
+ throw new \RuntimeException('PHP does not have "memcached" session module registered');
+ }
+
+ $this->savePath = $savePath;
+ parent::__construct($options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function registerSaveHandlers()
+ {
+ ini_set('session.save_handler', 'memcached');
+ ini_set('session.save_path', $this->savePath);
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * Sets any values memcached ini values.
+ *
+ * @see https://github.com/php-memcached-dev/php-memcached/blob/master/memcached.ini
+ */
+ protected function setOptions(array $options)
+ {
+ foreach ($options as $key => $value) {
+ if (in_array($key, array(
+ 'memcached.sess_locking', 'memcached.sess_lock_wait',
+ 'memcached.sess_prefix', 'memcached.compression_type',
+ 'memcached.compression_factor', 'memcached.compression_threshold',
+ 'memcached.serializer'))) {
+ ini_set($key, $value);
+ }
+ }
+
+ parent::setOptions($options);
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSqliteSessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSqliteSessionStorage.php
new file mode 100644
index 0000000000..1dac36ac2f
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSqliteSessionStorage.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage;
+
+/**
+ * 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.
+ *
+ * @see AbstractSessionStorage::__construct()
+ */
+ public function __construct($dbPath, array $options = array())
+ {
+ if (!extension_loaded('sqlite')) {
+ throw new \RuntimeException('PHP does not have "sqlite" session module registered');
+ }
+
+ $this->dbPath = $dbPath;
+ parent::__construct($options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function registerSaveHandlers()
+ {
+ ini_set('session.save_handler', 'sqlite');
+ ini_set('session.save_path', $this->dbPath);
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/NullSessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/NullSessionStorage.php
new file mode 100644
index 0000000000..b81fa8ae3a
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/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\Session\Storage;
+
+/**
+ * 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 openSession($savePath, $sessionName)
+ {
+ return true;
+ }
+
+ /**
+ * Close session.
+ *
+ * @return boolean
+ */
+ public function closeSession()
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function readSession($sessionId)
+ {
+ return '';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function writeSession($sessionId, $data)
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function destroySession($sessionId)
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function gcSession($lifetime)
+ {
+ return true;
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/SessionStorage/PdoSessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/PdoSessionStorage.php
similarity index 68%
rename from src/Symfony/Component/HttpFoundation/SessionStorage/PdoSessionStorage.php
rename to src/Symfony/Component/HttpFoundation/Session/Storage/PdoSessionStorage.php
index 24898b7c20..2015396b2f 100644
--- a/src/Symfony/Component/HttpFoundation/SessionStorage/PdoSessionStorage.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/PdoSessionStorage.php
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
-namespace Symfony\Component\HttpFoundation\SessionStorage;
+namespace Symfony\Component\HttpFoundation\Session\Storage;
/**
* PdoSessionStorage.
@@ -17,18 +17,19 @@ namespace Symfony\Component\HttpFoundation\SessionStorage;
* @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,21 +37,22 @@ 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
*
* @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())
{
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',
@@ -61,61 +63,27 @@ class PdoSessionStorage extends NativeSessionStorage
}
/**
- * Starts the session.
+ * {@inheritdoc}
*/
- 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();
- }
-
- /**
- * Opens a session.
- *
- * @param string $path (ignored)
- * @param string $name (ignored)
- *
- * @return Boolean true, if the session was opened, otherwise an exception is thrown
- */
- public function sessionOpen($path = null, $name = null)
+ public function openSession($path = null, $name = null)
{
return true;
}
/**
- * Closes a session.
- *
- * @return Boolean true, if the session was closed, otherwise false
+ * {@inheritdoc}
*/
- public function sessionClose()
+ public function closeSession()
{
- // 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
*/
- public function sessionDestroy($id)
+ public function destroySession($id)
{
// get table/column
$dbTable = $this->dbOptions['db_table'];
@@ -125,7 +93,7 @@ class PdoSessionStorage extends NativeSessionStorage
$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 +104,11 @@ class PdoSessionStorage extends NativeSessionStorage
}
/**
- * 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 gcSession($lifetime)
{
// get table/column
$dbTable = $this->dbOptions['db_table'];
@@ -154,7 +118,7 @@ class PdoSessionStorage extends NativeSessionStorage
$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,15 +129,11 @@ class PdoSessionStorage extends NativeSessionStorage
}
/**
- * 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
*/
- public function sessionRead($id)
+ public function readSession($id)
{
// get table/columns
$dbTable = $this->dbOptions['db_table'];
@@ -183,7 +143,7 @@ class PdoSessionStorage extends NativeSessionStorage
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();
@@ -200,21 +160,16 @@ class PdoSessionStorage extends NativeSessionStorage
return '';
} catch (\PDOException $e) {
- throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e);
+ throw new \RuntimeException(sprintf('PDOException was thrown when trying to read the session data: %s', $e->getMessage()), 0, $e);
}
}
/**
- * 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
*/
- public function sessionWrite($id, $data)
+ public function writeSession($id, $data)
{
// get table/column
$dbTable = $this->dbOptions['db_table'];
@@ -222,7 +177,7 @@ class PdoSessionStorage extends NativeSessionStorage
$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 +185,7 @@ class PdoSessionStorage extends NativeSessionStorage
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);
@@ -242,7 +197,7 @@ class PdoSessionStorage extends NativeSessionStorage
$this->createNewSession($id, $data);
}
} catch (\PDOException $e) {
- throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e);
+ throw new \RuntimeException(sprintf('PDOException was thrown when trying to write the session data: %s', $e->getMessage()), 0, $e);
}
return true;
@@ -268,7 +223,7 @@ class PdoSessionStorage extends NativeSessionStorage
//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/Session/Storage/SessionSaveHandlerInterface.php b/src/Symfony/Component/HttpFoundation/Session/Storage/SessionSaveHandlerInterface.php
new file mode 100644
index 0000000000..ec4530a150
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/SessionSaveHandlerInterface.php
@@ -0,0 +1,157 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage;
+
+/**
+ * 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 readSession() 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 already serialized (using session_encode()) to
+ * be stored.
+ *
+ * When a session is specifically destroyed, PHP will call the
+ * sessionSession() 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 sessionSession() 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 openSession() and closeSession() 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 openSession($savePath, $sessionName);
+
+ /**
+ * Close session.
+ *
+ * This method is for internal use by PHP and must not be called manually.
+ *
+ * @return boolean
+ */
+ function closeSession();
+
+ /**
+ * 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 unserialized automatically 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 readSession($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 writeSession($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 destroySession($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 gcSession($lifetime);
+}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php b/src/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php
new file mode 100644
index 0000000000..7eb720b7c6
--- /dev/null
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php
@@ -0,0 +1,92 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage;
+
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+
+/**
+ * StorageInterface.
+ *
+ * @author Fabien Potencier
+ * @author Drak
+ *
+ * @api
+ */
+interface SessionStorageInterface
+{
+ /**
+ * Starts the session.
+ *
+ * @throws \RuntimeException If something goes wrong starting the session.
+ *
+ * @return boolean True if started.
+ *
+ * @api
+ */
+ function start();
+
+ /**
+ * Returns the session ID
+ *
+ * @return mixed The session ID or false if the session has not started.
+ *
+ * @api
+ */
+ function getId();
+
+ /**
+ * Regenerates id that represents this storage.
+ *
+ * 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 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
+ */
+ function regenerate($destroy = false);
+
+ /**
+ * Force the session to be saved and closed.
+ *
+ * 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 save();
+
+ /**
+ * Clear all session data in memory.
+ */
+ function clear();
+
+ /**
+ * Gets a SessionBagInterface by name.
+ *
+ * @return SessionBagInterface
+ */
+ function getBag($name);
+
+ /**
+ * Registers a SessionBagInterface for use.
+ *
+ * @param SessionBagInterface $bag
+ */
+ function registerBag(SessionBagInterface $bag);
+}
diff --git a/src/Symfony/Component/HttpFoundation/SessionStorage/ArraySessionStorage.php b/src/Symfony/Component/HttpFoundation/SessionStorage/ArraySessionStorage.php
deleted file mode 100644
index 5a6558a2b3..0000000000
--- a/src/Symfony/Component/HttpFoundation/SessionStorage/ArraySessionStorage.php
+++ /dev/null
@@ -1,81 +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;
-
-/**
- * ArraySessionStorage mocks the session for unit tests.
- *
- * When doing functional testing, you should use FilesystemSessionStorage instead.
- *
- * @author Fabien Potencier
- * @author Bulat Shakirzyanov
- */
-
-class ArraySessionStorage implements SessionStorageInterface
-{
- /**
- * Storage data.
- *
- * @var array
- */
- private $data = array();
-
- /**
- * {@inheritdoc}
- */
- public function read($key, $default = null)
- {
- return array_key_exists($key, $this->data) ? $this->data[$key] : $default;
- }
-
- /**
- * {@inheritdoc}
- */
- public function regenerate($destroy = false)
- {
- if ($destroy) {
- $this->data = array();
- }
-
- return true;
- }
-
- /**
- * {@inheritdoc}
- */
- public function remove($key)
- {
- unset($this->data[$key]);
- }
-
- /**
- * {@inheritdoc}
- */
- public function start()
- {
- }
-
- /**
- * {@inheritdoc}
- */
- public function getId()
- {
- }
-
- /**
- * {@inheritdoc}
- */
- public function write($key, $data)
- {
- $this->data[$key] = $data;
- }
-}
diff --git a/src/Symfony/Component/HttpFoundation/SessionStorage/FilesystemSessionStorage.php b/src/Symfony/Component/HttpFoundation/SessionStorage/FilesystemSessionStorage.php
deleted file mode 100644
index ceb5913fe9..0000000000
--- a/src/Symfony/Component/HttpFoundation/SessionStorage/FilesystemSessionStorage.php
+++ /dev/null
@@ -1,192 +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
- * @param string $default The default value
- *
- * @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/NativeSessionStorage.php b/src/Symfony/Component/HttpFoundation/SessionStorage/NativeSessionStorage.php
deleted file mode 100644
index b759f7411a..0000000000
--- 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/SessionStorageInterface.php b/src/Symfony/Component/HttpFoundation/SessionStorage/SessionStorageInterface.php
deleted file mode 100644
index b61a2557b2..0000000000
--- a/src/Symfony/Component/HttpFoundation/SessionStorage/SessionStorageInterface.php
+++ /dev/null
@@ -1,97 +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;
-
-/**
- * SessionStorageInterface.
- *
- * @author Fabien Potencier
- *
- * @api
- */
-interface SessionStorageInterface
-{
- /**
- * Starts the session.
- *
- * @api
- */
- function start();
-
- /**
- * Returns the session ID
- *
- * @return mixed The session ID
- *
- * @throws \RuntimeException If the session was not started yet
- *
- * @api
- */
- function getId();
-
- /**
- * 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
- */
- function read($key);
-
- /**
- * 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
- */
- function remove($key);
-
- /**
- * 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
- */
- function write($key, $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
- */
- function regenerate($destroy = false);
-}
diff --git a/tests/Symfony/Tests/Component/Form/Extension/Csrf/CsrfProvider/SessionCsrfProviderTest.php b/tests/Symfony/Tests/Component/Form/Extension/Csrf/CsrfProvider/SessionCsrfProviderTest.php
index 958a328295..3358ffcb21 100644
--- a/tests/Symfony/Tests/Component/Form/Extension/Csrf/CsrfProvider/SessionCsrfProviderTest.php
+++ b/tests/Symfony/Tests/Component/Form/Extension/Csrf/CsrfProvider/SessionCsrfProviderTest.php
@@ -21,7 +21,7 @@ class SessionCsrfProviderTest extends \PHPUnit_Framework_TestCase
protected function setUp()
{
$this->session = $this->getMock(
- 'Symfony\Component\HttpFoundation\Session',
+ 'Symfony\Component\HttpFoundation\Session\Session',
array(),
array(),
'',
diff --git a/tests/Symfony/Tests/Component/HttpFoundation/RequestTest.php b/tests/Symfony/Tests/Component/HttpFoundation/RequestTest.php
index 4b208bb3ec..c147070fdf 100644
--- a/tests/Symfony/Tests/Component/HttpFoundation/RequestTest.php
+++ b/tests/Symfony/Tests/Component/HttpFoundation/RequestTest.php
@@ -12,10 +12,8 @@
namespace Symfony\Tests\Component\HttpFoundation;
-use Symfony\Component\HttpFoundation\SessionStorage\ArraySessionStorage;
-
-use Symfony\Component\HttpFoundation\Session;
-
+use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
+use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Request;
class RequestTest extends \PHPUnit_Framework_TestCase
@@ -848,7 +846,7 @@ class RequestTest extends \PHPUnit_Framework_TestCase
$request = new Request;
$this->assertFalse($request->hasSession());
- $request->setSession(new Session(new ArraySessionStorage()));
+ $request->setSession(new Session(new MockArraySessionStorage()));
$this->assertTrue($request->hasSession());
}
@@ -859,7 +857,7 @@ class RequestTest extends \PHPUnit_Framework_TestCase
$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 MockArraySessionStorage()));
$this->assertTrue($request->hasPreviousSession());
}
diff --git a/tests/Symfony/Tests/Component/HttpFoundation/Session/Attribute/AttributeBagTest.php b/tests/Symfony/Tests/Component/HttpFoundation/Session/Attribute/AttributeBagTest.php
new file mode 100644
index 0000000000..a5c921254c
--- /dev/null
+++ b/tests/Symfony/Tests/Component/HttpFoundation/Session/Attribute/AttributeBagTest.php
@@ -0,0 +1,155 @@
+
+ */
+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, $bag->all());
+ $array = array('should' => 'change');
+ $bag->initialize($array);
+ $this->assertEquals($array, $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/Session/Attribute/NamespacedAttributeBagTest.php b/tests/Symfony/Tests/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBagTest.php
new file mode 100644
index 0000000000..94e5d80b07
--- /dev/null
+++ b/tests/Symfony/Tests/Component/HttpFoundation/Session/Attribute/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/Session/Flash/AutoExpireFlashBagTest.php b/tests/Symfony/Tests/Component/HttpFoundation/Session/Flash/AutoExpireFlashBagTest.php
new file mode 100644
index 0000000000..3b12c0eaea
--- /dev/null
+++ b/tests/Symfony/Tests/Component/HttpFoundation/Session/Flash/AutoExpireFlashBagTest.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\Session\Flash;
+
+use Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag as FlashBag;
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
+
+/**
+ * AutoExpireFlashBagTest
+ *
+ * @author Drak
+ */
+class AutoExpireFlashBagTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @var \Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface
+ */
+ private $bag;
+
+ /**
+ * @var array
+ */
+ protected $array = array();
+
+ public function setUp()
+ {
+ parent::setUp();
+ $this->bag = new FlashBag();
+ $this->array = array('new' => array('notice' => 'A previous flash message'));
+ $this->bag->initialize($this->array);
+ }
+
+ public function tearDown()
+ {
+ $this->bag = null;
+ parent::tearDown();
+ }
+
+ public function testInitialize()
+ {
+ $bag = new FlashBag();
+ $array = array('new' => array('notice' => 'A previous flash message'));
+ $bag->initialize($array);
+ $this->assertEquals('A previous flash message', $bag->peek('notice'));
+ $array = array('new' => array(
+ 'notice' => 'Something else',
+ 'error' => 'a',
+ ));
+ $bag->initialize($array);
+ $this->assertEquals('Something else', $bag->peek('notice'));
+ $this->assertEquals('a', $bag->peek('error'));
+ }
+
+ public function testPeek()
+ {
+ $this->assertNull($this->bag->peek('non_existing'));
+ $this->assertEquals('default', $this->bag->peek('non_existing', 'default'));
+ $this->assertEquals('A previous flash message', $this->bag->peek('notice'));
+ $this->assertEquals('A previous flash message', $this->bag->peek('notice'));
+ }
+
+ public function testSet()
+ {
+ $this->bag->set('notice', 'Foo');
+ $this->assertNotEquals('Foo', $this->bag->peek('notice'));
+ }
+
+ public function testHas()
+ {
+ $this->assertFalse($this->bag->has('nothing'));
+ $this->assertTrue($this->bag->has('notice'));
+ }
+
+ public function testKeys()
+ {
+ $this->assertEquals(array('notice'), $this->bag->keys());
+ }
+
+ public function testPeekAll()
+ {
+ $array = array(
+ 'new' => array(
+ 'notice' => 'Foo',
+ 'error' => 'Bar',
+ ),
+ );
+
+ $this->bag->initialize($array);
+ $this->assertEquals(array(
+ 'notice' => 'Foo',
+ 'error' => 'Bar',
+ ), $this->bag->peekAll()
+ );
+
+ $this->assertEquals(array(
+ 'notice' => 'Foo',
+ 'error' => 'Bar',
+ ), $this->bag->peekAll()
+ );
+ }
+
+ public function testGet()
+ {
+ $this->assertNull($this->bag->get('non_existing'));
+ $this->assertEquals('default', $this->bag->get('non_existing', 'default'));
+ $this->assertEquals('A previous flash message', $this->bag->get('notice'));
+ $this->assertNull($this->bag->get('notice'));
+ }
+
+ public function testAll()
+ {
+ $this->bag->set('notice', 'Foo');
+ $this->bag->set('error', 'Bar');
+ $this->assertEquals(array(
+ 'notice' => 'A previous flash message',
+ ), $this->bag->all()
+ );
+
+ $this->assertEquals(array(), $this->bag->all());
+ }
+}
diff --git a/tests/Symfony/Tests/Component/HttpFoundation/Session/Flash/FlashBagTest.php b/tests/Symfony/Tests/Component/HttpFoundation/Session/Flash/FlashBagTest.php
new file mode 100644
index 0000000000..3616ec9156
--- /dev/null
+++ b/tests/Symfony/Tests/Component/HttpFoundation/Session/Flash/FlashBagTest.php
@@ -0,0 +1,121 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Tests\Component\HttpFoundation\Session\Flash;
+
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
+
+/**
+ * FlashBagTest
+ *
+ * @author Drak
+ */
+class FlashBagTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @var \Symfony\Component\HttpFoundation\SessionFlash\FlashBagInterface
+ */
+ private $bag;
+
+ /**
+ * @var array
+ */
+ protected $array = array();
+
+ public function setUp()
+ {
+ parent::setUp();
+ $this->bag = new FlashBag();
+ $this->array = array('notice' => '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, $bag->peekAll());
+ $array = array('should' => array('change'));
+ $bag->initialize($array);
+ $this->assertEquals($array, $bag->peekAll());
+ }
+
+ public function testPeek()
+ {
+ $this->assertNull($this->bag->peek('non_existing'));
+ $this->assertEquals('default', $this->bag->peek('not_existing', 'default'));
+ $this->assertEquals('A previous flash message', $this->bag->peek('notice'));
+ $this->assertEquals('A previous flash message', $this->bag->peek('notice'));
+ }
+
+ public function testGet()
+ {
+ $this->assertNull($this->bag->get('non_existing'));
+ $this->assertEquals('default', $this->bag->get('not_existing', 'default'));
+ $this->assertEquals('A previous flash message', $this->bag->get('notice'));
+ $this->assertNull($this->bag->get('notice'));
+ }
+
+ public function testAll()
+ {
+ $this->bag->set('notice', 'Foo');
+ $this->bag->set('error', 'Bar');
+ $this->assertEquals(array(
+ 'notice' => 'Foo',
+ 'error' => 'Bar'), $this->bag->all()
+ );
+
+ $this->assertEquals(array(), $this->bag->all());
+ }
+
+ public function testSet()
+ {
+ $this->bag->set('notice', 'Foo');
+ $this->bag->set('notice', 'Bar');
+ $this->assertEquals('Bar', $this->bag->peek('notice'));
+ }
+
+ public function testHas()
+ {
+ $this->assertFalse($this->bag->has('nothing'));
+ $this->assertTrue($this->bag->has('notice'));
+ }
+
+ public function testKeys()
+ {
+ $this->assertEquals(array('notice'), $this->bag->keys());
+ }
+
+ public function testPeekAll()
+ {
+ $this->bag->set('notice', 'Foo');
+ $this->bag->set('error', 'Bar');
+ $this->assertEquals(array(
+ 'notice' => 'Foo',
+ 'error' => 'Bar',
+ ), $this->bag->peekAll()
+ );
+ $this->assertTrue($this->bag->has('notice'));
+ $this->assertTrue($this->bag->has('error'));
+ $this->assertEquals(array(
+ 'notice' => 'Foo',
+ 'error' => 'Bar',
+ ), $this->bag->peekAll()
+ );
+ }
+}
diff --git a/tests/Symfony/Tests/Component/HttpFoundation/Session/SessionTest.php b/tests/Symfony/Tests/Component/HttpFoundation/Session/SessionTest.php
new file mode 100644
index 0000000000..3f57670581
--- /dev/null
+++ b/tests/Symfony/Tests/Component/HttpFoundation/Session/SessionTest.php
@@ -0,0 +1,171 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Tests\Component\HttpFoundation\Session;
+
+use Symfony\Component\HttpFoundation\Session\Session;
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
+use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
+
+/**
+ * SessionTest
+ *
+ * @author Fabien Potencier
+ * @author Robert Schönthal
+ * @author Drak
+ */
+class SessionTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @var \Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface
+ */
+ protected $storage;
+
+ /**
+ * @var \Symfony\Component\HttpFoundation\Session\SessionInterface
+ */
+ protected $session;
+
+ public function setUp()
+ {
+ $this->storage = new MockArraySessionStorage();
+ $this->session = new Session($this->storage, new AttributeBag(), new FlashBag());
+ }
+
+ protected function tearDown()
+ {
+ $this->storage = null;
+ $this->session = null;
+ }
+
+ public function testStart()
+ {
+ $this->assertEquals('', $this->session->getId());
+ $this->assertTrue($this->session->start());
+ $this->assertNotEquals('', $this->session->getId());
+ }
+
+ public function testGet()
+ {
+ // tests defaults
+ $this->assertNull($this->session->get('foo'));
+ $this->assertEquals(1, $this->session->get('foo', 1));
+ }
+
+ /**
+ * @dataProvider setProvider
+ */
+ public function testSet($key, $value)
+ {
+ $this->session->set($key, $value);
+ $this->assertEquals($value, $this->session->get($key));
+ }
+
+ 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());
+ }
+
+ /**
+ * @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->assertEquals(array(), $this->session->all());
+ }
+
+ public function setProvider()
+ {
+ 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')),
+ );
+ }
+
+ /**
+ * @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());
+ }
+
+ public function testInvalidate()
+ {
+ $this->session->set('invalidate', 123);
+ $this->session->invalidate();
+ $this->assertEquals(array(), $this->session->all());
+ }
+
+ public function testMigrate()
+ {
+ $this->session->set('migrate', 321);
+ $this->session->migrate();
+ $this->assertEquals(321, $this->session->get('migrate'));
+ }
+
+ public function testMigrateDestroy()
+ {
+ $this->session->set('migrate', 333);
+ $this->session->migrate(true);
+ $this->assertEquals(333, $this->session->get('migrate'));
+ }
+
+ public function testSerialize()
+ {
+ $compare = serialize($this->storage);
+
+ $this->assertSame($compare, $this->session->serialize());
+
+ $this->session->unserialize($compare);
+
+ $_storage = new \ReflectionProperty(get_class($this->session), 'storage');
+ $_storage->setAccessible(true);
+
+ $this->assertEquals($_storage->getValue($this->session), $this->storage, 'storage match');
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testUnserializeException()
+ {
+ $serialized = serialize(new \ArrayObject());
+ $this->session->unserialize($serialized);
+ }
+
+ public function testGetId()
+ {
+ $this->assertEquals('', $this->session->getId());
+ $this->session->start();
+ $this->assertNotEquals('', $this->session->getId());
+ }
+}
diff --git a/tests/Symfony/Tests/Component/HttpFoundation/Session/Storage/AbstractSessionStorageTest.php b/tests/Symfony/Tests/Component/HttpFoundation/Session/Storage/AbstractSessionStorageTest.php
new file mode 100644
index 0000000000..05bc5fefb3
--- /dev/null
+++ b/tests/Symfony/Tests/Component/HttpFoundation/Session/Storage/AbstractSessionStorageTest.php
@@ -0,0 +1,127 @@
+
+ *
+ * These tests require separate processes.
+ *
+ * @runTestsInSeparateProcesses
+ */
+class AbstractSessionStorageTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @return AbstractSessionStorage
+ */
+ protected function getStorage()
+ {
+ $storage = new CustomHandlerSessionStorage();
+ $storage->registerBag(new AttributeBag);
+
+ return $storage;
+ }
+
+ public function testBag()
+ {
+ $storage = $this->getStorage();
+ $bag = new FlashBag();
+ $storage->registerBag($bag);
+ $this->assertSame($bag, $storage->getBag($bag->getName()));
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testRegisterBagException()
+ {
+ $storage = $this->getStorage();
+ $storage->getBag('non_existing');
+ }
+
+ public function testGetId()
+ {
+ $storage = $this->getStorage();
+ $this->assertEquals('', $storage->getId());
+ $storage->start();
+ $this->assertNotEquals('', $storage->getId());
+ }
+
+ public function testRegenerate()
+ {
+ $storage = $this->getStorage();
+ $storage->start();
+ $id = $storage->getId();
+ $storage->getBag('attributes')->set('lucky', 7);
+ $storage->regenerate();
+ $this->assertNotEquals($id, $storage->getId());
+ $this->assertEquals(7, $storage->getBag('attributes')->get('lucky'));
+
+ }
+
+ public function testRegenerateDestroy()
+ {
+ $storage = $this->getStorage();
+ $storage->start();
+ $id = $storage->getId();
+ $storage->getBag('attributes')->set('legs', 11);
+ $storage->regenerate(true);
+ $this->assertNotEquals($id, $storage->getId());
+ $this->assertEquals(11, $storage->getBag('attributes')->get('legs'));
+ }
+
+ public function testCustomSaveHandlers()
+ {
+ $storage = new CustomHandlerSessionStorage();
+ $this->assertEquals('user', ini_get('session.save_handler'));
+ }
+
+ public function testNativeSaveHandlers()
+ {
+ $storage = new ConcreteSessionStorage();
+ $this->assertNotEquals('user', ini_get('session.save_handler'));
+ }
+}
diff --git a/tests/Symfony/Tests/Component/HttpFoundation/Session/Storage/MockArraySessionStorageTest.php b/tests/Symfony/Tests/Component/HttpFoundation/Session/Storage/MockArraySessionStorageTest.php
new file mode 100644
index 0000000000..4611457940
--- /dev/null
+++ b/tests/Symfony/Tests/Component/HttpFoundation/Session/Storage/MockArraySessionStorageTest.php
@@ -0,0 +1,90 @@
+
+ */
+class MockArraySessionStorageTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @var MockArraySessionStorage
+ */
+ private $storage;
+
+ /**
+ * @var array
+ */
+ private $attributes;
+
+ /**
+ * @var array
+ */
+ private $flashes;
+
+ private $data;
+
+ protected function setUp()
+ {
+ $this->attributes = new AttributeBag();
+ $this->flashes = new FlashBag();
+
+ $this->data = array(
+ $this->attributes->getStorageKey() => array('foo' => 'bar'),
+ $this->flashes->getStorageKey() => array('notice' => 'hello'),
+ );
+
+ $this->storage = new MockArraySessionStorage();
+ $this->storage->registerBag($this->flashes);
+ $this->storage->registerBag($this->attributes);
+ $this->storage->setSessionData($this->data);
+ }
+
+ protected function tearDown()
+ {
+ $this->data = null;
+ $this->flashes = null;
+ $this->attributes = 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 testRegenerate()
+ {
+ $this->storage->start();
+ $id = $this->storage->getId();
+ $this->storage->regenerate();
+ $this->assertNotEquals($id, $this->storage->getId());
+ $this->assertEquals(array('foo' => 'bar'), $this->storage->getBag('attributes')->all());
+ $this->assertEquals(array('notice' => 'hello'), $this->storage->getBag('flashes')->peekAll());
+
+ $id = $this->storage->getId();
+ $this->storage->regenerate(true);
+ $this->assertNotEquals($id, $this->storage->getId());
+ $this->assertEquals(array('foo' => 'bar'), $this->storage->getBag('attributes')->all());
+ $this->assertEquals(array('notice' => 'hello'), $this->storage->getBag('flashes')->peekAll());
+ }
+
+ public function testGetId()
+ {
+ $this->assertEquals('', $this->storage->getId());
+ $this->storage->start();
+ $this->assertNotEquals('', $this->storage->getId());
+ }
+}
diff --git a/tests/Symfony/Tests/Component/HttpFoundation/Session/Storage/MockFileSessionStorageTest.php b/tests/Symfony/Tests/Component/HttpFoundation/Session/Storage/MockFileSessionStorageTest.php
new file mode 100644
index 0000000000..770bcda8f0
--- /dev/null
+++ b/tests/Symfony/Tests/Component/HttpFoundation/Session/Storage/MockFileSessionStorageTest.php
@@ -0,0 +1,105 @@
+
+ */
+class MockFileSessionStorageTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @var string
+ */
+ private $sessionDir;
+
+ /**
+ * @var FileMockSessionStorage
+ */
+ protected $storage;
+
+ protected function setUp()
+ {
+ $this->sessionDir = sys_get_temp_dir().'/sf2test';
+ $this->storage = $this->getStorage();
+ }
+
+ protected function tearDown()
+ {
+ $this->sessionDir = null;
+ $this->storage = null;
+ array_map('unlink', glob($this->sessionDir.'/*.session'));
+ if (is_dir($this->sessionDir)) {
+ rmdir($this->sessionDir);
+ }
+ }
+
+ public function testStart()
+ {
+ $this->assertEquals('', $this->storage->getId());
+ $this->assertTrue($this->storage->start());
+ $id = $this->storage->getId();
+ $this->assertNotEquals('', $this->storage->getId());
+ $this->assertTrue($this->storage->start());
+ $this->assertEquals($id, $this->storage->getId());
+ }
+
+ public function testRegenerate()
+ {
+ $this->storage->start();
+ $this->storage->getBag('attributes')->set('regenerate', 1234);
+ $this->storage->regenerate();
+ $this->assertEquals(1234, $this->storage->getBag('attributes')->get('regenerate'));
+ $this->storage->regenerate(true);
+ $this->assertEquals(1234, $this->storage->getBag('attributes')->get('regenerate'));
+ }
+
+ public function testGetId()
+ {
+ $this->assertEquals('', $this->storage->getId());
+ $this->storage->start();
+ $this->assertNotEquals('', $this->storage->getId());
+ }
+
+ public function testSave()
+ {
+ $this->storage->start();
+ $this->assertNotEquals('108', $this->storage->getBag('attributes')->get('new'));
+ $this->assertFalse($this->storage->getBag('flashes')->has('newkey'));
+ $this->storage->getBag('attributes')->set('new', '108');
+ $this->storage->getBag('flashes')->set('newkey', 'test');
+ $this->storage->save();
+
+ $storage = $this->getStorage();
+ $storage->start();
+ $this->assertEquals('108', $storage->getBag('attributes')->get('new'));
+ $this->assertTrue($storage->getBag('flashes')->has('newkey'));
+ $this->assertEquals('test', $storage->getBag('flashes')->peek('newkey'));
+ }
+
+ public function testMultipleInstances()
+ {
+ $storage1 = $this->getStorage();
+ $storage1->start();
+ $storage1->getBag('attributes')->set('foo', 'bar');
+ $storage1->save();
+
+ $storage2 = $this->getStorage();
+ $storage2->start();
+ $this->assertEquals('bar', $storage2->getBag('attributes')->get('foo'), 'values persist between instances');
+ }
+
+ private function getStorage(array $options = array())
+ {
+ $storage = new MockFileSessionStorage($this->sessionDir, $options);
+ $storage->registerBag(new FlashBag);
+ $storage->registerBag(new AttributeBag);
+
+ return $storage;
+ }
+}
diff --git a/tests/Symfony/Tests/Component/HttpFoundation/Session/Storage/NativeFileSessionStorageTest.php b/tests/Symfony/Tests/Component/HttpFoundation/Session/Storage/NativeFileSessionStorageTest.php
new file mode 100644
index 0000000000..df3ef95cbf
--- /dev/null
+++ b/tests/Symfony/Tests/Component/HttpFoundation/Session/Storage/NativeFileSessionStorageTest.php
@@ -0,0 +1,23 @@
+
+ *
+ * @runTestsInSeparateProcesses
+ */
+class NativeFileSessionStorageTest extends \PHPUnit_Framework_TestCase
+{
+ public function testSaveHandlers()
+ {
+ $storage = new NativeFileSessionStorage(sys_get_temp_dir(), array('name' => 'TESTING'));
+ $this->assertEquals('files', ini_get('session.save_handler'));
+ $this->assertEquals(sys_get_temp_dir(), ini_get('session.save_path'));
+ $this->assertEquals('TESTING', ini_get('session.name'));
+ }
+}
diff --git a/tests/Symfony/Tests/Component/HttpFoundation/Session/Storage/NativeMemcacheSessionStorageTest.php b/tests/Symfony/Tests/Component/HttpFoundation/Session/Storage/NativeMemcacheSessionStorageTest.php
new file mode 100644
index 0000000000..4274ed5bd1
--- /dev/null
+++ b/tests/Symfony/Tests/Component/HttpFoundation/Session/Storage/NativeMemcacheSessionStorageTest.php
@@ -0,0 +1,27 @@
+
+ *
+ * @runTestsInSeparateProcesses
+ */
+class NativeMemcacheSessionStorageTest extends \PHPUnit_Framework_TestCase
+{
+ public function testSaveHandlers()
+ {
+ if (!extension_loaded('memcache')) {
+ $this->markTestSkipped('Skipped tests SQLite extension is not present');
+ }
+
+ $storage = new NativeMemcacheSessionStorage('tcp://127.0.0.1:11211?persistent=0', array('name' => 'TESTING'));
+ $this->assertEquals('memcache', ini_get('session.save_handler'));
+ $this->assertEquals('tcp://127.0.0.1:11211?persistent=0', ini_get('session.save_path'));
+ $this->assertEquals('TESTING', ini_get('session.name'));
+ }
+}
diff --git a/tests/Symfony/Tests/Component/HttpFoundation/Session/Storage/NativeMemcachedSessionStorageTest.php b/tests/Symfony/Tests/Component/HttpFoundation/Session/Storage/NativeMemcachedSessionStorageTest.php
new file mode 100644
index 0000000000..c0a12aa2b4
--- /dev/null
+++ b/tests/Symfony/Tests/Component/HttpFoundation/Session/Storage/NativeMemcachedSessionStorageTest.php
@@ -0,0 +1,32 @@
+
+ *
+ * @runTestsInSeparateProcesses
+ */
+class NativeMemcachedSessionStorageTest extends \PHPUnit_Framework_TestCase
+{
+ public function testSaveHandlers()
+ {
+ if (!extension_loaded('memcached')) {
+ $this->markTestSkipped('Skipped tests SQLite extension is not present');
+ }
+
+ // test takes too long if memcached server is not running
+ ini_set('memcached.sess_locking', '0');
+
+ $storage = new NativeMemcachedSessionStorage('127.0.0.1:11211', array('name' => 'TESTING'));
+
+ $this->assertEquals('memcached', ini_get('session.save_handler'));
+ $this->assertEquals('127.0.0.1:11211', ini_get('session.save_path'));
+ $this->assertEquals('TESTING', ini_get('session.name'));
+ }
+}
+
diff --git a/tests/Symfony/Tests/Component/HttpFoundation/Session/Storage/NativeSqliteSessionStorageTest.php b/tests/Symfony/Tests/Component/HttpFoundation/Session/Storage/NativeSqliteSessionStorageTest.php
new file mode 100644
index 0000000000..b1700326b1
--- /dev/null
+++ b/tests/Symfony/Tests/Component/HttpFoundation/Session/Storage/NativeSqliteSessionStorageTest.php
@@ -0,0 +1,28 @@
+
+ *
+ * @runTestsInSeparateProcesses
+ */
+class NativeSqliteSessionStorageTest extends \PHPUnit_Framework_TestCase
+{
+ public function testSaveHandlers()
+ {
+ if (!extension_loaded('sqlite')) {
+ $this->markTestSkipped('Skipped tests SQLite extension is not present');
+ }
+
+ $storage = new NativeSqliteSessionStorage(sys_get_temp_dir().'/sqlite.db', array('name' => 'TESTING'));
+ $this->assertEquals('sqlite', ini_get('session.save_handler'));
+ $this->assertEquals(sys_get_temp_dir().'/sqlite.db', ini_get('session.save_path'));
+ $this->assertEquals('TESTING', ini_get('session.name'));
+ }
+}
+
diff --git a/tests/Symfony/Tests/Component/HttpFoundation/Session/Storage/NullSessionStorageTest.php b/tests/Symfony/Tests/Component/HttpFoundation/Session/Storage/NullSessionStorageTest.php
new file mode 100644
index 0000000000..66599f68b3
--- /dev/null
+++ b/tests/Symfony/Tests/Component/HttpFoundation/Session/Storage/NullSessionStorageTest.php
@@ -0,0 +1,42 @@
+
+ *
+ * @runTestsInSeparateProcesses
+ */
+class NullSessionStorageTest extends \PHPUnit_Framework_TestCase
+{
+ public function testSaveHandlers()
+ {
+ $storage = new NullSessionStorage();
+ $this->assertEquals('user', ini_get('session.save_handler'));
+ }
+
+ public function testSession()
+ {
+ session_id('nullsessionstorage');
+ $storage = new NullSessionStorage();
+ $session = new Session($storage);
+ $this->assertNull($session->get('something'));
+ $session->set('something', 'unique');
+ $this->assertEquals('unique', $session->get('something'));
+ }
+
+ public function testNothingIsPersisted()
+ {
+ session_id('nullsessionstorage');
+ $storage = new NullSessionStorage();
+ $session = new Session($storage);
+ $session->start();
+ $this->assertEquals('nullsessionstorage', $session->getId());
+ $this->assertNull($session->get('something'));
+ }
+}
+
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 060cb0e913..0000000000
--- 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
deleted file mode 100644
index 8318101e66..0000000000
--- a/tests/Symfony/Tests/Component/HttpFoundation/SessionTest.php
+++ /dev/null
@@ -1,232 +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;
-
-use Symfony\Component\HttpFoundation\Session;
-use Symfony\Component\HttpFoundation\SessionStorage\ArraySessionStorage;
-
-/**
- * SessionTest
- *
- * @author Fabien Potencier
- * @author Robert Schönthal
- */
-class SessionTest extends \PHPUnit_Framework_TestCase
-{
- protected $storage;
- protected $session;
-
- public function setUp()
- {
- $this->storage = new ArraySessionStorage();
- $this->session = $this->getSession();
- }
-
- protected function tearDown()
- {
- $this->storage = null;
- $this->session = null;
- }
-
- public function testFlash()
- {
- $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());
- }
-
- public function testFlashesAreFlushedWhenNeeded()
- {
- $this->session->setFlash('foo', 'bar');
- $this->session->save();
-
- $this->session = $this->getSession();
- $this->assertTrue($this->session->hasFlash('foo'));
- $this->session->save();
-
- $this->session = $this->getSession();
- $this->assertFalse($this->session->hasFlash('foo'));
- }
-
- public function testAll()
- {
- $this->assertFalse($this->session->has('foo'));
- $this->assertNull($this->session->get('foo'));
-
- $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();
-
- $this->session->replace($attrs);
-
- $this->assertSame($attrs, $this->session->all());
-
- $this->session->clear();
-
- $this->assertSame(array(), $this->session->all());
- }
-
- public function testMigrateAndInvalidate()
- {
- $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();
-
- $this->assertSame('bar', $this->session->get('foo'));
- $this->assertSame('bar', $this->session->getFlash('foo'));
-
- $this->session = $this->getSession();
- $this->session->invalidate();
-
- $this->assertSame(array(), $this->session->all());
- $this->assertSame(array(), $this->session->getFlashes());
- }
-
- public function testSerialize()
- {
- $this->session = new Session($this->storage);
-
- $compare = serialize($this->storage);
-
- $this->assertSame($compare, $this->session->serialize());
-
- $this->session->unserialize($compare);
-
- $_storage = new \ReflectionProperty(get_class($this->session), 'storage');
- $_storage->setAccessible(true);
-
- $this->assertEquals($_storage->getValue($this->session), $this->storage, 'storage match');
- }
-
- public function testSave()
- {
- $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);
- }
-
- public function testGetId()
- {
- $this->assertNull($this->session->getId());
- }
-
- public function testStart()
- {
- $this->session->start();
-
- $this->assertSame(array(), $this->session->getFlashes());
- $this->assertSame(array(), $this->session->all());
- }
-
- public function testSavedOnDestruct()
- {
- $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'));
- }
-
- protected function getSession()
- {
- return new Session($this->storage);
- }
-}
diff --git a/tests/Symfony/Tests/Component/HttpKernel/EventListener/LocaleListenerTest.php b/tests/Symfony/Tests/Component/HttpKernel/EventListener/LocaleListenerTest.php
index cf12ec79fc..ecce76ec5e 100644
--- a/tests/Symfony/Tests/Component/HttpKernel/EventListener/LocaleListenerTest.php
+++ b/tests/Symfony/Tests/Component/HttpKernel/EventListener/LocaleListenerTest.php
@@ -33,7 +33,7 @@ class LocaleListenerTest extends \PHPUnit_Framework_TestCase
session_name('foo');
$request->cookies->set('foo', 'value');
- $session = $this->getMock('Symfony\Component\HttpFoundation\Session', array('get'), array(), '', false);
+ $session = $this->getMock('Symfony\Component\HttpFoundation\Session\Session', array('get'), array(), '', false);
$session->expects($this->once())->method('get')->will($this->returnValue('es'));
$request->setSession($session);
@@ -55,7 +55,7 @@ class LocaleListenerTest extends \PHPUnit_Framework_TestCase
$event = $this->getEvent($request);
// also updates the session _locale value
- $session = $this->getMock('Symfony\Component\HttpFoundation\Session', array('set', 'get'), array(), '', false);
+ $session = $this->getMock('Symfony\Component\HttpFoundation\Session\Session', array('set', 'get'), array(), '', false);
$session->expects($this->once())->method('set')->with('_locale', 'es');
$session->expects($this->once())->method('get')->with('_locale')->will($this->returnValue('es'));
$request->setSession($session);
diff --git a/tests/Symfony/Tests/Component/Security/Http/Firewall/ContextListenerTest.php b/tests/Symfony/Tests/Component/Security/Http/Firewall/ContextListenerTest.php
index dfbcd79e7f..5c810062ef 100644
--- a/tests/Symfony/Tests/Component/Security/Http/Firewall/ContextListenerTest.php
+++ b/tests/Symfony/Tests/Component/Security/Http/Firewall/ContextListenerTest.php
@@ -4,8 +4,8 @@ namespace Symfony\Test\Component\Security\Http\Firewall;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\HttpFoundation\Session;
-use Symfony\Component\HttpFoundation\SessionStorage\ArraySessionStorage;
+use Symfony\Component\HttpFoundation\Session\Session;
+use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
@@ -63,7 +63,7 @@ class ContextListenerTest extends \PHPUnit_Framework_TestCase
protected function runSessionOnKernelResponse($newToken, $original = null)
{
- $session = new Session(new ArraySessionStorage());
+ $session = new Session(new MockArraySessionStorage());
if ($original !== null) {
$session->set('_security_session', $original);
diff --git a/tests/Symfony/Tests/Component/Security/Http/Logout/SessionLogoutHandlerTest.php b/tests/Symfony/Tests/Component/Security/Http/Logout/SessionLogoutHandlerTest.php
index 05df6cf8f5..9f38e4c9c1 100644
--- a/tests/Symfony/Tests/Component/Security/Http/Logout/SessionLogoutHandlerTest.php
+++ b/tests/Symfony/Tests/Component/Security/Http/Logout/SessionLogoutHandlerTest.php
@@ -22,7 +22,7 @@ class SessionLogoutHandlerTest extends \PHPUnit_Framework_TestCase
$request = $this->getMock('Symfony\Component\HttpFoundation\Request');
$response = new Response();
- $session = $this->getMock('Symfony\Component\HttpFoundation\Session', array(), array(), '', false);
+ $session = $this->getMock('Symfony\Component\HttpFoundation\Session\Session', array(), array(), '', false);
$request
->expects($this->once())