Url Routing with PHP – Part Two

This is the second part of a series, I assume you have read Url Routing with PHP – Part One as it covers the basics that I'll build on in Part Two.

Requirements

Assign Responsibilities

The first problem I want to address is that there are two different things going on with the script in part one. 1. The parsing of the URL to obtain a command array and 2. The actual dispatch of the command by the switch statement. I want to create an architecture that easily allows changing the way incoming URLs are formatted, and also the substitution of alternative dispatch schemes.

Standardized Command Objects

To achieve this goal, I create a standard command object. This will allow changing the way the urls are interpreted or dispatched as long as the interpreter creates command objects and the dispatcher accepts them.

  1. class Axial_Command
  2.         {
  3.         var $commandName = ";
  4.         var $parameters = array();
  5.        
  6.         function Axial_Command($commandName,$parameters)
  7.                 {
  8.                 $this->commandName = $commandName;
  9.                 $this->parameters = $parameters;
  10.                 }
  11.         function getCommandName()
  12.                 {
  13.                 return $this->commandName;
  14.                 }
  15.         function getParameters()
  16.                 {
  17.                 return $this->parameters;
  18.                 }
  19.         }

Interpreting URLs

Next we need a class that handles interpreting the incoming URL and creating a command object. The following class uses the same method as in part one and creates commands based on a simple format for the incoming URL, a command followed by its parameters separated by forward slashes.

  1. class Axial_UrlInterpreter
  2.         {
  3.  
  4.         function Axial_UrlInterpreter()
  5.                 {
  6.                 $requestURI = explode('/', $_SERVER['REQUEST_URI']);
  7.                 $scriptName = explode('/',$_SERVER['SCRIPT_NAME']);
  8.                         for($i= 0;$i < sizeof($scriptName);$i++)
  9.                                 {
  10.                                 if ($requestURI[$i] == $scriptName[$i])
  11.                                         {
  12.                                         unset($requestURI[$i]);
  13.                                         }
  14.                                 }
  15.                 $commandArray = array_values($requestURI);
  16.                 $commandName = $commandArray[0];
  17.                 $parameters = array_slice($commandArray,1);
  18.                 $this->command = new Axial_Command($commandArray[0],$parameters);
  19.                      }
  20.        
  21.         function getCommand()
  22.                 {
  23.                 return $this->command;
  24.          }
  25.         }

Dispatching Commands

And finally we can pass the command object to the dispatcher. In order to keep the code simple I use a switch statement that includes different scripts based on the command. There are many other methods that could be used in fact thats the point the of architecture I'm presenting.

  1. class Axial_CommandDispatcher
  2.         {
  3.         var $command;
  4.         function Axial_CommandDispatcher($command)
  5.                 {
  6.                 $this->command = $command;
  7.                 }
  8.  
  9.         function Dispatch()
  10.                 {
  11.                 switch ($this->command->getCommandName())
  12.                         {
  13.                         case 'commandOne' :
  14.                                 include('commandone.php');
  15.                                 break;
  16.                         case 'commandTwo' :
  17.                                 include('commandtwo.php');
  18.                                 break;
  19.                         case ":
  20.                                 include('root.php');
  21.                                 break;
  22.                         default:
  23.                                 include('default.php');
  24.                                 break;
  25.                         }
  26.                 }
  27.         }

Put it all together

In the first part of this series I used an .htaccess file to redirect to index.php. The same .htaccess is all we need now. Our new index.php will look a bit different though. With the changes from part one made we can now substitute different classes to handle the interpretation of URLs or command dispatching easily.

  1. include('axial.command.php');
  2. include('axial.urlinterpreter.php');
  3. include('axial.commanddispatcher.php');
  4. $urlInterpreter = new Axial_UrlInterpreter();
  5. $command = $urlInterpreter->getCommand();
  6. $commandDispatcher = new Axial_CommandDispatcher($command);
  7. $commandDispatcher->Dispatch();

Try it out

You can visit examples.phpaddiction.com/urlrouter/part_2/ to see it in action and download the sample code.

Next up

In the next part of this series I will explore some ways to get rid of the switch statement in the dispatcher and create a small framework to hang your code from.

Comments

12 Responses to “Url Routing with PHP – Part Two”

  1. L!MP on April 6th, 2007 2:38 pm
    Gravatar

    replace

    $this->command = new Axial_Command($commandArray[0],$parameters);

    to

    $this->command = new Axial_Command($commandName ,$parameters);

  2. Doug Hill on April 7th, 2007 12:39 am
    Gravatar

    Sure, either way works, It is quicker to leave out the array access and perhaps the clearer style, thanks for the pointer.

  3. Roman on April 8th, 2007 5:55 am
    Gravatar

    I use $_SERVER['PATH_INFO']. This way I only get what is past the script path.

  4. Doug Hill on April 9th, 2007 4:49 am
    Gravatar

    Roman: I decided against using PATH_INFO since it wouldn't allow for separators other than forward slashes "/". I wanted the entire URL after the script not just the directories. Also I found that support for PATH_INFO wasn't the same across PHP versions.

  5. Tom on May 18th, 2009 4:26 pm
    Gravatar

    I dont understand why is a url router?

    Here an example of an url router:

    $listRoutes = array(
    "home" => "",
    "articleHome" => "articles",
    "news" => "articles/news",
    "category" => "articles/",
    "article" => "articles//article-.html"
    );

    foreach ($listRoutes AS $name => $route)
    {
    if (strpos($route,'<') === false)
    {
    echo "Static $name: ".(match_static_route($url,$route) ? 'Yes' : 'No');
    }
    else
    {
    echo "Dynamic $name: ".(count($r = match_dynamic_route2($url,$route)) != 0 ? 'Yes':'No')." — ";
    print_r($r);
    }
    }

    function match_static_route($url,$route)
    {
    return $url == $route;
    }

    function match_dynamic_route2($url,$route)
    {
    $result = array();

    $route = str_replace("/","\/",$route);
    $route = str_replace("<","(?P",">[^\/]*)",$route);

    if (preg_match("/$route$/",$url,$matches) > 0)
    {
    foreach ($matches AS $key=>$value)
    {
    if (!is_numeric($key))
    {
    $result[$key] = $value;
    }
    }
    }

    return $result;
    }

    You can exploit the parameters on $r.

  6. Carlo on June 25th, 2009 6:59 am
    Gravatar

    Please can you Tom from the previous comment or someone else explain to me the point?

    What does that mean "you can exploit". Also the introducing sentence is not quite clear for me, I am not english.

    But I see in the code some interesting point, so I'd like to understand the whole thing

    Thank You
    Carlo

  7. Johan Hultin on June 28th, 2009 2:17 pm
    Gravatar

    I don't really like the whole thing with cases there, I'd say it's quite static and should be worked on to be more dynamic, I will work on it and see if I find any neat solution

  8. stunaz on July 29th, 2009 3:41 am
    Gravatar

    There is a bug on that script.
    we can see it on the example. while testing the link with two parameters, the css style disappear.
    how can we fix that?

  9. stunaz on July 29th, 2009 4:00 am
    Gravatar

    i mean in the example that we download from the link

  10. vulo on October 5th, 2009 9:40 pm
    Gravatar

    While using the url router,
    in the functions that I am using instead of variables I get

    AC_RunActiveContent.js
    Scripts
    robots.txt, loaded into the urls – array
    and the the actual url (for example – events, ot contacts)

    Also I think it is reloading the page every time. with these values into the urls – array

    Have anyone idea why this is happening.
    Regards
    Vulo

  11. IT Support London on November 24th, 2009 8:21 am
    Gravatar

    i cant get the code to work arghhhh!

  12. Anonymous on May 29th, 2010 9:40 am
    Gravatar

    Great Article.

    //
    // Shorter way of parsing the path.
    //
    array_diff (
    explode('/', $_SERVER['REQUEST_URI']),
    explode('/', $_SERVER['SCRIPT_NAME'])
    );

Leave a Reply




XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>