Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

[2.3] Handle PHP sessions started outside of Symfony #7571

Closed
wants to merge 5 commits into from

8 participants

Drak Bilal Amarni Stan Lemon Fabien Potencier Pascal Borreli Markus Staab Christophe Coevoet Ben Davies
Drak
Q A
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets
License MIT
Doc PR symfony/symfony-docs#2474

This PR brings a way to allow Symfony2 to manage a session started outside of Symfony in such a way that quite explicit. It also introduces more robust detection of previously started sessions under PHP 5.3 and supports real session status detection under PHP 5.4

...tpFoundation/Session/Storage/NativeSessionStorage.php
@@ -130,27 +132,26 @@ public function start()
return true;
}
- // catch condition where session was started automatically by PHP
- if (!$this->started && !$this->closed && $this->saveHandler->isActive()
- && $this->saveHandler->isSessionHandlerInterface()) {
- $this->loadSession();
-
- return true;
+ if (version_compare(phpversion(), '5.4.0', '>=') && session_status() === \PHP_SESSION_ACTIVE) {
+ throw new \RuntimeException('Failed to start the session: already started by PHP');
+ } elseif (version_compare(phpversion(), '5.4.0', '<') && isset($_SESSION) && session_id()) {

not need to do version_compare twice?

Drak
drak added a note

My logic is the second check should not be made at all if the first fails due to session not started. PHP version is required here because the first if is precise under PHP 5.4.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...ttpFoundation/Session/Storage/Proxy/AbstractProxy.php
@@ -72,16 +72,31 @@ public function isWrapper()
*/
public function isActive()
{
+ if (version_compare(phpversion(), '5.4.0', '>=')) {
+ return $this->active = session_status() === \PHP_SESSION_ACTIVE ? true : false;

? true : false is redundant

Drak
drak added a note

fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...ation/Tests/Session/Storage/PhpSessionStorageTest.php
((67 lines not shown))
+ {
+ if (version_compare(phpversion(), '5.4.0', '<')) {
+ $this->markTestSkipped('Test skipped, for PHP 5.4 only.');
+ }
+
+ $storage = $this->getStorage();
+
+ $this->assertFalse(isset($_SESSION));
+ $this->assertFalse($storage->getSaveHandler()->isActive());
+ $this->assertFalse($storage->isStarted());
+
+ session_start();
+ $this->assertTrue(isset($_SESSION));
+ // in PHP 5.4 we can reliably detect a session started
+ $this->assertTrue($storage->getSaveHandler()->isActive());
+ // PHP sesion might have started, but the storage driver has not, so false is correct here
Pascal Borreli
pborreli added a note

typo sesion => session

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Drak drak [HttpFoundation][Session] Better detection of existing sessions.
Extended and improved tests, and introduced a way to work with applications
that start the PHP session using session_start()
9bf391f
Bilal Amarni

great feature!

1) Is the metadatabag appropriate for the PhpSessionStorage? As the session may be created and updated outside Symfony, information contained in it might be wrong.

2) Imo the clear() method should be overrided in PhpSessionStorage, and it should only clear keys managed by Symfony instead of clearing the whole $_SESSION global, as Symfony can't work with what has been created out of the box (it has its own namespace), why should it remove it? To me this feature is only about sharing the storage driver, not the data.

Drak

@bamarni - point 1 - the purpose of this is to allow Symfony to interface with a system where you cannot avoid it starting the session. This allows legacy code to do it's thing unhindered and for Symfony do manage it's stuff. The chance of a key collision is of course possible though remote, but the chance of the legacy code to overwrite the entire $_SESSION is always there - that's why I've been so vehemently opposed to doing anything like this (there are many other things that can go wrong too). But doing things this way is explicit, so I feel it gives the programmer plenty awareness of what they are doing. Together with the documentation, I think it's usecase will be clear.

As for 2, that was well spotted. I will make the change now.

...tpFoundation/Session/Storage/NativeSessionStorage.php
((18 lines not shown))
*/
public function setSaveHandler($saveHandler = null)
{
- // Wrap $saveHandler in proxy
+ if (!$saveHandler instanceof AbstractProxy &&
+ !$saveHandler instanceof NativeSessionHandler &&
+ !$saveHandler instanceof \SessionHandlerInterface &&
+ $saveHandler !== null) {

symfony standard is usually the reverse of this?
null !== $saveHandler

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Stan Lemon

This would be a great addition, it would make my life introducing Symfony into legacy code a lot easier.

...tpFoundation/Session/Storage/NativeSessionStorage.php
@@ -130,27 +132,26 @@ public function start()
return true;
}
- // catch condition where session was started automatically by PHP
- if (!$this->started && !$this->closed && $this->saveHandler->isActive()
- && $this->saveHandler->isSessionHandlerInterface()) {
- $this->loadSession();
-
- return true;
+ if (version_compare(phpversion(), '5.4.0', '>=') && \PHP_SESSION_ACTIVE === session_status()) {
+ throw new \RuntimeException('Failed to start the session: already started by PHP');
+ } elseif (version_compare(phpversion(), '5.4.0', '<') && isset($_SESSION) && session_id()) {
Christophe Coevoet Collaborator
stof added a note

This can be if instead of elseif as the previous if block throws an exception

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...tpFoundation/Session/Storage/NativeSessionStorage.php
((24 lines not shown))
if (!session_start()) {
throw new \RuntimeException('Failed to start the session');
}
$this->loadSession();
-
- if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) {
- $this->saveHandler->setActive(false);
+ if (!$this->saveHandler->isWrapper() && !$this->getSaveHandler()->isSessionHandlerInterface()) {
Christophe Coevoet Collaborator
stof added a note

why changing it to use the getter the second time ?

Drak
drak added a note

Man you have good eyes :) fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Drak

@fabpot - is this PR acceptable now before I start on the docs?

...tpFoundation/Session/Storage/NativeSessionStorage.php
@@ -130,27 +132,26 @@ public function start()
return true;
}
- // catch condition where session was started automatically by PHP
- if (!$this->started && !$this->closed && $this->saveHandler->isActive()
- && $this->saveHandler->isSessionHandlerInterface()) {
- $this->loadSession();
-
- return true;
+ if (version_compare(phpversion(), '5.4.0', '>=') && \PHP_SESSION_ACTIVE === session_status()) {
+ throw new \RuntimeException('Failed to start the session: already started by PHP');
+ } elseif (version_compare(phpversion(), '5.4.0', '<') && isset($_SESSION) && session_id()) {
+ // not 100% fool-proof, but is the most reliable way to determine if a session is active in PHP 5.3
+ throw new \RuntimeException('Failed to start the session: already started by PHP ($_SESSION is set)');
Fabien Potencier Owner
fabpot added a note

All exception messages must end with a dot.

Drak
drak added a note

Fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Drak drak referenced this pull request in symfony/symfony-docs
Merged

[WCM] Document PhpSessionStorage #2474

Drak

Updated with docs PR.

.../HttpFoundation/Session/Storage/PhpSessionStorage.php
((31 lines not shown))
+ {
+ $this->setMetadataBag($metaBag);
+ $this->setSaveHandler($handler);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function start()
+ {
+ if ($this->started && !$this->closed) {
+ return true;
+ }
+
+ $this->loadSession();
+ if (!$this->saveHandler->isWrapper() && !$this->getSaveHandler()->isSessionHandlerInterface()) {
Markus Staab
staabm added a note

Here also getter at 2nd condition

Drak
drak added a note

Fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
.../HttpFoundation/Session/Storage/PhpSessionStorage.php
((11 lines not shown))
+
+namespace Symfony\Component\HttpFoundation\Session\Storage;
+
+use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag;
+
+/**
+ * Allows session to be started by PHP and managed by Symfony2
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class PhpSessionStorage extends NativeSessionStorage
+{
+ /**
+ * Constructor.
+ *
+ * @param object|null $handler Must be instance of AbstractProxy or NativeSessionHandler;
Markus Staab
staabm added a note

Phpdoc allows those type information also here @param AbstractProxy|NativeSessionHandler|SessionHandlerInterface|null

Drak
drak added a note

It looks really unweildly like that:

    /**
     * Constructor.
     *
     * @param AbstractProxy|NativeSessionHandler|\SessionHandlerInterface|null $handler
     * @param MetadataBag                                                      $metaBag MetadataBag.
     */
    public function __construct($handler = null, MetadataBag $metaBag = null)
Fabien Potencier Owner
fabpot added a note

Looks good to me.

Drak
drak added a note

OK, done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Markus Staab staabm commented on the diff
...on/Tests/Session/Storage/NativeSessionStorageTest.php
((88 lines not shown))
+ /**
+ * @expectedException \RuntimeException
+ */
+ public function testCanStartOutside54()
+ {
+ if (version_compare(phpversion(), '5.4.0', '<')) {
+ $this->markTestSkipped('Test skipped, for PHP 5.4 only.');
+ }
+
+ $storage = $this->getStorage();
+
+ $this->assertFalse(isset($_SESSION));
+ $this->assertFalse($storage->getSaveHandler()->isActive());
+ $this->assertFalse($storage->isStarted());
+
+ session_start();
Markus Staab
staabm added a note

Should the session be destroyed so no sideffect are left behind by this test

Drak
drak added a note

Yes. I had meant to do that but forgot. Adding now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...ation/Tests/Session/Storage/PhpSessionStorageTest.php
((15 lines not shown))
+use Symfony\Component\HttpFoundation\Session\Storage\PhpSessionStorage;
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
+
+/**
+ * Test class for PhpSessionStorage.
+ *
+ * @author Drak <drak@zikula.org>
+ *
+ * These tests require separate processes.
+ *
+ * @runTestsInSeparateProcesses
+ */
+class PhpSessionStorageTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @param array $options
Markus Staab
staabm added a note

There is no param

Drak
drak added a note

Fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Fabien Potencier fabpot referenced this pull request
Closed

Update session start check #7216

...tpFoundation/Session/Storage/NativeSessionStorage.php
@@ -130,27 +131,28 @@ public function start()
return true;
}
- // catch condition where session was started automatically by PHP
- if (!$this->started && !$this->closed && $this->saveHandler->isActive()
- && $this->saveHandler->isSessionHandlerInterface()) {
- $this->loadSession();
+ if (version_compare(phpversion(), '5.4.0', '>=') && \PHP_SESSION_ACTIVE === session_status()) {
+ throw new \RuntimeException('Failed to start the session: already started by PHP');
Fabien Potencier Owner
fabpot added a note

There is some missing dots at the end of exception messages (and you removed some as well).

Drak
drak added a note

The CS is mainly sorted out in the other PR. I'll fix this particular one though.

Drak
drak added a note

Fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Fabien Potencier fabpot referenced this pull request from a commit
Fabien Potencier fabpot merged branch drak/session_detect (PR #7571)
This PR was squashed before being merged into the master branch (closes #7571).

Discussion
----------

[2.3] Handle PHP sessions started outside of Symfony

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets |
| License       | MIT
| Doc PR        | symfony/symfony-docs#2474

This PR brings a way to allow Symfony2 to manage a session started outside of Symfony in such a way that quite explicit. It also introduces more robust detection of previously started sessions under PHP 5.3 and supports real session status detection under PHP 5.4

Commits
-------

df99902 [2.3] Handle PHP sessions started outside of Symfony
7bf8933
Fabien Potencier fabpot closed this pull request from a commit
Fabien Potencier fabpot merged branch drak/session_detect (PR #7571)
This PR was squashed before being merged into the master branch (closes #7571).

Discussion
----------

[2.3] Handle PHP sessions started outside of Symfony

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets |
| License       | MIT
| Doc PR        | symfony/symfony-docs#2474

This PR brings a way to allow Symfony2 to manage a session started outside of Symfony in such a way that quite explicit. It also introduces more robust detection of previously started sessions under PHP 5.3 and supports real session status detection under PHP 5.4

Commits
-------

df99902 [2.3] Handle PHP sessions started outside of Symfony
7bf8933
Fabien Potencier fabpot closed this in 7bf8933
Daniel Platt hackzilla referenced this pull request from a commit
Fabien Potencier fabpot merged branch drak/session_detect (PR #7571)
This PR was squashed before being merged into the master branch (closes #7571).

Discussion
----------

[2.3] Handle PHP sessions started outside of Symfony

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets |
| License       | MIT
| Doc PR        | symfony/symfony-docs#2474

This PR brings a way to allow Symfony2 to manage a session started outside of Symfony in such a way that quite explicit. It also introduces more robust detection of previously started sessions under PHP 5.3 and supports real session status detection under PHP 5.4

Commits
-------

df99902 [2.3] Handle PHP sessions started outside of Symfony
57eacea
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 5, 2013
  1. Drak

    [HttpFoundation][Session] Better detection of existing sessions.

    drak authored
    Extended and improved tests, and introduced a way to work with applications
    that start the PHP session using session_start()
  2. Drak
Commits on Apr 11, 2013
  1. Drak

    Fix coding standards

    drak authored
  2. Drak
Commits on Apr 13, 2013
  1. Drak
This page is out of date. Refresh to see the latest.
2  src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
View
@@ -318,6 +318,7 @@ private function registerSessionConfiguration(array $config, ContainerBuilder $c
if (null == $config['handler_id']) {
// Set the handler class to be null
$container->getDefinition('session.storage.native')->replaceArgument(1, null);
+ $container->getDefinition('session.storage.php_bridge')->replaceArgument(1, null);
} else {
$container->setAlias('session.handler', $config['handler_id']);
}
@@ -327,6 +328,7 @@ private function registerSessionConfiguration(array $config, ContainerBuilder $c
$this->addClassesToCompile(array(
'Symfony\\Bundle\\FrameworkBundle\\EventListener\\SessionListener',
'Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage',
+ 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\PhpBridgeSessionStorage',
'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NativeFileSessionHandler',
'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\AbstractProxy',
'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\SessionHandlerProxy',
5 src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml
View
@@ -9,6 +9,7 @@
<parameter key="session.flashbag.class">Symfony\Component\HttpFoundation\Session\Flash\FlashBag</parameter>
<parameter key="session.attribute_bag.class">Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag</parameter>
<parameter key="session.storage.native.class">Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage</parameter>
+ <parameter key="session.storage.php_bridge.class">Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage</parameter>
<parameter key="session.storage.mock_file.class">Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage</parameter>
<parameter key="session.handler.native_file.class">Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler</parameter>
<parameter key="session_listener.class">Symfony\Bundle\FrameworkBundle\EventListener\SessionListener</parameter>
@@ -26,6 +27,10 @@
<argument type="service" id="session.handler" />
</service>
+ <service id="session.storage.php_bridge" class="%session.storage.php_bridge.class%">
+ <argument type="service" id="session.handler" />
+ </service>
+
<service id="session.flash_bag" class="%session.flashbag.class%" public="false" />
<service id="session.attribute_bag" class="%session.attribute_bag.class%" public="false" />
51 src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php
View
@@ -12,6 +12,7 @@
namespace Symfony\Component\HttpFoundation\Session\Storage;
use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
@@ -91,9 +92,9 @@ class NativeSessionStorage implements SessionStorageInterface
* upload_progress.min-freq, "1"
* url_rewriter.tags, "a=href,area=href,frame=src,form=,fieldset="
*
- * @param array $options Session configuration options.
- * @param object $handler SessionHandlerInterface.
- * @param MetadataBag $metaBag MetadataBag.
+ * @param array $options Session configuration options.
+ * @param AbstractProxy|NativeSessionHandler|\SessionHandlerInterface|null $handler
+ * @param MetadataBag $metaBag MetadataBag.
*/
public function __construct(array $options = array(), $handler = null, MetadataBag $metaBag = null)
{
@@ -130,27 +131,28 @@ public function start()
return true;
}
- // catch condition where session was started automatically by PHP
- if (!$this->started && !$this->closed && $this->saveHandler->isActive()
- && $this->saveHandler->isSessionHandlerInterface()) {
- $this->loadSession();
+ if (version_compare(phpversion(), '5.4.0', '>=') && \PHP_SESSION_ACTIVE === session_status()) {
+ throw new \RuntimeException('Failed to start the session: already started by PHP.');
+ }
- return true;
+ if (version_compare(phpversion(), '5.4.0', '<') && isset($_SESSION) && session_id()) {
+ // not 100% fool-proof, but is the most reliable way to determine if a session is active in PHP 5.3
+ throw new \RuntimeException('Failed to start the session: already started by PHP ($_SESSION is set).');
}
if (ini_get('session.use_cookies') && headers_sent()) {
throw new \RuntimeException('Failed to start the session because headers have already been sent.');
}
- // start the session
+ // ok to try and start the session
if (!session_start()) {
throw new \RuntimeException('Failed to start the session');
}
$this->loadSession();
-
if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) {
- $this->saveHandler->setActive(false);
+ // This condition matches only PHP 5.3 with internal save handlers
+ $this->saveHandler->setActive(true);
}
return true;
@@ -215,7 +217,8 @@ public function save()
{
session_write_close();
- if (!$this->saveHandler->isWrapper() && !$this->getSaveHandler()->isSessionHandlerInterface()) {
+ if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) {
+ // This condition matches only PHP 5.3 with internal save handlers
$this->saveHandler->setActive(false);
}
@@ -329,7 +332,7 @@ public function setOptions(array $options)
}
/**
- * Registers save handler as a PHP session handler.
+ * Registers session save handler as a PHP session handler.
*
* To use internal PHP session save handlers, override this method using ini_set with
* session.save_handler and session.save_path e.g.
@@ -337,21 +340,35 @@ public function setOptions(array $options)
* ini_set('session.save_handler', 'files');
* ini_set('session.save_path', /tmp');
*
+ * or pass in a NativeSessionHandler instance which configures session.save_handler in the
+ * constructor, for a template see NativeFileSessionHandler or use handlers in
+ * composer package drak/native-session
+ *
* @see http://php.net/session-set-save-handler
* @see http://php.net/sessionhandlerinterface
* @see http://php.net/sessionhandler
+ * @see http://github.com/drak/NativeSession
+ *
+ * @param AbstractProxy|NativeSessionHandler|\SessionHandlerInterface|null $saveHandler
*
- * @param object $saveHandler Default null means NativeProxy.
+ * @throws \InvalidArgumentException
*/
public function setSaveHandler($saveHandler = null)
{
- // Wrap $saveHandler in proxy
+ if (!$saveHandler instanceof AbstractProxy &&
+ !$saveHandler instanceof NativeSessionHandler &&
+ !$saveHandler instanceof \SessionHandlerInterface &&
+ null !== $saveHandler) {
+ throw new \InvalidArgumentException('Must be instance of AbstractProxy or NativeSessionHandler; implement \SessionHandlerInterface; or be null.');
+ }
+
+ // Wrap $saveHandler in proxy and prevent double wrapping of proxy
if (!$saveHandler instanceof AbstractProxy && $saveHandler instanceof \SessionHandlerInterface) {
$saveHandler = new SessionHandlerProxy($saveHandler);
} elseif (!$saveHandler instanceof AbstractProxy) {
- $saveHandler = new NativeProxy();
+ $saveHandler = version_compare(phpversion(), '5.4.0', '>=') ?
+ new SessionHandlerProxy(new \SessionHandler()) : new NativeProxy();
}
-
$this->saveHandler = $saveHandler;
if ($this->saveHandler instanceof \SessionHandlerInterface) {
69 src/Symfony/Component/HttpFoundation/Session/Storage/PhpBridgeSessionStorage.php
View
@@ -0,0 +1,69 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * 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\Storage\MetadataBag;
+use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler;
+
+/**
+ * Allows session to be started by PHP and managed by Symfony2
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class PhpBridgeSessionStorage extends NativeSessionStorage
+{
+ /**
+ * Constructor.
+ *
+ * @param AbstractProxy|NativeSessionHandler|\SessionHandlerInterface|null $handler
+ * @param MetadataBag $metaBag MetadataBag
+ */
+ public function __construct($handler = null, MetadataBag $metaBag = null)
+ {
+ $this->setMetadataBag($metaBag);
+ $this->setSaveHandler($handler);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function start()
+ {
+ if ($this->started && !$this->closed) {
+ return true;
+ }
+
+ $this->loadSession();
+ if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) {
+ // This condition matches only PHP 5.3 + internal save handlers
+ $this->saveHandler->setActive(true);
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ // clear out the bags and nothing else that may be set
+ // since the purpose of this driver is to share a handler
+ foreach ($this->bags as $bag) {
+ $bag->clear();
+ }
+
+ // reconnect the bags to the session
+ $this->loadSession();
+ }
+}
15 src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/AbstractProxy.php
View
@@ -72,16 +72,31 @@ public function isWrapper()
*/
public function isActive()
{
+ if (version_compare(phpversion(), '5.4.0', '>=')) {
+ return $this->active = \PHP_SESSION_ACTIVE === session_status();
+ }
+
return $this->active;
}
/**
* Sets the active flag.
*
+ * Has no effect under PHP 5.4+ as status is detected
+ * automatically in isActive()
+ *
+ * @internal
+ *
* @param Boolean $flag
+ *
+ * @throws \LogicException
*/
public function setActive($flag)
{
+ if (version_compare(phpversion(), '5.4.0', '>=')) {
+ throw new \LogicException('This method is disabled in PHP 5.4.0+');
+ }
+
$this->active = (bool) $flag;
}
143 src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php
View
@@ -11,11 +11,15 @@
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
+use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
+use Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy;
+use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
/**
* Test class for NativeSessionStorage.
@@ -28,6 +32,28 @@
*/
class NativeSessionStorageTest extends \PHPUnit_Framework_TestCase
{
+ private $savePath;
+
+ protected function setUp()
+ {
+ ini_set('session.save_handler', 'files');
+ ini_set('session.save_path', $this->savePath = sys_get_temp_dir().'/sf2test');
+ if (!is_dir($this->savePath)) {
+ mkdir($this->savePath);
+ }
+ }
+
+ protected function tearDown()
+ {
+ session_write_close();
+ array_map('unlink', glob($this->savePath.'/*'));
+ if (is_dir($this->savePath)) {
+ rmdir($this->savePath);
+ }
+
+ $this->savePath = null;
+ }
+
/**
* @param array $options
*
@@ -75,7 +101,6 @@ public function testRegenerate()
$storage->regenerate();
$this->assertNotEquals($id, $storage->getId());
$this->assertEquals(7, $storage->getBag('attributes')->get('lucky'));
-
}
public function testRegenerateDestroy()
@@ -126,32 +151,132 @@ public function testCookieOptions()
$this->assertEquals($options, $gco);
}
- public function testSetSaveHandler()
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testSetSaveHandlerException()
{
$storage = $this->getStorage();
- $storage->setSaveHandler(new \StdClass());
- $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy', $storage->getSaveHandler());
+ $storage->setSaveHandler(new \StdClass);
}
- public function testSetSaveHandlerPHP53()
+ public function testSetSaveHandler53()
{
if (version_compare(phpversion(), '5.4.0', '>=')) {
$this->markTestSkipped('Test skipped, for PHP 5.3 only.');
}
+ ini_set('session.save_handler', 'files');
$storage = $this->getStorage();
- $storage->setSaveHandler(new NativeFileSessionHandler());
+ $storage->setSaveHandler();
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy', $storage->getSaveHandler());
+ $storage->setSaveHandler(null);
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy', $storage->getSaveHandler());
+ $storage->setSaveHandler(new NativeSessionHandler());
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy', $storage->getSaveHandler());
+ $storage->setSaveHandler(new SessionHandlerProxy(new SessionHandler()));
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
+ $storage->setSaveHandler(new SessionHandler());
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
+ $storage->setSaveHandler(new NativeProxy());
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy', $storage->getSaveHandler());
}
- public function testSetSaveHandlerPHP54()
+ public function testSetSaveHandler54()
{
if (version_compare(phpversion(), '5.4.0', '<')) {
- $this->markTestSkipped('Test skipped, for PHP 5.4+ only.');
+ $this->markTestSkipped('Test skipped, for PHP 5.4 only.');
}
+ ini_set('session.save_handler', 'files');
$storage = $this->getStorage();
- $storage->setSaveHandler(new NullSessionHandler());
+ $storage->setSaveHandler();
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
+ $storage->setSaveHandler(null);
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
+ $storage->setSaveHandler(new SessionHandlerProxy(new NativeSessionHandler()));
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
+ $storage->setSaveHandler(new NativeSessionHandler());
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
+ $storage->setSaveHandler(new SessionHandlerProxy(new SessionHandler()));
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
+ $storage->setSaveHandler(new SessionHandler());
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
+ }
+
+ /**
+ * @expectedException \RuntimeException
+ */
+ public function testStartedOutside53()
+ {
+ if (version_compare(phpversion(), '5.4.0', '>=')) {
+ $this->markTestSkipped('Test skipped, for PHP 5.3 only.');
+ }
+
+ $storage = $this->getStorage();
+
+ $this->assertFalse(isset($_SESSION));
+
+ session_start();
+ $this->assertTrue(isset($_SESSION));
+ // PHP session might have started, but the storage driver has not, so false is correct here
+ $this->assertFalse($storage->isStarted());
+
+ $key = $storage->getMetadataBag()->getStorageKey();
+ $this->assertFalse(isset($_SESSION[$key]));
+ $storage->start();
+ }
+
+ /**
+ * @expectedException \RuntimeException
+ */
+ public function testCanStartOutside54()
+ {
+ if (version_compare(phpversion(), '5.4.0', '<')) {
+ $this->markTestSkipped('Test skipped, for PHP 5.4 only.');
+ }
+
+ $storage = $this->getStorage();
+
+ $this->assertFalse(isset($_SESSION));
+ $this->assertFalse($storage->getSaveHandler()->isActive());
+ $this->assertFalse($storage->isStarted());
+
+ session_start();
Markus Staab
staabm added a note

Should the session be destroyed so no sideffect are left behind by this test

Drak
drak added a note

Yes. I had meant to do that but forgot. Adding now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ $this->assertTrue(isset($_SESSION));
+ $this->assertTrue($storage->getSaveHandler()->isActive());
+ // PHP session might have started, but the storage driver has not, so false is correct here
+ $this->assertFalse($storage->isStarted());
+
+ $key = $storage->getMetadataBag()->getStorageKey();
+ $this->assertFalse(isset($_SESSION[$key]));
+ $storage->start();
+ }
+}
+
+class SessionHandler implements \SessionHandlerInterface
+{
+ public function open($savePath, $sessionName)
+ {
+ }
+
+ public function close()
+ {
+ }
+
+ public function read($id)
+ {
+ }
+
+ public function write($id, $data)
+ {
+ }
+
+ public function destroy($id)
+ {
+ }
+
+ public function gc($maxlifetime)
+ {
}
}
124 src/Symfony/Component/HttpFoundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php
View
@@ -0,0 +1,124 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Storage;
+
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler;
+use Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage;
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
+
+/**
+ * Test class for PhpSessionStorage.
+ *
+ * @author Drak <drak@zikula.org>
+ *
+ * These tests require separate processes.
+ *
+ * @runTestsInSeparateProcesses
+ */
+class PhpSessionStorageTest extends \PHPUnit_Framework_TestCase
+{
+ private $savePath;
+
+ protected function setUp()
+ {
+ ini_set('session.save_handler', 'files');
+ ini_set('session.save_path', $this->savePath = sys_get_temp_dir().'/sf2test');
+ if (!is_dir($this->savePath)) {
+ mkdir($this->savePath);
+ }
+ }
+
+ protected function tearDown()
+ {
+ session_write_close();
+ array_map('unlink', glob($this->savePath.'/*'));
+ if (is_dir($this->savePath)) {
+ rmdir($this->savePath);
+ }
+
+ $this->savePath = null;
+ }
+
+ /**
+ * @return PhpBridgeSessionStorage
+ */
+ protected function getStorage()
+ {
+ $storage = new PhpBridgeSessionStorage();
+ $storage->registerBag(new AttributeBag);
+
+ return $storage;
+ }
+
+ public function testPhpSession53()
+ {
+ if (version_compare(phpversion(), '5.4.0', '>=')) {
+ $this->markTestSkipped('Test skipped, for PHP 5.3 only.');
+ }
+
+ $storage = $this->getStorage();
+
+ $this->assertFalse(isset($_SESSION));
+ $this->assertFalse($storage->getSaveHandler()->isActive());
+
+ session_start();
+ $this->assertTrue(isset($_SESSION));
+ // in PHP 5.3 we cannot reliably tell if a session has started
+ $this->assertFalse($storage->getSaveHandler()->isActive());
+ // PHP session might have started, but the storage driver has not, so false is correct here
+ $this->assertFalse($storage->isStarted());
+
+ $key = $storage->getMetadataBag()->getStorageKey();
+ $this->assertFalse(isset($_SESSION[$key]));
+ $storage->start();
+ $this->assertTrue(isset($_SESSION[$key]));
+ }
+
+ public function testPhpSession54()
+ {
+ if (version_compare(phpversion(), '5.4.0', '<')) {
+ $this->markTestSkipped('Test skipped, for PHP 5.4 only.');
+ }
+
+ $storage = $this->getStorage();
+
+ $this->assertFalse(isset($_SESSION));
+ $this->assertFalse($storage->getSaveHandler()->isActive());
+ $this->assertFalse($storage->isStarted());
+
+ session_start();
+ $this->assertTrue(isset($_SESSION));
+ // in PHP 5.4 we can reliably detect a session started
+ $this->assertTrue($storage->getSaveHandler()->isActive());
+ // PHP session might have started, but the storage driver has not, so false is correct here
+ $this->assertFalse($storage->isStarted());
+
+ $key = $storage->getMetadataBag()->getStorageKey();
+ $this->assertFalse(isset($_SESSION[$key]));
+ $storage->start();
+ $this->assertTrue(isset($_SESSION[$key]));
+ }
+
+ public function testClear()
+ {
+ $storage = $this->getStorage();
+ session_start();
+ $_SESSION['drak'] = 'loves symfony';
+ $storage->getBag('attributes')->set('symfony', 'greatness');
+ $key = $storage->getBag('attributes')->getStorageKey();
+ $this->assertEquals($_SESSION[$key], array('symfony' => 'greatness'));
+ $this->assertEquals($_SESSION['drak'], 'loves symfony');
+ $storage->clear();
+ $this->assertEquals($_SESSION[$key], array());
+ $this->assertEquals($_SESSION['drak'], 'loves symfony');
+ }
+}
79 src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Proxy/AbstractProxyTest.php
View
@@ -86,13 +86,35 @@ public function testIsWrapper()
$this->assertFalse($this->proxy->isWrapper());
}
- public function testIsActive()
+ public function testIsActivePhp53()
{
+ if (version_compare(phpversion(), '5.4.0', '>=')) {
+ $this->markTestSkipped('Test skipped, for PHP 5.3 only.');
+ }
+
+ $this->assertFalse($this->proxy->isActive());
+ }
+
+ /**
+ * @runInSeparateProcess
+ */
+ public function testIsActivePhp54()
+ {
+ if (version_compare(phpversion(), '5.4.0', '<')) {
+ $this->markTestSkipped('Test skipped, for PHP 5.4 only.');
+ }
+
$this->assertFalse($this->proxy->isActive());
+ session_start();
+ $this->assertTrue($this->proxy->isActive());
}
- public function testSetActive()
+ public function testSetActivePhp53()
{
+ if (version_compare(phpversion(), '5.4.0', '>=')) {
+ $this->markTestSkipped('Test skipped, for PHP 5.3 only.');
+ }
+
$this->proxy->setActive(true);
$this->assertTrue($this->proxy->isActive());
$this->proxy->setActive(false);
@@ -101,6 +123,19 @@ public function testSetActive()
/**
* @runInSeparateProcess
+ * @expectedException \LogicException
+ */
+ public function testSetActivePhp54()
+ {
+ if (version_compare(phpversion(), '5.4.0', '<')) {
+ $this->markTestSkipped('Test skipped, for PHP 5.4 only.');
+ }
+
+ $this->proxy->setActive(true);
+ }
+
+ /**
+ * @runInSeparateProcess
*/
public function testName()
{
@@ -113,14 +148,32 @@ public function testName()
/**
* @expectedException \LogicException
*/
- public function testNameException()
+ public function testNameExceptionPhp53()
{
+ if (version_compare(phpversion(), '5.4.0', '>=')) {
+ $this->markTestSkipped('Test skipped, for PHP 5.3 only.');
+ }
+
$this->proxy->setActive(true);
$this->proxy->setName('foo');
}
/**
* @runInSeparateProcess
+ * @expectedException \LogicException
+ */
+ public function testNameExceptionPhp54()
+ {
+ if (version_compare(phpversion(), '5.4.0', '<')) {
+ $this->markTestSkipped('Test skipped, for PHP 5.4 only.');
+ }
+
+ session_start();
+ $this->proxy->setName('foo');
+ }
+
+ /**
+ * @runInSeparateProcess
*/
public function testId()
{
@@ -133,9 +186,27 @@ public function testId()
/**
* @expectedException \LogicException
*/
- public function testIdException()
+ public function testIdExceptionPhp53()
{
+ if (version_compare(phpversion(), '5.4.0', '>=')) {
+ $this->markTestSkipped('Test skipped, for PHP 5.3 only.');
+ }
+
$this->proxy->setActive(true);
$this->proxy->setId('foo');
}
+
+ /**
+ * @runInSeparateProcess
+ * @expectedException \LogicException
+ */
+ public function testIdExceptionPhp54()
+ {
+ if (version_compare(phpversion(), '5.4.0', '<')) {
+ $this->markTestSkipped('Test skipped, for PHP 5.4 only.');
+ }
+
+ session_start();
+ $this->proxy->setId('foo');
+ }
}
8 src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php
View
@@ -23,7 +23,7 @@
class SessionHandlerProxyTest extends \PHPUnit_Framework_TestCase
{
/**
- * @var PHPUnit_Framework_MockObject_Matcher
+ * @var \PHPUnit_Framework_MockObject_Matcher
*/
private $mock;
@@ -52,7 +52,11 @@ public function testOpen()
$this->assertFalse($this->proxy->isActive());
$this->proxy->open('name', 'id');
- $this->assertTrue($this->proxy->isActive());
+ if (version_compare(phpversion(), '5.4.0', '<')) {
+ $this->assertTrue($this->proxy->isActive());
+ } else {
+ $this->assertFalse($this->proxy->isActive());
+ }
}
public function testOpenFalse()
Something went wrong with that request. Please try again.