As promised, my attempt at Rails style mapping:
code:
PHP Code:
class Mapper
{
// Example: '$controller/$action/$id/' stored
// as array('$controller', '$action', '$id')
var $patterns = array();
// Example:
// array('controller' => 'ServerPage', 'action' => 'show')
var $defaults = array();
// Stores a new pattern/default combo pair
function connect($pattern, $default = array()) {
$this->patterns[] = $this->clean_explode($pattern);
$this->defaults[] = $default;
}
// Takes 'foo/bar', matches it with a patterns,
// then combins it with the defults to give a full request.
function request($raw_path) {
$path = $this->clean_explode($raw_path);
$request = $this->create_request($path);
if (isset($request['controller'])) {
// could do with some file include logic here.
$controller = $request['controller'].'Controller';
return new $controller($request);
} else {
throw new Exception('Controller not found.');
}
}
// Removes trailing slashes after an explode.
function clean_explode($raw_path) {
$path = explode('/', $raw_path);
!end($path) && array_pop($path);
return $path;
}
// Compares a path (in array form) with the stored patterns.
// Returns the index of the pattern, or throws an exception.
function resolve($path) {
$path_length = count($path);
foreach ($this->patterns as $index => $pattern) {
if ($path_length <= count($pattern)) {
foreach ($path as $i => $part) {
if ($path[$i] != $pattern[$i] && $pattern[$i][0] != '$') {
continue 2;
}
}
// passed the foreach tests, this is the one
return $index;
}
}
throw new Exception('Path not found.');
}
// Takes the selected pattern, and combines it with the
// path data and the defaults to give a full request array.
function create_request($path) {
$index = $this->resolve($path);
// Fill in the blanks
$request = $this->defaults[$index];
foreach($this->patterns[$index] as $i => $unit) {
if($unit[0] == '$' && isset($path[$i])) {
$request[substr($unit, 1)] = $path[$i];
}
}
return $request;
}
}
class BaseController
{
protected $params;
function __construct($params = array()) {
$this->params = $params;
}
function get_params() {
return $this->params;
}
function execute() {
// Untested.
// This function could be overridden in the child
// controllers to add pre and post filters.
/* if (isset($this->_params['action'])) {
$action = $this->_params['action'];
if ($action && method_exists($this, $action)) {
return $this->$action();
}
} */
}
}
class ServerPageController extends BaseController { }
class UserController extends BaseController { }
tests:
PHP Code:
require_once('simpletest/unit_tester.php');
require_once('simpletest/reporter.php');
class TestOfSkeleton extends UnitTestCase
{
function testProbe() {
$map = new Mapper();
$map->connect('$foo/$bar', array('controller' => 'ServerPage', 'action' => 'show'));
$this->assertEqual($map->patterns[0], array('$foo', '$bar'));
$this->assertEqual($map->defaults[0], array('controller' => 'ServerPage', 'action' => 'show'));
$map = new Mapper();
$map->connect('$foo/$bar/', array('controller' => 'ServerPage', 'action' => 'show'));
$this->assertEqual($map->patterns[0], array('$foo', '$bar'));
$this->assertEqual($map->defaults[0], array('controller' => 'ServerPage', 'action' => 'show'));
}
function testCreateRequest() {
$map = new Mapper();
$map->connect('$foo/$bar', array('controller' => 'ServerPage', 'action' => 'show'));
$r = $map->create_request(array('alpha', 'beta'));
$this->assertEqual($r, array('controller' => 'ServerPage', 'action' => 'show', 'foo' => 'alpha', 'bar' => 'beta'));
}
function testSimpleMapping()
{
$map = new Mapper();
$map->connect('$page/', array('controller' => 'ServerPage', 'action' => 'show'));
$raw_request = 'about/';
$controller = $map->request($raw_request);
$this->assertIsA($controller, 'ServerPageController');
$this->assertEqual($controller->get_params(), array('controller' => 'ServerPage', 'action' => 'show', 'page' => 'about'));
}
function testSimpleMappingWithoutTrainlingSlash()
{
$map = new Mapper();
$map->connect('$page/', array('controller' => 'ServerPage', 'action' => 'show'));
$raw_request = 'about';
$controller = $map->request($raw_request);
$this->assertIsA($controller, 'ServerPageController');
$this->assertEqual($controller->get_params(), array('controller' => 'ServerPage', 'action' => 'show', 'page' => 'about'));
}
function testMultipleMapping()
{
$map = new Mapper();
$map->connect('$page/', array('controller' => 'ServerPage', 'action' => 'show'));
$map->connect('$controller/$action/$id');
$raw_request = 'about/';
$controller = $map->request($raw_request);
$this->assertIsA($controller, 'ServerPageController');
$this->assertEqual($controller->get_params(), array('controller' => 'ServerPage', 'action' => 'show', 'page' => 'about'));
}
function testMultipleMappingAlternate()
{
$map = new Mapper();
$map->connect('$page/', array('controller' => 'ServerPage', 'action' => 'show'));
$map->connect('$controller/$action/$id');
$raw_request = 'user/show/John';
$controller = $map->request($raw_request);
$this->assertIsA($controller, 'UserController');
$this->assertEqual($controller->get_params(), array('controller' => 'user', 'action' => 'show', 'id' => 'John'));
}
function testShortMultipleMapping()
{
$map = new Mapper();
$map->connect('$page/', array('controller' => 'ServerPage', 'action' => 'show'));
$map->connect('$controller/$action/$id');
$raw_request = 'user/list';
$controller = $map->request($raw_request);
$this->assertIsA($controller, 'UserController');
$this->assertEqual($controller->get_params(), array('controller' => 'user', 'action' => 'list'));
}
}
$test = &new GroupTest("MVC Controller");
$test->addTestCase(new TestOfSkeleton());
$reporter = new HtmlReporter();
$test->run($reporter);
Douglas
Bookmarks