Browse Source

Bug #15523: `yii\web\Session` settings could now be configured after session is started (StalkAlex, rob006, daniel1302, samdark)

Co-authored-by: Alexander Makarov <sam@rmcreative.ru>
Co-authored-by: Robert Korulczyk <robert@korulczyk.pl>
Co-authored-by: daniel.1302 <daniel.1302@gmail.com>
tags/2.0.14
Alexander 7 years ago committed by Alexander Makarov
parent
commit
24f4e3126a
  1. 2
      framework/CHANGELOG.md
  2. 9
      framework/web/DbSession.php
  3. 39
      framework/web/Session.php
  4. 21
      tests/framework/web/session/AbstractDbSessionTest.php
  5. 38
      tests/framework/web/session/SessionTest.php

2
framework/CHANGELOG.md

@ -3,6 +3,8 @@ Yii Framework 2 Change Log
2.0.14 under development 2.0.14 under development
------------------------ ------------------------
- Bug #15523: `yii\web\Session` settings could now be configured after session is started (StalkAlex, rob006, daniel1302, samdark)
- Enh #15216: Added `yii\web\ErrorHandler::$traceLine` to allow opening file at line clicked in IDE (vladis84) - Enh #15216: Added `yii\web\ErrorHandler::$traceLine` to allow opening file at line clicked in IDE (vladis84)
- Enh #14488: Added support for X-Forwarded-Host to `yii\web\Request`, fixed `getServerPort()` usage (si294r, samdark) - Enh #14488: Added support for X-Forwarded-Host to `yii\web\Request`, fixed `getServerPort()` usage (si294r, samdark)
- Enh #13019: Support JSON in SchemaBuilderTrait (zhukovra, undefinedor) - Enh #13019: Support JSON in SchemaBuilderTrait (zhukovra, undefinedor)

9
framework/web/DbSession.php

@ -13,6 +13,7 @@ use yii\db\Connection;
use yii\db\PdoValue; use yii\db\PdoValue;
use yii\db\Query; use yii\db\Query;
use yii\di\Instance; use yii\di\Instance;
use yii\helpers\ArrayHelper;
/** /**
* DbSession extends [[Session]] by using database as session data storage. * DbSession extends [[Session]] by using database as session data storage.
@ -81,10 +82,12 @@ class DbSession extends MultiFieldSession
* This method will initialize the [[db]] property to make sure it refers to a valid DB connection. * This method will initialize the [[db]] property to make sure it refers to a valid DB connection.
* @throws InvalidConfigException if [[db]] is invalid. * @throws InvalidConfigException if [[db]] is invalid.
*/ */
public function init() public function __construct(array $config = [])
{ {
parent::init(); // db component should be initialized before configuring DbSession component
$this->db = Instance::ensure($this->db, Connection::className()); // @see https://github.com/yiisoft/yii2/pull/15523#discussion_r166701079
$this->db = Instance::ensure(ArrayHelper::remove($config, 'db', $this->db), Connection::className());
parent::__construct($config);
} }
/** /**

39
framework/web/Session.php

@ -89,6 +89,11 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/ */
private $_cookieParams = ['httponly' => true]; private $_cookieParams = ['httponly' => true];
/**
* @var $frozenSessionData array|null is used for saving session between recreations due to session parameters update.
*/
private $frozenSessionData;
/** /**
* Initializes the application component. * Initializes the application component.
@ -413,6 +418,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/ */
public function setUseCookies($value) public function setUseCookies($value)
{ {
$this->freeze();
if ($value === false) { if ($value === false) {
ini_set('session.use_cookies', '0'); ini_set('session.use_cookies', '0');
ini_set('session.use_only_cookies', '0'); ini_set('session.use_only_cookies', '0');
@ -423,6 +429,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
ini_set('session.use_cookies', '1'); ini_set('session.use_cookies', '1');
ini_set('session.use_only_cookies', '0'); ini_set('session.use_only_cookies', '0');
} }
$this->unfreeze();
} }
/** /**
@ -439,6 +446,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/ */
public function setGCProbability($value) public function setGCProbability($value)
{ {
$this->freeze();
if ($value >= 0 && $value <= 100) { if ($value >= 0 && $value <= 100) {
// percent * 21474837 / 2147483647 ≈ percent * 0.01 // percent * 21474837 / 2147483647 ≈ percent * 0.01
ini_set('session.gc_probability', floor($value * 21474836.47)); ini_set('session.gc_probability', floor($value * 21474836.47));
@ -446,6 +454,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
} else { } else {
throw new InvalidParamException('GCProbability must be a value between 0 and 100.'); throw new InvalidParamException('GCProbability must be a value between 0 and 100.');
} }
$this->unfreeze();
} }
/** /**
@ -461,7 +470,9 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/ */
public function setUseTransparentSessionID($value) public function setUseTransparentSessionID($value)
{ {
$this->freeze();
ini_set('session.use_trans_sid', $value ? '1' : '0'); ini_set('session.use_trans_sid', $value ? '1' : '0');
$this->unfreeze();
} }
/** /**
@ -478,7 +489,9 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/ */
public function setTimeout($value) public function setTimeout($value)
{ {
$this->freeze();
ini_set('session.gc_maxlifetime', $value); ini_set('session.gc_maxlifetime', $value);
$this->unfreeze();
} }
/** /**
@ -902,4 +915,30 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
$this->open(); $this->open();
unset($_SESSION[$offset]); unset($_SESSION[$offset]);
} }
/**
* If session is started it's not possible to edit session ini settings. In PHP7.2+ it throws exception.
* This function saves session data to temporary variable and stop session.
* @since 2.0.14
*/
protected function freeze()
{
if (isset($_SESSION) && $this->getIsActive()) {
$this->frozenSessionData = $_SESSION;
}
$this->close();
}
/**
* Starts session and restores data from temporary variable
* @since 2.0.14
*/
protected function unfreeze()
{
$this->open();
if (null !== $this->frozenSessionData) {
$_SESSION = $this->frozenSessionData;
$this->frozenSessionData = null;
}
}
} }

21
tests/framework/web/session/AbstractDbSessionTest.php

@ -197,4 +197,25 @@ abstract class AbstractDbSessionTest extends TestCase
$this->assertEquals(['base'], $history); $this->assertEquals(['base'], $history);
$this->createTableSession(); $this->createTableSession();
} }
public function testInstantiate()
{
$oldTimeout = ini_get('session.gc_maxlifetime');
// unset Yii::$app->db to make sure that all queries are made against sessionDb
Yii::$app->set('sessionDb', Yii::$app->db);
Yii::$app->set('db', null);
$session = new DbSession([
'timeout' => 300,
'db' => 'sessionDb',
]);
$this->assertSame(Yii::$app->sessionDb, $session->db);
$this->assertSame(300, $session->timeout);
$session->close();
Yii::$app->set('db', Yii::$app->sessionDb);
Yii::$app->set('sessionDb', null);
ini_set('session.gc_maxlifetime', $oldTimeout);
}
} }

38
tests/framework/web/session/SessionTest.php

@ -32,4 +32,42 @@ class SessionTest extends TestCase
$this->assertNotEmpty($newSessionId); $this->assertNotEmpty($newSessionId);
$this->assertEquals($oldSessionId, $newSessionId); $this->assertEquals($oldSessionId, $newSessionId);
} }
/**
* Test to prove that after Session::open changing session parameters will not throw exceptions
* and its values will be changed as expected.
*/
public function testParamsAfterSessionStart()
{
$session = new Session();
$session->open();
$oldUseTransparentSession = $session->getUseTransparentSessionID();
$session->setUseTransparentSessionID(true);
$newUseTransparentSession = $session->getUseTransparentSessionID();
$this->assertNotEquals($oldUseTransparentSession, $newUseTransparentSession);
$this->assertTrue($newUseTransparentSession);
//without this line phpunit will complain about risky tests due to unclosed buffer
$session->setUseTransparentSessionID(false);
$oldTimeout = $session->getTimeout();
$session->setTimeout(600);
$newTimeout = $session->getTimeout();
$this->assertNotEquals($oldTimeout, $newTimeout);
$this->assertEquals(600, $newTimeout);
$oldUseCookies = $session->getUseCookies();
$session->setUseCookies(false);
$newUseCookies = $session->getUseCookies();
if (null !== $newUseCookies) {
$this->assertNotEquals($oldUseCookies, $newUseCookies);
$this->assertFalse($newUseCookies);
}
$oldGcProbability = $session->getGCProbability();
$session->setGCProbability(100);
$newGcProbability = $session->getGCProbability();
$this->assertNotEquals($oldGcProbability, $newGcProbability);
$this->assertEquals(100, $newGcProbability);
}
} }

Loading…
Cancel
Save