Updated controller model so that sessions are linked to controllers, and current-controller is represented as a stack

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@40424 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Sam Minnee 2007-08-17 05:45:15 +00:00
parent 9b045b61db
commit 9c80282dfd
3 changed files with 119 additions and 20 deletions

View File

@ -2,7 +2,15 @@
/** /**
* Handles all manipulation of the session. * Handles all manipulation of the session.
* *
* The static methods are used to manipulate the currently active controller's session.
* The instance methods are used to manipulate a particular session. There can be more than one of these created.
*
* In order to support things like testing, the session is associated with a particular Controller. In normal usage, this is loaded from
* and saved to the regular PHP session, but for things like static-page-generation and unit-testing, you can create multiple Controllers,
* each with their own session.
*
* The instance object is basically just a way of manipulating a set of nested maps, and isn't specific to session data.
* This class is currently really basic and could do with a more well-thought-out implementation * This class is currently really basic and could do with a more well-thought-out implementation
* *
* $session->myVar = 'XYZ' would be fine, as would Session::data->myVar. What about the equivalent * $session->myVar = 'XYZ' would be fine, as would Session::data->myVar. What about the equivalent
@ -15,10 +23,41 @@
*/ */
class Session { class Session {
public static function set($name, $val) { public static function set($name, $val) {
return Controller::curr()->getSession()->inst_set($name, $val);
}
public static function addToArray($name, $val) {
return Controller::curr()->getSession()->inst_addToArray($name, $val);
}
public static function get($name) {
return Controller::curr()->getSession()->inst_get($name);
}
public static function clear($name) {
return Controller::curr()->getSession()->inst_clear($name);
}
public static function getAll() {
return Controller::curr()->getSession()->inst_getAll($name);
}
/**
* Session data
*/
protected $data = array();
/**
* Create a new session object, with the given starting data
* @param $data Can be an array of data (such as $_SESSION) or another Session object to clone.
*/
function __construct($data) {
if($data instanceof Session) $data = $data->inst_getAll();
$this->data = $data;
}
public function inst_set($name, $val) {
$names = explode('.', $name); $names = explode('.', $name);
// We still want to do this even if we have strict path checking for legacy code // We still want to do this even if we have strict path checking for legacy code
$var = &$_SESSION; $var = &$this->data;
foreach($names as $n) { foreach($names as $n) {
$var = &$var[$n]; $var = &$var[$n];
@ -27,11 +66,11 @@ class Session {
$var = $val; $var = $val;
} }
public static function addToArray($name, $val) { public function inst_addToArray($name, $val) {
$names = explode('.', $name); $names = explode('.', $name);
// We still want to do this even if we have strict path checking for legacy code // We still want to do this even if we have strict path checking for legacy code
$var = &$_SESSION; $var = &$this->data;
foreach($names as $n) { foreach($names as $n) {
$var = &$var[$n]; $var = &$var[$n];
@ -40,14 +79,14 @@ class Session {
$var[] = $val; $var[] = $val;
} }
public static function get($name) { public function inst_get($name) {
$names = explode('.', $name); $names = explode('.', $name);
if(!isset($_SESSION)) { if(!isset($this->data)) {
return null; return null;
} }
$var = $_SESSION; $var = $this->data;
foreach($names as $n) { foreach($names as $n) {
if(!isset($var[$n])) { if(!isset($var[$n])) {
@ -59,11 +98,11 @@ class Session {
return $var; return $var;
} }
public static function clear($name) { public function inst_clear($name) {
$names = explode('.', $name); $names = explode('.', $name);
// We still want to do this even if we have strict path checking for legacy code // We still want to do this even if we have strict path checking for legacy code
$var = &$_SESSION; $var = &$this->data;
foreach($names as $n) { foreach($names as $n) {
$var = &$var[$n]; $var = &$var[$n];
@ -72,8 +111,8 @@ class Session {
$var = null; $var = null;
} }
public static function getAll() { public function inst_getAll() {
return $_SESSION; return $this->data;
} }
/** /**

View File

@ -15,8 +15,17 @@ class Controller extends ViewableData {
protected $requestParams; protected $requestParams;
protected $action; protected $action;
/**
* The {@link Session} object for this controller
*/
protected $session;
protected static $currentController; /**
* Stack of current controllers.
* Controller::$controller_stack[0] is the current controller.
*/
protected static $controller_stack = array();
protected $basicAuthEnabled = true; protected $basicAuthEnabled = true;
@ -43,7 +52,7 @@ class Controller extends ViewableData {
protected $baseInitCalled = false; protected $baseInitCalled = false;
function run($requestParams) { function run($requestParams) {
if(isset($_GET['debug_profile'])) Profiler::mark("Controller", "run"); if(isset($_GET['debug_profile'])) Profiler::mark("Controller", "run");
Controller::$currentController = $this; $this->pushCurrent();
$this->response = new HTTPResponse(); $this->response = new HTTPResponse();
$this->requestParams = $requestParams; $this->requestParams = $requestParams;
@ -56,7 +65,10 @@ class Controller extends ViewableData {
if(!$this->baseInitCalled) user_error("init() method on class '$this->class' doesn't call Controller::init(). Make sure that you have parent::init() included.", E_USER_WARNING); if(!$this->baseInitCalled) user_error("init() method on class '$this->class' doesn't call Controller::init(). Make sure that you have parent::init() included.", E_USER_WARNING);
// If we had a redirection or something, halt processing. // If we had a redirection or something, halt processing.
if($this->response->isFinished()) return $this->response; if($this->response->isFinished()) {
$this->popCurrent();
return $this->response;
}
// Look at the action variables for forms // Look at the action variables for forms
foreach($this->requestParams as $paramName => $paramVal) { foreach($this->requestParams as $paramName => $paramVal) {
@ -104,7 +116,10 @@ class Controller extends ViewableData {
// disregard validation if a single field is called // disregard validation if a single field is called
if(!isset($_REQUEST['action_callfieldmethod'])) { if(!isset($_REQUEST['action_callfieldmethod'])) {
$valid = $form->beforeProcessing(); $valid = $form->beforeProcessing();
if(!$valid) return $this->response; if(!$valid) {
$this->popCurrent();
return $this->response;
}
} }
// If the action wasnt' set, choose the default on the form. // If the action wasnt' set, choose the default on the form.
@ -185,6 +200,7 @@ class Controller extends ViewableData {
if(isset($_GET['debug_profile'])) Profiler::unmark("Controller", "run"); if(isset($_GET['debug_profile'])) Profiler::unmark("Controller", "run");
$this->popCurrent();
return $this->response; return $this->response;
} }
@ -255,14 +271,18 @@ class Controller extends ViewableData {
} }
public static function currentController() { public static function currentController() {
return Controller::$currentController; return self::curr();
} }
/** /**
* Returns the current controller * Returns the current controller
*/ */
public static function curr() { public static function curr() {
return Controller::$currentController; if(Controller::$controller_stack) {
return Controller::$controller_stack[0];
} else {
user_error("No current controller available", E_USER_WARNING);
}
} }
/** /**
@ -323,11 +343,32 @@ class Controller extends ViewableData {
function PastMember() { function PastMember() {
return Cookie::get("PastMember") ? true : false; return Cookie::get("PastMember") ? true : false;
} }
/**
* Pushes this controller onto the stack of current controllers.
* This means that any redirection, session setting, or other things that rely on Controller::curr() will now write to this
* controller object.
*/
function pushCurrent() {
array_unshift(self::$controller_stack, $this);
// Create a new session object
if(!$this->session) $this->session = new Session(null);
}
/**
* Pop this controller off the top of the stack.
*/
function popCurrent() {
if($this == self::$controller_stack[0]) {
array_shift(self::$controller_stack);
} else {
user_error("popCurrent called on $this->Controller controller, but it wasn't at the top of the stack", E_USER_WARNING);
}
}
/** /**
* Handle redirection * Handle redirection
*/ */
function redirect($url) { function redirect($url) {
// Attach site-root to relative links, if they have a slash in them // Attach site-root to relative links, if they have a slash in them
if(substr($url,0,4) != "http" && $url[0] != "/" && strpos($url,'/') !== false){ if(substr($url,0,4) != "http" && $url[0] != "/" && strpos($url,'/') !== false){
@ -336,6 +377,21 @@ class Controller extends ViewableData {
$this->response->redirect($url); $this->response->redirect($url);
} }
/**
* Get the Session object representing this Controller's session
*/
function getSession() {
return $this->session;
}
/**
* Set the Session object.
*/
function setSession(Session $session) {
$this->session = $session;
}
} }
?> ?>

View File

@ -42,16 +42,20 @@ class Director {
function direct($url) { function direct($url) {
if(isset($_GET['debug_profile'])) Profiler::mark("Director","direct"); if(isset($_GET['debug_profile'])) Profiler::mark("Director","direct");
$controllerObj = Director::getControllerForURL($url); $controllerObj = Director::getControllerForURL($url);
// Load the session into the controller
$controllerObj->setSession(new Session($_SESSION));
if(is_string($controllerObj) && substr($controllerObj,0,9) == 'redirect:') { if(is_string($controllerObj) && substr($controllerObj,0,9) == 'redirect:') {
Director::redirect(substr($controllerObj, 9)); Director::redirect(substr($controllerObj, 9));
} else if($controllerObj) { } else if($controllerObj) {
$response = $controllerObj->run(array_merge((array)$_GET, (array)$_POST, (array)$_FILES)); $response = $controllerObj->run(array_merge((array)$_GET, (array)$_POST, (array)$_FILES));
// Save the updated session back
$_SESSION = $controllerObj->getSession()->inst_getAll();
if(isset($_GET['debug_profile'])) Profiler::mark("Outputting to browser");
$response->output(); $response->output();
if(isset($_GET['debug_profile'])) Profiler::unmark("Outputting to browser");
} }
if(isset($_GET['debug_profile'])) Profiler::unmark("Director","direct"); if(isset($_GET['debug_profile'])) Profiler::unmark("Director","direct");