Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

Below is what I have come up with for a router/dispatcher system for my personal framework I am working on. Can you please review and tell me any improvements that could be made?

The first part is an array of URI -> to class/method/id_number/page_number using regex. I have only included a partial list of routes, there will be at least 50 possible routes that will have to run the regex on. I am thinking that is pretty bad for performance, but it seems the best way I know of to do it, since I need to match page numbers and id numbers when they exists.

I am new to MVC so this is my first attempt and I am sure you guys can give me improvement on this, thanks for any tips or help!

.htaccess file:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$   index.php?uri=$1    [NC,L,QSA]

Map array():

/**
 * Map URI to class/method and ID and Page numbers
 * Must be an array
 */
$uri_route_map = array( 
    //forums
    'forums/' => array(
        'controller' => 'forums',
        'method' => 'index',
        'id_number' => '',
        'page_number' => ''),

    'forums/viewforum/(?<id_number>\d+)' =>  array(
        'controller' => 'forums',
        'method' => 'viewforum',
        'id_number' => isset($id_number),
        'page_number' => ''),  

    'forums/viewthread/(?<id_number>\d+)' =>  array(
        'controller' => 'forums',
        'method' => 'viewthread',
        'id_number' => isset($id_number),
        'page_number' => ''),

    'forums/viewthread/(?<id_number>\d+)/page-(?<page_number>\d+)' =>  array(
        'controller' => 'forums',
        'method' => 'viewthread',
        'id_number' => isset($id_number),
        'page_number' => isset($page_number)),

    // user routes
    // account routes
    // blog routes 
    // mail routes
    // various other routes
);

Router class that reads and matches the Map array above

/**
 * Run URI against our Map array to get class/method/id-page numbers
 */
 class Router
{
    private $_controller = '';
    private $_method = '';
    public $page_number = '';
    public $id_number = '';

    public function __construct($uri, array $uri_route_map)
    {
        foreach ($uri_route_map as $rUri => $rRoute)
        {
            if (preg_match("#^{$rUri}$#Ui", $uri, $uri_digits))
            {
                //if page number and ID number in uri then set it locally
                $this->page_number = (isset($uri_digits['page_number']) ? $uri_digits['page_number'] : null);
                $this->id_number = (isset($uri_digits['id_number']) ? $uri_digits['id_number'] : null);
                $this->_controller = $rRoute['controller'];
                $this->_method = $rRoute['method'];

                // just for debug and testing while working on it / will be removed from final code
                echo '<hr> $page_number = ' . $this->page_number . '<br><br>';
                echo '<hr> $id_number = ' . $this->id_number . '<br><br>';
                echo '<hr> $controller = ' . $this->_controller . '<br><br>';
                echo '<hr> $method = ' . $this->_method . '<br><br>';
                break;
            }else{
                $this->page_number = '';
                $this->id_number = '';
                $this->_controller = '404';
                $this->_method = '404';
            }
        }
    }

    public function getController()
    {
        return $this->_controller;
    }

    public function getMethod()
    {
        return $this->_method;
    }

    public function getPageNumber()
    {
        return $this->page_number;
    }

    public function getIDNumber()
    {
        return $this->id_number;
    }

    /**
     * Call our class and method from values in the URI
     */
    public function dispatch()
    {
        if (file_exists('controller' . $this->_controller . '.php'))
        {
            include ('controller' . $this->_controller . '.php');
            $controllerName = 'Controller' . $this->_controller;
            $controller = new $controllerName($this->getIDNumber(),$this->getPageNumber());
            $method = $this->_method;
            if (method_exists($this->_controller, $this->_method))
            {
                return $controller->$method();
            } else {
                // method does not exist
            }
        } else {
            // Controller does not exist
        }
    }

}

Run it

/**
 * Testing the class
 */
$uri = isset($_GET['uri']) ? $_GET['uri'] : null;
$router = new Router($uri, $uri_route_map);
$router->dispatch();

?>
share|improve this question
add comment

2 Answers 2

up vote 3 down vote accepted

If you can limit your URI structure to using a delimiter - / comes to mind - you could avoid the regex.

Here's a rough example (not with any configuration, but shows the concept):

$uri = 'forums/viewforum/1';
$parts = explode('/', $uri);

$controller = $parts[0];
$method = $parts[1];
$id = parts[2];

I'd take a look at the router implementations of some popular frameworks (my recommendation would be ZF's router, but that's just me) - there's nothing wrong from learning how someone else tackled the same problem.

share|improve this answer
add comment

Also check out 'Silex' and 'Slim' which are microframeworks which include routing to see how they tackle this problem. Personally I think that just having id_number and page_number is restrictive. Eventually you'll want more complexity (and potentially params) passed in each route.

Silex for example maps each route to a closure, which is a pattern that I think works pretty well.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.