Url Routing with PHP - Part One
Most PHP frameworks use some variation of the front controller pattern to centralize common code and logic. There are advantages and disadvantages to this. I am going to ignore those for now. In fact the first part of this series will explore a simple procedural URL routing method that contains many of the disadvantages. In later articles we will build upon this basis and address the disadvantages.
Requirements
- PHP
- Apache with mod_rewrite enabled.
Redirecting with mod_rewrite
This article doesn't discuss mod_rewrite and the .htaccess in detail, the following .htaccess file is sufficient for the needs of our simple front controller. The .htaccess file should be placed in the same directory as the front controller script. All requests will be sent to index.php while requests for a file or directory that does exist will bypass the mod_rewrite rules and be served directly by the web server.
file: .htaccess
-
Options +FollowSymLinks
-
IndexIgnore */*
-
# Turn on the RewriteEngine
-
RewriteEngine On
-
# Rules
-
RewriteCond %{REQUEST_FILENAME} !-f
-
RewriteCond %{REQUEST_FILENAME} !-d
-
RewriteRule . index.php
The Entry Point
I chose a simple format for the incoming URL, a command followed by its parameters separated by forward slashes "/".
For example:
www.example.com/command/parameter1/parameter2/
in this example $requestURI would contain.
This works fine except in the case where the front controller is not in the root directory.
For example:
www.example.com/myapps/app1/command/parameter1/parameter2/
in this case $requestURI would contain.
-
Array ( [0] => [1] => myapps [2] => app1 [3] => command [4] => parameter1 [5] => parameter2 [6] => )
To filter out the path to the front controller we will need to use the $_SERVER['SCRIPT_NAME'] variable.
In the above example $scriptName would contain.
Array ( [0] => [1] => myapps[2] => app1 [3] => index.php )
Which can be used to remove the path and script name from the URI.
Note: As you can see the explode method leaves an empty array member at the first position. This will be fixed by the following code.
-
-
{
-
if ($requestURI[$i] == $scriptName[$i])
-
{
-
}
-
}
-
After the code above $command would contain.
Array ( [0] => command [1] => parameter1 [2] => parameter2 [3] => )
Dispatching the command
Now that we have a command with its parameters stored in an array it is trivial to handle them via a switch statement. The following is a short example.
-
switch($command[0])
-
{
-
-
case 'commandOne' :
-
break;
-
-
case 'commandTwo' :
-
break;
-
-
default:
-
echo 'That command does not exist.';
-
break;
-
}
Try it out
You can visit examples.phpaddiction.com/urlrouter/part_1/ to see it in action and download the sample code.
Next up
The next part in the series will focus on moving the URL router into a class and out of the global namespace.
Update I've updated the .htaccess per the comment made by DrBacchus.
Comments
21 Responses to “Url Routing with PHP - Part One”
Leave a Reply
that's sweet ! Can't wait to read part-2
Oi Friend,
for script to function correctly is necessary to add the following line before "FOR"
— LINE TO ADD —-
$scriptNameArray = array_values($scriptName);
— LINE TO ADD —-
for($i= 0; $i
Copy and paste seems to have bit me! I've fixed the code now Diego. Thanks for the catch.
Doug
For users of PHP 4.3.0+ I believe lines 4-10 in the second source code example can be simplified by making use of the array_diff_assoc function like so:
$command = array_diff_assoc($requestURI, $scriptName);
Untested, so I could be wrong, but just a thought. Nice tutorial!
array_diff_assoc() may work I haven't tried it either. I picked the longer for loop to make what was happening in the sample code clearer. I'll try it out though and see.
Update:
$commandArray = array_diff_assoc($requestURI,$scriptName);
$commandArray = array_values($commandArray);
The above works fine. The article isn't really about how you parse the URLs though, off hand I can think of dozens of schemes. The point is actually being able to translate some arbitrary format into a command array.. or in later articles a command object.
Your rewrite conditions are slightly inaccurate:
RewriteCond %{REQUEST_FILENAME} !-f [OR]
RewriteCond %{REQUEST_FILENAME} !-d
Because this is an OR condition, the rewrite rule will always be run. (A OR B) is TRUE when either A or B is true. Any argument will always be either (not a file) or (not a directory). You need to remove the [OR] there, otherwise a request for index.php will cause a loop.
What you want is "if it's not a file AND it's not a directory":
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
You are correct, I need to edit the article, in later versions of the sample code I removed the [OR].
How do you use mod_rewrite on the sam server? Thank you!
Why not use something simple like:
substr($_SERVER['REQUEST_URI'], strlen($_SERVER['SCRIPT_NAME']));
to distinguish between the script name and the parameters, then explode on that?
Just an idea.
You might also want to filter out multiple slashes in anticipation of exploding on the '/' as your separator.
$rawParams = preg_replace('@[\/]{2,}@', '/', $rawParams);
It's simply awesome !
Ty
What is the advantage to doing all this? Why not have the PHP script parse the path instead of Apache? Why learn two (possibly conflicting) languages?
I'm not sure I understand your question, The point of the artice is to have the PHP script parse the path.
re: What is the advantage to doing all this?
If you're referring to the usage of mod_rewrite, the point is (IMHO) to hide index.php from the URL so that instead of http://example.com/index.php?action=foo/bar or http://example.com/?action=foo/bar you can use http://example.com/foo/bar
If you're looking for motivation, read http://www.w3.org/Provider/Style/URI
With this method, is it possible to get error404 message at all for non-existent 'pages'?
Hi,
nice tutorial you wrote here. I built a similar class for PHP, that works fine. But now, there's a problem:
I'd like to route urls like the MediaWiki uses. That means urls like http://localhost/user:Test/param1/param2 But when I call these addresses i receive the message (from Apache) that the access has been denied. Do you know any solutions to this problem?
Thanks for the tutorial and greetings
Nice and brilliant script.
So simple.
ANother helpful url is found here.
Routing in php
Php routing
Hi guys,
I'm newbie to all this stuff. I am wondering if i could do the following WITHOUT CHANGING THE URL
http://example.com/foo/bar
to call
http://example.com/foo?article_name="bar"
Thanks in advance
Hi all,
I am wonder whether your Rewrite Rull works with Apache on Window plateform or not.
Here's My Version!
.htaccess
RewriteEngine on
RewriteCond $1 !^(index\.php|img|css|js)
RewriteRule ^(.*)$ /index.php/$1 [L]
.php
abstract class URI {
protected $pathArray = array();
protected $path;
public function __construct() {
global $HTTP_SERVER_VARS;
$this->pathArray = explode("/",$HTTP_SERVER_VARS["REQUEST_URI"]);
}
public function getPathByNumber($pathNumber){
if (substr($this->pathArray[1], -4, 4) == '.php') {
$this->path = mysql_escape_string($this->pathArray[$pathNumber + 1]);
}else {
$this->path = mysql_escape_string($this->pathArray[$pathNumber]);
}
return $this->path;
}
} //ends URI class
Thanks for your tutorial.