0
votes

I'm building a PHP Framework for conclusion of my course, and I've stuck on a solution for match some custom routes and standard routes.

My framework's route are similar at routes of Zend Framework 1.

It's match standard routes for

/module/controller/action/param/value/param2/value2/paramn/valuen

The part of URI are optional, and the / route leads to application module, index controller and index action without params and values.

I'm stuck in some custom routes, that I define this way:

/blog/:postname/
/admin/logout/
/blog/posts/:year/:category/
/about/

That routes must match this examples URI requests.

/blog/my-first-post/
/blog/my-first-post/referenced/facebook/
/admin/logout/
/admin/logout/session-id/246753/action
/blog/posts/2013/turism/
/blog/posts/2013/turism/page/2/

But not had to match the standard routes. The custom routes must precede the standard routes. Some examples of standard routes. Examples:

/
/application/
/application/index/
/application/index/index/
/blog/posts/view/id/3/
/admin/login/
/admin/login/logout (that one are the 
/admin/blog/posts/edit/id/3/
/admin/blog/posts/edit/id/3/success/false/

The way I find to do this ellegantily is using RegEx for the matches, but I've trying to learn RegEx for more than one month and don't got it all.

PS: After match the current route, I must to bind the :variable with the related position in the REQUEST_URI.

Thank you for help.

2

2 Answers

0
votes

While admittedly tempting, I wouldn't go with regex in this particular case. Even though I usually go that way. A simple loop and match would do, unless your course is setting some restrictions you have to follow.

I put together an example that should get the job done and runs in the console, just to show what i mean.

function get_route($uri){
  $routes = [
    'blog#show' => 'blog/:postname',
    'admin#logout' => 'admin/logout',
    'blog#category' => 'blog/posts/:year/:category',
    'home#about' => 'about'
  ];

  $params = [];

  $uri = preg_replace('/#|\?.+/', '', $uri); // remove hash or query strings
  $uri = preg_replace('/(^\/)?(\/$)?/', '', $uri); // trim slashes
  $uri = explode('/', $uri);
  $action = null;
  foreach ($routes as $this_action => $this_route) { // loop through possible routes
    $fractions = explode('/', $this_route);
    if (sizeof($fractions) !== sizeof($uri)) continue; // did not match length of uri
    for ($i=0; $i<sizeof($uri); $i++) { // compare each part of uri to each part of route
      if (substr($fractions[$i], 0, 1) !== ':' && $fractions[$i] !== $uri[$i]) break; // not a match and not a param
      if ($i === sizeof($uri)-1) { // made it to the last fraction!
        $ii = 0;
        foreach ($fractions as $fraction) {
          if (substr($fraction, 0, 1) == ':') { // it's a param, map it!
            $params[substr($fraction,1)] = $uri[$ii];
          }
          $ii++;
        }

        return ['action'=>$this_action, 'params'=>$params];
      }
    }
  }
  return false;
}
0
votes

I could reach my needs with this code, a lot of tests has passed.

public function matchCustomRoute($uri)
{
    if($uri == '')
    {
        return null;
    }

    $customRoutes = $this->getRoutes();

    $explodeUri = explode('/', $uri);
    $arrayUri = array();
    foreach($explodeUri as $uriPart)
    {
        if($uriPart == '')
        {
            continue;
        }
        $arrayUri[] = $uriPart;
    }
    $countUri = count($arrayUri);


    foreach($customRoutes as $key => $value)
    {

        $explodeRoute = explode('/',$value['route']);
        $arrayRoute = array();
        foreach($explodeRoute as $routePart)
        {
            if($routePart == '')
            {
                continue;
            }
            $arrayRoute[] = $routePart;
        }
        $countRoute = count($arrayRoute);
        if($countRoute > $countUri)
        {
            continue;
        }
        $matches = 0;

        for($i = 0 ; $i < $countRoute ; $i++)
        {
            $match = preg_match('/'.$arrayUri[$i].'/', '/'.$arrayRoute[$i].'/');
            if($match == 0)
            {
                if(substr($arrayRoute[$i], 0, 1) == ':')
                {
                    $value['params'][substr($arrayRoute[$i], 1)] = $arrayUri[$i];
                }
                else
                {
                    continue;
                }
            }
            $matches++;

        }
        if($matches == $countRoute)
        {
            return $value;
        }

    }
    return null;
}

Thank you for help.