4

For a couple of years I have been working on my own lightweight MVC framework for PHP. Which I may at some point release under an opensource license.

Here is what I have been using for handling routes:

function routes($routes, $uriPath) {

    // Loop through every route and compare it with the URI
    foreach ($routes as $route => $actualPage) {

        // Create a route with all identifiers replaced with ([^/]+) regex syntax
        // E.g. $route_regex = shop-please/([^/]+)/moo (originally shop-please/:some_identifier/moo)
        $route_regex = preg_replace('@:[^/]+@', '([^/]+)', $route);

        // Check if URI path matches regex pattern, if so create an array of values from the URI
        if(!preg_match('@' . $route_regex . '@', $uriPath, $matches)) continue;

        // Create an array of identifiers from the route
        preg_match('@' . $route_regex . '@', $route, $identifiers);

        // Combine the identifiers with the values
        $this->request->__get = array_combine($identifiers, $matches);
        array_shift($this->request->__get);

        return $actualPage;
    }

    // We didn't find a route match
    return false;
}

$routes is a passed array formatted like this:

$routes = array(
    // route => actual page
    'page/:action/:id' => 'actualPage',
    'page/:action' => 'actualPage',
)

$uriPath is the URI path without a leading forward-slash e.g. page/update/102

In my page controllers I can access the the route information like so:

echo $this->request->__get['action'];
// update

echo $this->request->__get['id'];
// 102

My question is essentially "can this be simplified or optimised?". With particular emphasis on simplifying the regex and the number of preg_replace and preg_match calls.

1

1 Answer 1

5

I find use of regex in such scenario very unwise, mostly because this can be done without it. I've put up a simply code below that does exactly the same without regex.

Code:

<?php
$routes             = array
(
    // actual path => filter
    'foo'   => array('page', ':action', ':id'),
    'bar'   => array('page', ':action')
);

/**
 * @author Gajus Kuizinas <[email protected]>
 * @copyright Anuary Ltd, http://anuary.com
 * @version 1.0.0 (2011 12 06)
 */
function ay_dispatcher($url, $routes)
{
    $final_path         = FALSE;

    $url_path           = explode('/', $url);
    $url_path_length    = count($url_path);

    foreach($routes as $original_path => $filter)
    {
        // reset the parameters every time in case there is partial match
        $parameters     = array();

        // this filter is irrelevent
        if($url_path_length <> count($filter))
        {
            continue;
        }

        foreach($filter as $i => $key)
        {
            if(strpos($key, ':') === 0)
            {
                $parameters[substr($key, 1)]    = $url_path[$i];
            }
            // this filter is irrelevent
            else if($key != $url_path[$i])
            {       
                continue 2;
            }
        }

        $final_path = $original_path;

        break;
    }

    return $final_path ? array('path' => $final_path, 'parameters' => $parameters) : FALSE;
}

die(var_dump( ay_dispatcher('page/edit', $routes), ay_dispatcher('page/edit/12', $routes), ay_dispatcher('random/invalid/url', $routes) ));

Output:

array(2) {
  ["path"]=>
  string(3) "bar"
  ["parameters"]=>
  array(1) {
    ["action"]=>
    string(4) "edit"
  }
}
array(2) {
  ["path"]=>
  string(3) "foo"
  ["parameters"]=>
  array(2) {
    ["action"]=>
    string(4) "edit"
    ["id"]=>
    string(2) "12"
  }
}
bool(false)
Sign up to request clarification or add additional context in comments.

4 Comments

I do like this solution! Maybe I should stop mimicking Zend Framework so much. However, possibly for beginners, the regex method makes the $routes array slightly more readable.
It takes 1 line of code to convert your $routes to my format, e.g. array_map(funciton($e){ return explode('/', $e); }$your_routes).
An updated version can be found at meta.codereview.stackexchange.com/questions/411/…
@Gajus Hello. Would you take a stab at my question for the possibility of earning 300 points? stackoverflow.com/questions/42172228/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.