Page 1 of 1
Request class and front controller
Posted: Sun Aug 20, 2006 3:01 pm
by matthijs
Today I've spend my time studying the code of Arborints' skeleton code. There's a Request class which is extended by an A_Controller_Front_Request class. I understand how the request class functions (basicly). Can anyone explain how the A_Controller_Front_Request should be used?
Request.php
Code: Select all
<?php
class A_Http_Request {
var $data = array();
var $is_post = false;
function A_Http_Request() {
if (get_magic_quotes_gpc()) {
$this->removeSlashes($_GET);
$this->removeSlashes($_POST);
$this->removeSlashes($_COOKIE);
}
if (!strcasecmp($_SERVER['REQUEST_METHOD'], 'POST')) {
$this->data =& $_POST;
$this->is_post = true;
} else {
$this->data =& $_GET;
}
if (isset($_SERVER['PATH_INFO'])) {
$this->data['PATH_INFO'] = trim($_SERVER['PATH_INFO'], '/');
}
}
function removeSlashes(&$var) {
if (is_array($var)) {
foreach ($var as $name => $value) {
if (is_array($value)) {
$this->removeSlashes($value);
} else {
$var[$name] = stripslashes($value);
}
}
} else {
$var = stripslashes($var);
}
}
function get($name) {
return (isset($this->data[$name]) ? $this->data[$name] : null);
}
function set($name, $value) {
$this->data[$name] = $value;
}
function has($name) {
return isset($this->data[$name]);
}
}
?>
Front.php:
Code: Select all
<?php
class A_Controller_Front_Request extends A_Http_Request {
var $action_param = 'action';
function A_Controller_Front_Request($action='') {
$this->Request();
$this->setActionParam($action);
}
function setAction($value) {
$this->set($this->action_param, $value);
}
function getAction() {
return $this->get($this->action_param);
}
function setActionParam($name) {
if ($name) {
$this->action_param = $name;
}
}
function getActionParam($name) {
return $this->action_param;
}
}
?>
Trial:
Code: Select all
<?php
error_reporting(E_ALL);
require_once('config.php');
require_once('A/Http/Request.php');
require_once('A/Controller/Front.php');
$Request = & new A_Http_Request();
echo '<h2>Request vars:</h2><pre>' . print_r($Request->data, true) . '</pre>';
$Controller = & new A_Controller_Front_Request();
?>
Trying to instantiate A_Controller_Front_Request() leads to the error Call to undefined method A_Controller_Front_Request::Request(). I've tried different ways of instantiating the class, but always the same result. What am I missing?
I'm just playing a bit with the different classes to try to understand what's going on. So hopefully can give me some hints here.
Posted: Sun Aug 20, 2006 3:17 pm
by Jenk
the constructor is not called Request(), so you should change to $this->A_Http_Request();
Posted: Sun Aug 20, 2006 3:48 pm
by matthijs
Wow, thanks. I've been overlooking that for hours.. now only thing left is understanding how everything works..
So I've been mixing some examples to make some sort of frontcontroller-request mapper, uh .... thing.
test.php
Code: Select all
<?php
error_reporting(E_ALL);
require_once('config.php');
require_once('A/DL.php');
require_once('A/Locator.php');
require_once('A/Http/Request.php');
require_once 'A/Http/PathInfo.php';
require_once('A/Http/Response.php');
require_once('A/Controller/Front.php');
require_once('A/Controller/Mapper.php');
$Locator =& new A_Locator();
$Response =& new A_Http_Response();
$Request = new A_Controller_Front_Request();
$Locator->set('Request', $Request);
$Locator->set('Response', $Response);
$Mapper = new A_Http_PathInfo('', false);
$Mapper->run($Request);
$DefaultAction =& new A_DL('', 'home', 'run');
$ErrorAction =& new A_DL('', 'error', 'run');
$Mapper2 =& new A_Controller_Mapper('controller/', $DefaultAction);
$Controller =& new A_Controller_Front($Mapper2,$ErrorAction);
$Controller->run($Locator);
$Response->out();
?>
This seems to work. When I go the an URL like test.php/edit/ the frontcontroller gets the class edit in the directory controller/ and runs it.
However, as I only understand globally what's going on, I'm sure this code might not be the best. For example, it doesn't matter if I use $Mapper2 (as now) or change it to $Mapper.
What I'm basicly trying to do is get a frontcontroller to work with clean urls, like /controller/action/moreparams/etc/etc. That's why I've combined the example from the A_HTML_Pathinfo with the Frontcontroller.
Re: Request class and front controller
Posted: Sun Aug 20, 2006 7:27 pm
by Christopher
matthijs wrote:Can anyone explain how the A_Controller_Front_Request should be used?
I guess I might be a reasonable person to answer that question. That class was an example to show how you could pass the controller parameters through the request to your controllers. The main reason to do this is that it the Action often want to know what the current parameters used to call itself were (to build a URL to itself for example). Because the parameter key/name is definable, you need to carry the values through is a configuration independent way. Hence the extra functionality added to the Request.
FYI - this kind of object is often called a Context class because in addition to request information it also contains widely used application information. For example, you could add a getConfigVar() method to allow access to configuration variables anywhere the Request was available. It is pretty common that that information is wanted in the same places.
Finally, an alternate way to deal with getting some of this common information passed through is to register it with the Response -- making it a kind of View Helper for the master View object.
Posted: Mon Aug 21, 2006 12:13 am
by matthijs
Thanks Arborint. Your answer makes it a bit more clear. I'll digest the new information and play some more with the classes.
I've read 2 books on OOP and design patterns cover to cover but even though that's nice, I don't think I'm learning a lot. Looking at the examples in the skeleton and playing with them a bit the last 2 days has teached me more.
Thanks again.
Posted: Sun Sep 10, 2006 1:12 pm
by matthijs
Today I started playing with my example again and I noticed something really weird.
The front controller will randomly dispatch to different controllers. Cant find the pattern yet. But once in a while it will return the default action, other times the action from the URL.
Say I visit this url:
localhost/widgets/
The first time I get the output from the widgets class. But then when I refresh the browser after a few minutes I get the default response. Isn't that weird? Like php remembers the request vars some times, or messes things up in memory?
Well, at least I know something isn't right here..
Posted: Mon Sep 11, 2006 9:11 am
by Maugrim_The_Reaper
If you haven't the concept already - Test Driven Design via Unit Testing (with the excellent SimpleTest for PHP) is a great way of testing your code. The real bonus is designing the tests first - which lets you decide how things work and interrelate before you start the actual coding to develop PHP which passes your tests.
It's relatively simple to setup and get started with (though the real pudding arrives with practice). Can test your code with a few clicks of the mouse, or a command line call. Can test multiple classes in a "group". Quickly catches odd behaviour if covered by the tests.
Highly recommend you look it up since once you get the hang of it it's a hugely valuable tool when designing code at the level you're currently working at. There's a testing forum hereabouts if you need a crash course on the Front Controller example - which practically begs for testing (I always find the FC part the most complicated piece of coding in an app - even the cleanest code just gets complicated as you adapt it).
Just my 2c.
From your description - looks like resolving the controller name from the action is going awry. A debugger or unit test of that section of the functionality might offer a few clues. Failing that the usual var_dump() approach rarely fails with trial and error... (as if that's new advice

).
Can you post the current resolving logic from action to controller name?
Posted: Mon Sep 11, 2006 1:36 pm
by matthijs
Hi Maugrim,
thanx for your 2cts. Indeed, starting with TDD is high on my list. In fact I started already but found it too difficult to even write the most basic test...
As far as the current code is concerned. I can post some parts of it:
(it's just an experiment build on top of the skeleton code of arborint. I didn't change anything in the skeleton classes (version .5.1) so I assume the fault is the way I call the different classes. First the index.php file:
Code: Select all
<?php
error_reporting(E_ALL);
require_once('config.php');
require_once('A/DL.php');
require_once('A/Locator.php');
require_once('A/Http/Request.php');
require_once('A/Http/Response.php');
require_once('A/Http/PathInfo.php');
require_once('A/Controller/Front.php');
require_once('A/Controller/Mapper.php');
$Locator =& new A_Locator();
$Response =& new A_Http_Response();
$Request = new A_Http_Request();
$Locator->set('Request', $Request);
$Locator->set('Response', $Response);
$Mapper = new A_Http_Pathinfo('', false);
$Mapper->run($Request);
$DefaultAction =& new A_DL('', 'home', 'run');
$ErrorAction =& new A_DL('','error', 'run');
// maybe it's wrong I call this mapper as well?
$Mapper =& new A_Controller_Mapper('app/controls/', $DefaultAction);
$Controller =& new A_Controller_Front($Mapper, $ErrorAction);
$Controller->run($Locator);
$Response->out();
?>
home.php in app/controls
Code: Select all
<?php
class home{
function run(&$locator){
$content = '
<html>
<body>
<h2>Front controller: home</h2>
<p>Welkom op de homepage</p>
</body>
</html>
';
$response = & $locator->get('Response');
$response->setContent($content);
}
}
?>
But I'm sure I'll need to give this code some more thought. This was just a bit of testing to see what's going on.
Welcome back by the way ..

Posted: Mon Sep 11, 2006 2:03 pm
by Christopher
matthijs wrote:Today I started playing with my example again and I noticed something really weird.
The front controller will randomly dispatch to different controllers. Cant find the pattern yet. But once in a while it will return the default action, other times the action from the URL.
Say I visit this url:
localhost/widgets/
The first time I get the output from the widgets class. But then when I refresh the browser after a few minutes I get the default response. Isn't that weird? Like php remembers the request vars some times, or messes things up in memory?
Well, at least I know something isn't right here..
Actually not wierd, it is because that code is using PATH_INFO plus you are rewriting. The problem is that the first time you submit the URL, the base URL is "localhost/" with the route "widgets/", but the second time you submit the URL the base is considered to be "localhost/widgets/" with no route. You can use the <base> tag in the doc <head> to resolve this.
Posted: Mon Sep 11, 2006 3:26 pm
by matthijs
Aha
Thx!
Posted: Mon Sep 11, 2006 5:52 pm
by Maugrim_The_Reaper
Unit Testing is tough to start with at times - an example run through on the Testing forum would be get you a lot of feedback

.