Blog

Url Routing with PHP – Part Three

Posted by:

In the final part of this series, I will build a functional modular front controller that can be easily adapted to small projects. It is basically the “C” in MVC for a simple MVC framework. If you haven’t read the first two articles I recommend that you read over them first. Url Routing with PHP – Part One and Url Routing with PHP – Part Two.

Requirements

  • Apache with mod_rewrite enabled
  • PHP

A few changes

If you have followed along with the series you will see a few changes to the code that I left you with in Url Routing with PHP – Part Two. The Axial_URLInterpreter and the Axial_Command object received a few minor changes suggested via comments on the previous articles. Most of the changes are in the Axial_CommandDispatch class. A new class Axial_Controller is introduced in this article, along with some changes to the directory structure.

Dispatch via convention

Using a front controller allows creation and access to controllers in a standard way. By using a consistent naming convention and directory structure, command objects can be dispatched via a generic piece of code. When a command is dispatched the appropriate controller is loaded and its execute() method called.

  1. class Axial_CommandDispatcher
  2.       {
  3.       var $Command;
  4.  
  5.       function Axial_CommandDispatcher(&$command)
  6.             {
  7.             $this->Command = $command;
  8.             }
  9.  
  10.       function isController($controllerName)
  11.             {
  12.             if(file_exists(‘axial/controllers/’.$controllerName.‘/controller.’.$controllerName.‘.php’))
  13.                   {
  14.                   return true;
  15.                   }
  16.             else
  17.                   {
  18.                   return false;
  19.                   }
  20.             }
  21.  
  22.  
  23.       function Dispatch()
  24.             {
  25.             $controllerName = $this->Command->getControllerName();
  26.  
  27.             if($this->isController($controllerName) == false)
  28.                   {
  29.                   $controllerName = ‘error’;
  30.                   }
  31.             include(‘axial/controllers/’.$controllerName.‘/controller.’.$controllerName.‘.php’);
  32.             $controllerClass = $controllerName."Controller";
  33.             $controller = new $controllerClass($this->Command);
  34.             $controller->execute();
  35.             }
  36.       }

As you can see from the code above on line 31, the controller must reside in a directory under ‘axial/controllers/controllername and be named using the convention controller.controllername.php.

The Controller

All controllers must descend from the base class Axial_Controller. The execute() function in the base class searches for the function passed in the Axial_Command object prepended with a ‘_’ within the controller, if this method is found it is executed if it is not found the _error() function is called, if no function is specified the _default() function is called.

  1. class Axial_Controller
  2.       {
  3.       var $Command;
  4.       function Axial_Controller(&$command)
  5.             {
  6.             $this->Command = $command;
  7.             }
  8.  
  9.       function _default()
  10.             {
  11.  
  12.             }
  13.       function _error()
  14.             {
  15.  
  16.             }
  17.       function execute()
  18.             {
  19.             $functionToCall = $this->Command->getFunction();
  20.             if($this->Command->getFunction() == )
  21.                   {
  22.                   $functionToCall = ‘default’;
  23.                   }
  24.  
  25.             if(!is_callable(array(&$this,‘_’.$functionToCall)))
  26.                   {
  27.                   $functionToCall = ‘error’;
  28.                   }
  29.  
  30.  
  31.             call_user_func(array(&$this,‘_’.$functionToCall));
  32.             }
  33.       }

Try it out

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

Conclusion

If you look at the index.php from the sample code in the second part in the series you will see that the code is virtually unchanged from this version. The main change is in the Axial_CommandDispatcher class and with the addition fo the Axial_Controller class I have build a pluggable extendable front controller based framework. Take a look at the example page and code to get a feel for what you can do with this mini framework.

When I began this series I left out any discussion of the view portion of MVC since it was beyond the scope of the title and what I intended to discuss, I discovered though that in order to build any further on the front controller portion of the framework I would need to introduce that concept. So I’m going to stop this series here and will pick it up again later together with a more fitting title and topic.

28


About the Author

Discussion

  1. Jon  June 16, 2007

    Anyone know how to get CheckSpelling On to work with this URL routing?

  2. Jon  June 16, 2007

    I'm using URL routing to display a user's rss feeds. However, these feeds are physically stored in a separate directory.

    So when someones goes to say:
    http://site.com/users/jon/feed.rss

    The URL rounting parses the /users/ /jon/ and feed.rss and currently redirects to the physical location of the .rss feed.

    Is there a way to mask the URL switch so when someone goes to that .rss link it appears as if the folder and file do in fact reside in the that directory?

    Or perhaps I should read the .rss and output it?

    Thoughts?

  3. Doug Hill  June 20, 2007

    Jon,
    I think the most flexible solution would be to read the .rss and output it. Thats probably the way I would go. I suppose it all depends on the situation.

    If you are doing the redirect I'm assuming the feeds are pre-generated, why not just place the feeds in the physical directory you want the url to mimic and skip the redirect. If the physical location is /users/jon/feed.rss and you use this as the actual URL then the .htaccess is going to bypass the url routing anyway since the file exists.

  4. John  November 3, 2007

    Hi, How will i get parameter from default or any function?

    ie.

    1.  
    2. function _default(){
    3.  $id= intval($_GET["id"]);
    4.  echo"id $id";
    5. }
    6.  

    It doesn't work

  5. Doug Hill  November 4, 2007

    John:

    Look back at part two of the series. I'll post a longer reply later I'm a bit constrained for time today.

  6. jerkan  November 6, 2007

    what if you wanna show something from a controller and another thing from another controller?

  7. Doug Hill  November 9, 2007

    John:
    Here is a quick fix to the example.

    In Axial_UrlInterpreter change the line in the constructor from this:

    1.  
    2. $requestURI = explode('/', $_SERVER['REQUEST_URI']);
    3.  

    to this:

    1.  
    2. $tempRequestURI = str_replace('?'.$_SERVER['QUERY_STRING'],",$_SERVER['REQUEST_URI']);
    3. $requestURI = explode('/', $tempRequestURI);
    4.  

    This will remove the query string so routing works correctly and leave the $_GET variable intact.

    I have an entirely new version of code for this series, guess I should get to posting soon.

    Doug

  8. John  November 16, 2007

    Thanks Doug. I will try your code. Your tutorial is very help.
    I want use simple framework instead of larger framework such as phpcake or codeigniter.
    Keep up the good work.

    Regards

  9. John  November 16, 2007

    Hi Doug,

    I tried your above code and it's not working.

  10. Doug Hill  November 17, 2007

    John, could you drop me some code on email or here to show me what the problem is?

    Doug

  11. John  November 17, 2007

    Doug,
    I've downloaded files from from this tutorial then simply called it through browser http://localhost/mvc/test/dostuff/id/23/. I want to get id from this url.

    1.  
    2.  
    3.         function _error()
    4.             {
    5.             showErrorTemplate($this->Command);
    6.             }
    7.         function _dostuff()        
    8.             {
    9.              echo"id=$_GET[id]";
    10.              showTemplate($this->Command);
    11.             }
    12.  
    13. }
    14.  
  12. Doug Hill  November 17, 2007

    I think I see what the problem is. If you just want the parameters to the function from the url as formatted like your example http://localhost/mvc/test/dostuff/id/23/ you would use $this->Command->getParameters() to access them as an array instead of the $_GET[] variable.

    If you want to actually get them from $_GET[] you would have to format your URL like http://localhost/mvc/test/dostuff/?id=23 and use the changes to the code i posted before.

    function _dostuff()
        {
        $params =  $this->Command->getParameters();
        echo $params[0].': '.$params[1];
        showTemplate($this->Command);
        }
    
  13. John  November 17, 2007

    Thank you Doug. It would be great if we could get/post/request parameter value by calling its name.
    ie

    echo"$params[id]";

    http://localhost/mvc/test/dostuff/id/23/page/3/sort/true

    echo"$params[id],$params[page],$params[sort]";

    so, it will prints 23,3,true

    How did you managed to get url like this http://www.phpaddiction.com/tags/php/url-routing-with-php-part-three/

    Did you use MVC method or it is done using .htaccess ?

    Regards

  14. Doug Hill  November 18, 2007

    John if you look at the Axial_UrlInterpreter class you will see that you can modify how the url is transformed into a command object. Instead of the way I did it you could make it handle the parameters as key => value pairs… or any other way you want. If i get a little time this week i'll write a new article that covers this.

    The urls in the blog come from wordpress not my doing!

    Doug

  15. Richard  February 20, 2008

    Hi Doug, nice tutorial btw, really clarified MVC for me. I've been using on open source MVC framework and I'm looking to build my own now :) Anyway one thing that did demystify me in your tutorial is models – where are they? Could you illustrated within your sample code how you could implement one?

    Cheers

  16. Handy  April 11, 2008

    Thank you for the tutorial. Zend framework provides also a very good frontcontroller. So it might be a help to understand the functionality and build own frontcontroller.

  17. zsolt sandor  December 4, 2008

    Hi

    a nice tutorial, ad the first url router witch works on all servers / a least on those teht i tried /

    question:
    should be the exampleutils.php the model?

  18. vulo  October 5, 2009

    Hi, I have problem with the url router, as it reload 2 3 times before to load the page, of course it is too fast for a normal use to see it, but I include mail sending of every execution of the url-array.
    So

    I receive that these values are loaded into the url array before the actual data from the URL is loaded

    AC_RunActiveContent.js
    Scripts
    robots.txt
    /not the same every time/

    and then
    the actual URL parameter – 'event' or 'contacts'
    I am using some session messages, that's how i find this "bug".

    question:
    Has anyone have ides, why this is happening.
    Thanks in advance
    Vulo

  19. B@stian  May 25, 2011

    Hi,
    I am wondering what the '&' before $command means?
    for example you are using it here:
    function Axial_CommandDispatcher(&$command)
    and function Axial_Controller(&$command)

    Could you please explain it ?

    Thanks

  20. Doug Hill  June 8, 2011

    The & sign denotes a pass by reference variable.

    Doug

  21. sike  July 8, 2011

    Thank you very much for these articles and I do hope you continue the series -I have found them very useful

  22. Tai Sheppard  October 5, 2011

    This is exactly what I was looking for. I needed a dispatcher class that would pass url cruft as variable intact to the methods in the class.

    One thing that isn't correct though is that you need to test the class for the called method and then adjust the command parameters so that dispatcher doesn't mistake a parameter for the method.

    This way you can call the default method with parameters which you can't do with the script as you have it posted here.

    If you call /test/param1/param2/param3 for instance, the dispatcher will use param1 as your method. Simply test the controller for the method using method_exists() and if it fails, array_unshift() to add param1 to your existing parameters.

    Otherwise … KILLER job Doug, thanks a bunch.

    Tai

  23. Rudi  July 14, 2012

    Hi Doug,

    I just wanted to say thank you for the series on this topic. I have been scouring the internet to find something more in-depth on re writing and a re write engine and this is the closest thing I could find and it has given me a great starting point.

    I will be using this on my project shorty https://github.com/drpain/baseline.

    Thanks once again!

  24. Robert  August 21, 2012

    I'm working on my own framework which does the routing but I'm having issues with setting 404 headers. It will work on the requested page but if the page is valid and the css files are not the css files will show as 200 ok and not 404. Can you do a 4th part that will do proper 404 headers for files?

  25. Luis  September 5, 2012

    So, There's no way to remove "/root/" from the URL string, unless I'm calling the default controller and no function? What if i have another function that has to be executed inside "root"? The only way I have found to eliminate "root" from the URL string is this:

    myserver.com/router//listdata/13/26 (it shows a series of numbers from 13 to 26)

    See the double slash between "router" and "listdata"? That makes me nervous. What should I do to eliminate this ugliness?

    Thanks for sharing

  26. wilfredo  June 15, 2014

    Dude this opened my mind, thx a lot for the tutorial, this helped me to build my own clases, router and controllers, now ill try to build models with the same concept. Also you need to put links on your tutorials, in the first part i was lost, there is not a "click here for next part", and in the secction "related post" was empty. i have to search in all the web page, or change the url "part-one" to "part-two"

  27. click this link now  July 18, 2014

    Hi all, here every person is sharing these experience, so it's pleasant to read this web site, and I used to go to see this webpage daily.

  28. best kitesurfing kites 2014  July 23, 2014

    Thanks for finally writing about >Url Routing with PHP – Part Three ? phpaddiction <Liked it!

Add a Comment