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

Closed
wants to merge 5 commits into from

2 participants

@ghost
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

@bendavies bendavies and 1 other commented on an outdated diff Apr 5, 2013
...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?

@ghost
ghost added a note Apr 5, 2013

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
@bendavies bendavies and 1 other commented on an outdated diff Apr 5, 2013
...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

@ghost
ghost added a note Apr 5, 2013

fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@pborreli pborreli commented on an outdated diff Apr 5, 2013
...ation/Tests/Session/Storage/PhpSessionStorageTest.php
+ {
+ 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
@pborreli
pborreli added a note Apr 5, 2013

typo sesion => session

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
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
@bamarni

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.

@ghost

@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.

@bendavies bendavies commented on an outdated diff Apr 5, 2013
...tpFoundation/Session/Storage/NativeSessionStorage.php
*/
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
@stanlemon

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

@stof stof commented on an outdated diff Apr 5, 2013
...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()) {
@stof
Symfony member
stof added a note Apr 5, 2013

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
@stof stof and 1 other commented on an outdated diff Apr 5, 2013
...tpFoundation/Session/Storage/NativeSessionStorage.php
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()) {
@stof
Symfony member
stof added a note Apr 5, 2013

why changing it to use the getter the second time ?

@ghost
ghost added a note Apr 5, 2013

Man you have good eyes :) fixed.

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

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

@fabpot fabpot and 1 other commented on an outdated diff Apr 6, 2013
...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)');
@fabpot
Symfony member
fabpot added a note Apr 6, 2013

All exception messages must end with a dot.

@ghost
ghost added a note Apr 6, 2013

Fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@ghost Unknown referenced this pull request in symfony/symfony-docs Apr 6, 2013
Merged

[WCM] Document PhpSessionStorage #2474

@ghost

Updated with docs PR.

@staabm staabm and 1 other commented on an outdated diff Apr 7, 2013
.../HttpFoundation/Session/Storage/PhpSessionStorage.php
+ {
+ $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()) {
@staabm
staabm added a note Apr 7, 2013

Here also getter at 2nd condition

@ghost
ghost added a note Apr 7, 2013

Fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@staabm staabm and 2 others commented on an outdated diff Apr 7, 2013
.../HttpFoundation/Session/Storage/PhpSessionStorage.php
+
+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;
@staabm
staabm added a note Apr 7, 2013

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

@ghost
ghost added a note Apr 7, 2013

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)
@fabpot
Symfony member
fabpot added a note Apr 7, 2013

Looks good to me.

@ghost
ghost added a note Apr 7, 2013

OK, done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@staabm staabm commented on the diff Apr 7, 2013
...on/Tests/Session/Storage/NativeSessionStorageTest.php
+ /**
+ * @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();
@staabm
staabm added a note Apr 7, 2013

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

@ghost
ghost added a note Apr 7, 2013

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
@staabm staabm and 1 other commented on an outdated diff Apr 7, 2013
...ation/Tests/Session/Storage/PhpSessionStorageTest.php
+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
@staabm
staabm added a note Apr 7, 2013

There is no param

@ghost
ghost added a note Apr 7, 2013

Fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@fabpot fabpot referenced this pull request Apr 9, 2013
Closed

Update session start check #7216

@fabpot fabpot and 1 other commented on an outdated diff Apr 11, 2013
...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');
@fabpot
Symfony member
fabpot added a note Apr 11, 2013

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

@ghost
ghost added a note Apr 11, 2013

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

@ghost
ghost added a note Apr 11, 2013

Fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@fabpot fabpot added a commit that referenced this pull request Apr 18, 2013
@fabpot 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
@fabpot fabpot added a commit that closed this pull request Apr 18, 2013
@fabpot 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
@fabpot fabpot closed this in 7bf8933 Apr 18, 2013
@hackzilla hackzilla pushed a commit that referenced this pull request Dec 10, 2014
@fabpot 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