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
------------------------
- 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 #14488: Added support for X-Forwarded-Host to `yii\web\Request`, fixed `getServerPort()` usage (si294r, samdark)
- 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\Query;
use yii\di\Instance;
use yii\helpers\ArrayHelper;
/**
* 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.
* @throws InvalidConfigException if [[db]] is invalid.
*/
public function init()
public function __construct(array $config = [])
{
parent::init();
$this->db = Instance::ensure($this->db, Connection::className());
// db component should be initialized before configuring DbSession component
// @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];
/**
* @var $frozenSessionData array|null is used for saving session between recreations due to session parameters update.
*/
private $frozenSessionData;
/**
* Initializes the application component.
@ -413,6 +418,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/
public function setUseCookies($value)
{
$this->freeze();
if ($value === false) {
ini_set('session.use_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_only_cookies', '0');
}
$this->unfreeze();
}
/**
@ -439,6 +446,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/
public function setGCProbability($value)
{
$this->freeze();
if ($value >= 0 && $value <= 100) {
// percent * 21474837 / 2147483647 ≈ percent * 0.01
ini_set('session.gc_probability', floor($value * 21474836.47));
@ -446,6 +454,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
} else {
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)
{
$this->freeze();
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)
{
$this->freeze();
ini_set('session.gc_maxlifetime', $value);
$this->unfreeze();
}
/**
@ -902,4 +915,30 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
$this->open();
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->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->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