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.
*
*
* 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
*
* $session->myVar = 'XYZ' would be fine, as would Session::data->myVar. What about the equivalent
@ -15,10 +23,41 @@
*/
class Session {
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);
// 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) {
$var = &$var[$n];
@ -27,11 +66,11 @@ class Session {
$var = $val;
}
public static function addToArray($name, $val) {
public function inst_addToArray($name, $val) {
$names = explode('.', $name);
// 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) {
$var = &$var[$n];
@ -40,14 +79,14 @@ class Session {
$var[] = $val;
}
public static function get($name) {
public function inst_get($name) {
$names = explode('.', $name);
if(!isset($_SESSION)) {
if(!isset($this->data)) {
return null;
}
$var = $_SESSION;
$var = $this->data;
foreach($names as $n) {
if(!isset($var[$n])) {
@ -59,11 +98,11 @@ class Session {
return $var;
}
public static function clear($name) {
public function inst_clear($name) {
$names = explode('.', $name);
// 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) {
$var = &$var[$n];
@ -72,8 +111,8 @@ class Session {
$var = null;
}
public static function getAll() {
return $_SESSION;
public function inst_getAll() {
return $this->data;
}
/**

View File

@ -15,8 +15,17 @@ class Controller extends ViewableData {
protected $requestParams;
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;
@ -43,7 +52,7 @@ class Controller extends ViewableData {
protected $baseInitCalled = false;
function run($requestParams) {
if(isset($_GET['debug_profile'])) Profiler::mark("Controller", "run");
Controller::$currentController = $this;
$this->pushCurrent();
$this->response = new HTTPResponse();
$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 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
foreach($this->requestParams as $paramName => $paramVal) {
@ -104,7 +116,10 @@ class Controller extends ViewableData {
// disregard validation if a single field is called
if(!isset($_REQUEST['action_callfieldmethod'])) {
$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.
@ -185,6 +200,7 @@ class Controller extends ViewableData {
if(isset($_GET['debug_profile'])) Profiler::unmark("Controller", "run");
$this->popCurrent();
return $this->response;
}
@ -255,14 +271,18 @@ class Controller extends ViewableData {
}
public static function currentController() {
return Controller::$currentController;
return self::curr();
}
/**
* Returns the current controller
*/
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() {
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
*/
function redirect($url) {
// Attach site-root to relative links, if they have a slash in them
if(substr($url,0,4) != "http" && $url[0] != "/" && strpos($url,'/') !== false){
@ -336,6 +377,21 @@ class Controller extends ViewableData {
$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) {
if(isset($_GET['debug_profile'])) Profiler::mark("Director","direct");
$controllerObj = Director::getControllerForURL($url);
// Load the session into the controller
$controllerObj->setSession(new Session($_SESSION));
if(is_string($controllerObj) && substr($controllerObj,0,9) == 'redirect:') {
Director::redirect(substr($controllerObj, 9));
} else if($controllerObj) {
$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();
if(isset($_GET['debug_profile'])) Profiler::unmark("Outputting to browser");
}
if(isset($_GET['debug_profile'])) Profiler::unmark("Director","direct");