Thanks to djahandarie for pointing out a code error: PHP_SELF has been replaced with REQUEST_URI.
While I may not be a huge fan of Code Igniter, their approach to URLs is absolutely incredible. I honestly never knew before I started using it that it was even possible to append a bunch of stuff after a filename in the URL without using the question mark. For those of you who don’t know what I’m talking about, these two URLs will load the same file:
http://www.fugitivethought.com/index.php
http://www.fugitivethought.com/index.php/foo/bar
If you don’t believe me, try it out right now! What’s so great about this? Well first of all, it (at least mildly) looks like you are browsing folders. There are no ampersands or question marks mucking up the URL. The second cool thing is that a very simple mod_rewrite rule allows you to remove the index.php from there, to create a very pretty URL that would look like this:
http://www.fugitivethought.com/foo/bar
The .htaccess file that will allow this is taken directly from the Code Igniter user guide (http://codeigniter.com/user_guide/general/urls.html):
RewriteEngine on
RewriteCond $1 !^(index\.php|images|robots\.txt)
RewriteRule ^(.*)$ /index.php/$1 [L]
If you don’t get the importance of pretty, human interpretable URLs then you can read more about it here:http://www.aardvarkmedia.co.uk/about/articles/007.html
Now in order to make this approach to URLs useful we need a parser inside of index.php with a very simple map that will allow us to tell it what class and function to call based on the different parts of the URL. While looking to implement the best parts of Code Igniter for a personal project, I wrote a script file that does exactly this.
Basically, you keep a set of “Controller” classes inside of a directory named Controllers. You can change the name of this directory by editing line 34 in the code below (35 in the downloaded file). The map variable (lines 11 to 18) is simply a mapping of what class and function to call based on the first part of the URL (in our above entries, this would be the “foo” portion). If there is no entry on the list to match this first step, then it will use the entry labeled “default”. For each entry it calls, it pops the top entry (first entry) off the stack of ‘/’ separated portions of the URL and passes the rest of them to the function. So if we visit http://hostname/foo/bar/cat, the function will figure out which function to call for “foo”, and then call it passing an array containing “bar” and “cat”. Any further redirecting of the URL can be done inside the function that is called.
- // Define the path root for the application.
- // Example: A site rooted at http://foo.com/bar/index.php would put bar/index.php here
- define(‘_PAGE_ROOT_’, ‘bar/index.php’);
- // Parse the URL
- $uri = substr($_SERVER[‘REQUEST_URI’], strlen(_PAGE_ROOT_) + 2);
- $pgs = explode(‘/’, $uri);
- // Describe the URI map, further mapping can be done at each controller
- // inside of the map method.
- // default – load views.php and the Views class and call the home() method
- $map[‘default’] = array(‘views’, ‘home’);
- $map[‘year’] = array(‘views’, ‘year’);
- $map[‘month’] = array(‘views’, ‘month’);
- // note the use of “Map” here
- $map[‘control’] = array(‘control’, ‘map’);
- $map[‘request’] = array(‘actions’, ‘request’);
- $map[‘csv’] = array(‘feeds’, ‘csv’);
- // Map the first section of the URI to a controller and action
- if ( isset($map[$pgs[0]]) ) {
- $action = $map[$pgs[0]];
- array_shift($pgs);
- } else {
- $action = $map[‘default’];
- }
- // Cleaner format
- $controller_class = ucfirst($action[0]);
- $controller_action = $action[1];
- $controller_params =& $pgs;
- // Load the controller
- require ‘Controllers/’.$action[0].‘.php’;
- // Instantiate the controller
- $controller = new $controller_class;
- // Call the action
- $controller->$controller_action($controller_params);
or you can click here to download a local copy for perusal.
In the example code in the provided file, the map calls a function named “map” in the “control” class if the first string is “control”. The map function in the control class looks at the next entry in the stack of URL nibblets and calls a particular function for that. For example, it could call the “bar” method of the control class and pass it whatever it deems necessary.
One major advantage of using this over the Code Igniter code is that I am not artificially blocking using GET in the URL. If you want to have a URL like:
http://hostname/foo/bar/?blah=moo&moreblah=moremoo
then you are perfectly welcome to do so. The controller functions will be called always and be able to use the built in $_GET global in PHP as any normal PHP application would. And you still get to have pretty URLs.