Merge pull request #3216 from halkyon/session_inst

Better support for overloading start and destroy methods in Session
This commit is contained in:
Mateusz U 2014-06-23 10:15:15 +12:00
commit 32ae468454
10 changed files with 101 additions and 86 deletions

View File

@ -439,7 +439,7 @@ class Controller extends RequestHandler implements TemplateGlobalProvider {
if(isset(self::$controller_stack[1])) {
$this->session = self::$controller_stack[1]->getSession();
} else {
$this->session = new Session(null);
$this->session = Injector::inst()->create('Session', array());
}
}
}

View File

@ -135,12 +135,13 @@ class Director implements TemplateGlobalProvider {
$req->addHeader($header, $value);
}
// Initiate an empty session - doesn't initialize an actual PHP session until saved (see below)
$session = Injector::inst()->create('Session', isset($_SESSION) ? $_SESSION : array());
// Only resume a session if its not started already, and a session identifier exists
if(!isset($_SESSION) && Session::request_contains_session_id()) {
Session::start();
$session->inst_start();
}
// Initiate an empty session - doesn't initialize an actual PHP session until saved (see belwo)
$session = new Session(isset($_SESSION) ? $_SESSION : null);
$output = Injector::inst()->get('RequestProcessor')->preRequest($req, $session, $model);
@ -151,7 +152,7 @@ class Director implements TemplateGlobalProvider {
$result = Director::handleRequest($req, $session, $model);
// Save session data (and start/resume it if required)
// Save session data. Note that inst_save() will start/resume the session if required.
$session->inst_save();
// Return code for a redirection request
@ -229,7 +230,7 @@ class Director implements TemplateGlobalProvider {
if(!$httpMethod) $httpMethod = ($postVars || is_array($postVars)) ? "POST" : "GET";
if(!$session) $session = new Session(null);
if(!$session) $session = Injector::inst()->create('Session', array());
// Back up the current values of the superglobals
$existingRequestVars = isset($_REQUEST) ? $_REQUEST : array();

View File

@ -139,7 +139,7 @@ class Session {
/**
* Start PHP session, then create a new Session object with the given start data.
*
* @param $data Can be an array of data (such as $_SESSION) or another Session object to clone.
* @param $data array|Session Can be an array of data (such as $_SESSION) or another Session object to clone.
*/
public function __construct($data) {
if($data instanceof Session) $data = $data->inst_getAll();
@ -150,9 +150,8 @@ class Session {
if ($this->data['HTTP_USER_AGENT'] != $this->userAgent()) {
// Funny business detected!
$this->inst_clearAll();
Session::destroy();
Session::start();
$this->inst_destroy();
$this->inst_start();
}
}
}
@ -347,11 +346,78 @@ class Session {
if(Controller::has_curr()) {
return Controller::curr()->getSession();
} else {
if(!self::$default_session) self::$default_session = new Session(isset($_SESSION) ? $_SESSION : array());
if(!self::$default_session) {
self::$default_session = Injector::inst()->create('Session', isset($_SESSION) ? $_SESSION : array());
}
return self::$default_session;
}
}
public function inst_start($sid = null) {
$path = Config::inst()->get('Session', 'cookie_path');
if(!$path) $path = Director::baseURL();
$domain = Config::inst()->get('Session', 'cookie_domain');
$secure = Director::is_https() && Config::inst()->get('Session', 'cookie_secure');
$session_path = Config::inst()->get('Session', 'session_store_path');
$timeout = Config::inst()->get('Session', 'timeout');
if(!session_id() && !headers_sent()) {
if($domain) {
session_set_cookie_params($timeout, $path, $domain, $secure, true);
} else {
session_set_cookie_params($timeout, $path, null, $secure, true);
}
// Allow storing the session in a non standard location
if($session_path) session_save_path($session_path);
// If we want a secure cookie for HTTPS, use a seperate session name. This lets us have a
// seperate (less secure) session for non-HTTPS requests
if($secure) session_name('SECSESSID');
// @ is to supress win32 warnings/notices when session wasn't cleaned up properly
// There's nothing we can do about this, because it's an operating system function!
if($sid) session_id($sid);
@session_start();
$this->data = isset($_SESSION) ? $_SESSION : array();
}
// Modify the timeout behaviour so it's the *inactive* time before the session expires.
// By default it's the total session lifetime
if($timeout && !headers_sent()) {
Cookie::set(session_name(), session_id(), $timeout/86400, $path, $domain ? $domain
: null, $secure, true);
}
}
public function inst_destroy($removeCookie = true) {
if(session_id()) {
if($removeCookie) {
$path = Config::inst()->get('Session', 'cookie_path');
if(!$path) $path = Director::baseURL();
$domain = Config::inst()->get('Session', 'cookie_domain');
$secure = Config::inst()->get('Session', 'cookie_secure');
if($domain) {
Cookie::set(session_name(), '', null, $path, $domain, $secure, true);
} else {
Cookie::set(session_name(), '', null, $path, null, $secure, true);
}
unset($_COOKIE[session_name()]);
}
session_destroy();
// Clean up the superglobal - session_destroy does not do it.
// http://nz1.php.net/manual/en/function.session-destroy.php
unset($_SESSION);
$this->data = array();
}
}
public function inst_set($name, $val) {
// Quicker execution path for "."-free names
if(strpos($name,'.') === false) {
@ -472,7 +538,11 @@ class Session {
public function inst_save() {
if($this->changedData) {
$this->inst_finalize();
if(!isset($_SESSION)) Session::start();
if(!isset($_SESSION)) {
$this->inst_start();
}
$this->recursivelyApply($this->changedData, $_SESSION);
}
}
@ -529,41 +599,7 @@ class Session {
* @param string $sid Start the session with a specific ID
*/
public static function start($sid = null) {
$path = Config::inst()->get('Session', 'cookie_path');
if(!$path) $path = Director::baseURL();
$domain = Config::inst()->get('Session', 'cookie_domain');
$secure = Director::is_https() && Config::inst()->get('Session', 'cookie_secure');
$session_path = Config::inst()->get('Session', 'session_store_path');
$timeout = Config::inst()->get('Session', 'timeout');
if(!session_id() && !headers_sent()) {
if($domain) {
session_set_cookie_params($timeout, $path, $domain,
$secure /* secure */, true /* httponly */);
} else {
session_set_cookie_params($timeout, $path, null,
$secure /* secure */, true /* httponly */);
}
// Allow storing the session in a non standard location
if($session_path) session_save_path($session_path);
// If we want a secure cookie for HTTPS, use a seperate session name. This lets us have a
// seperate (less secure) session for non-HTTPS requests
if($secure) session_name('SECSESSID');
// @ is to supress win32 warnings/notices when session wasn't cleaned up properly
// There's nothing we can do about this, because it's an operating system function!
if($sid) session_id($sid);
@session_start();
}
// Modify the timeout behaviour so it's the *inactive* time before the session expires.
// By default it's the total session lifetime
if($timeout && !headers_sent()) {
Cookie::set(session_name(), session_id(), $timeout/86400, $path, $domain ? $domain
: null, $secure, true);
}
self::current_session()->inst_start($sid);
}
/**
@ -572,29 +608,7 @@ class Session {
* @param bool $removeCookie If set to TRUE, removes the user's cookie, FALSE does not remove
*/
public static function destroy($removeCookie = true) {
if(session_id()) {
if($removeCookie) {
$path = Config::inst()->get('Session', 'cookie_path');
if(!$path) $path = Director::baseURL();
$domain = Config::inst()->get('Session', 'cookie_domain');
$secure = Config::inst()->get('Session', 'cookie_secure');
if($domain) {
Cookie::set(session_name(), '', null, $path, $domain, $secure, true);
}
else {
Cookie::set(session_name(), '', null, $path, null, $secure, true);
}
unset($_COOKIE[session_name()]);
}
session_destroy();
// Clean up the superglobal - session_destroy does not do it.
// http://nz1.php.net/manual/en/function.session-destroy.php
unset($_SESSION);
}
self::current_session()->inst_destroy($removeCookie);
}
/**

View File

@ -201,7 +201,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
DataObject::reset();
if(class_exists('SiteTree')) SiteTree::reset();
Hierarchy::reset();
if(Controller::has_curr()) Controller::curr()->setSession(new Session(array()));
if(Controller::has_curr()) Controller::curr()->setSession(Injector::inst()->create('Session', array()));
Security::$database_is_ready = null;
$fixtureFile = static::get_fixture_file();

View File

@ -23,7 +23,7 @@ class TestSession {
private $lastUrl;
public function __construct() {
$this->session = new Session(array());
$this->session = Injector::inst()->create('Session', array());
$this->controller = new Controller();
$this->controller->setSession($this->session);
$this->controller->pushCurrent();

View File

@ -18,7 +18,7 @@ URLs. Here is an example from the subsites module:
* Return a session that has a user logged in as an administrator
*/
public function adminLoggedInSession() {
return new Session(array(
return Injector::inst()->create('Session', array(
'loggedInAs' => $this->idFromFixture('Member', 'admin')
));
}

View File

@ -5,7 +5,7 @@ class FakeController extends Controller {
public function __construct() {
parent::__construct();
$session = new Session(isset($_SESSION) ? $_SESSION : null);
$session = Injector::inst()->create('Session', isset($_SESSION) ? $_SESSION : array());
$this->setSession($session);
$this->pushCurrent();

View File

@ -362,7 +362,7 @@ class RestfulServiceTest_MockRestfulService extends RestfulService {
public function request($subURL = '', $method = "GET", $data = null, $headers = null, $curlOptions = array()) {
if(!$this->session) {
$this->session = new Session(array());
$this->session = Injector::inst()->create('Session', array());
}
$url = $this->baseURL . $subURL; // Url for the request

View File

@ -47,7 +47,7 @@ class SessionTest extends SapphireTest {
}
public function testSettingExistingDoesntClear() {
$s = new Session(array('something' => array('does' => 'exist')));
$s = Injector::inst()->create('Session', array('something' => array('does' => 'exist')));
$s->inst_set('something.does', 'exist');
$result = $s->inst_changedData();
@ -59,7 +59,7 @@ class SessionTest extends SapphireTest {
* Check that changedData isn't populated with junk when clearing non-existent entries.
*/
public function testClearElementThatDoesntExist() {
$s = new Session(array('something' => array('does' => 'exist')));
$s = Injector::inst()->create('Session', array('something' => array('does' => 'exist')));
$s->inst_clear('something.doesnt.exist');
$result = $s->inst_changedData();
@ -77,7 +77,7 @@ class SessionTest extends SapphireTest {
* Check that changedData is populated with clearing data.
*/
public function testClearElementThatDoesExist() {
$s = new Session(array('something' => array('does' => 'exist')));
$s = Injector::inst()->create('Session', array('something' => array('does' => 'exist')));
$s->inst_clear('something.does');
$result = $s->inst_changedData();
@ -97,7 +97,7 @@ class SessionTest extends SapphireTest {
$_SERVER['HTTP_USER_AGENT'] = 'Test Agent';
// Generate our session
$s = new Session(array());
$s = Injector::inst()->create('Session', array());
$s->inst_set('val', 123);
$s->inst_finalize();
@ -105,7 +105,7 @@ class SessionTest extends SapphireTest {
$_SERVER['HTTP_USER_AGENT'] = 'Fake Agent';
// Verify the new session reset our values
$s2 = new Session($s);
$s2 = Injector::inst()->create('Session', $s);
$this->assertNotEquals($s2->inst_get('val'), 123);
}
}

View File

@ -537,7 +537,7 @@ class VersionedTest extends SapphireTest {
* Tests that reading mode persists between requests
*/
public function testReadingPersistent() {
$session = new Session(array());
$session = Injector::inst()->create('Session', array());
// Set to stage
Director::test('/?stage=Stage', null, $session);
@ -568,7 +568,7 @@ class VersionedTest extends SapphireTest {
);
// Test that session doesn't redundantly store the default stage if it doesn't need to
$session2 = new Session(array());
$session2 = Injector::inst()->create('Session', array());
Director::test('/', null, $session2);
$this->assertEmpty($session2->inst_changedData());
Director::test('/?stage=Live', null, $session2);