I have learned how to build a Model-View-Controller and Object-Relational-Mapping, now I learned how to build a simple and effective URL Routing System. I should now have the confidence to rewrite RockForums.Co without the use of symfony or any other PHP Framework. Beside PHP is an excellent framework in itself.
Here the PHP code for the Simple & Effective URL Routing System.
class route {
static private $ROUTES;
static public function init() {
$path = false;
if (isset($_SERVER['PATH_INFO'])) {
$path = $_SERVER['PATH_INFO'];
} else {
$path = $_SERVER['REQUEST_URI'];
$self = $_SERVER['PHP_SELF'];
$self = dirname($self);
$self = str_replace('\\', '/', $self); // for Windows Compatibility.
$self = strlen($self);
$path = substr($path, $self);
if ($path != '') {
$thehash = strpos($path, '#');
if ($thehash) {
$path = substr($path, 0, $thehash);
}
$question = strpos($path, '?');
if ($question) {
$path = substr($path, 0, $question);
}
} else {
$path = false;
}
}
if (!$path) {
$routes = array('index', 'index');
} else {
$path = trim($path);
$path = trim($path, "/");
$routes = strtolower($path);
$routes = explode('/', $routes);
}
// Useful for pagenationing an index.
if (is_numeric($routes[0])) {
$tempRoutes = array('index', 'index');
foreach ($routes as $route) {
$tempRoutes[] = $route;
}
unset($route);
$routes = $tempRoutes;
unset($tempRoutes);
}
if (class_exists($routes[0] . '_action')) {
if (!isset($routes[1])) {
$routes[1] = 'index';
}
if (method_exists($routes[0] . '_action', $routes[1])) {
self::$ROUTES = $routes;
} else {
$altRoutes = array($routes[0], 'index');
$count = 1;
while (isset($routes[$count])) {
$altRoutes[] = $routes[$count];
$count++;
}
self::$ROUTES = $altRoutes;
}
call_user_func(array(self::$ROUTES[0] . '_action',
self::$ROUTES[1]));
return;
} elseif (class_exists('index_action')) {
if (method_exists('index_action', $routes[0])) {
$altRoutes = array('index', $routes[0]);
$count = 1;
} else {
$altRoutes = array('index', 'index');
$count = 0;
}
while (isset($routes[$count])) {
$altRoutes[] = $routes[$count];
$count++;
}
self::$ROUTES = $altRoutes;
call_user_func(array(self::$ROUTES[0] . '_action',
self::$ROUTES[1]));
return;
}
page::show404();
}
static public function getRoutes() {
return self::$ROUTES;
}
}
class index_action {
static public function index() {
echo 'Index, Index';
$route = route::getRoutes();
if (isset($route[2])) {
echo ', ' . $route[2];
}
}
static public function testing() {
echo 'Index, Testing';
$route = route::getRoutes();
if (isset($route[2])) {
echo ', ' . $route[2];
}
}
}
class test_action {
static public function index() {
echo 'Test, Index';
$route = route::getRoutes();
if (isset($route[2])) {
echo ', ' . $route[2];
}
}
static public function test() {
echo 'Test, Test';
$route = route::getRoutes();
if (isset($route[2])) {
echo ', ' . $route[2];
}
}
}
route::init();
The class name is in 0, the method name is in 1, 2 and above are the parameters. The benefit of writing a URL Routing System in PHP rather than writing it purely in .htaccess (Apache) or web.config (IIS7) is cross compatibly with Apache and IIS, and probably with some other web servers.
Code for Apache .htaccess
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -f [NC,OR]
RewriteCond %{REQUEST_FILENAME} -d [NC]
RewriteRule .* - [L]
RewriteRule ^(.*)$ index.php/$1 [QSA,L]
</IfModule>
Code for IIS7 web.config
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Imported Rule 3" stopProcessing="true">
<match url=".*" ignoreCase="false" />
<conditions logicalGrouping="MatchAny">
<add input="{REQUEST_FILENAME}" matchType="IsFile" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" />
</conditions>
<action type="None" />
</rule>
<rule name="Imported Rule 4" stopProcessing="true">
<match url="^(.*)$" ignoreCase="false" />
<action type="Rewrite" url="index.php/{R:1}" appendQueryString="true" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
.htaccess and web.config are used as pointers to the file.
One more thing if you’re planning on using URL slug, always prefix it with a dash (-) to avoid problems.
Let me know what you think?
Update: I have added rtrim and other improvement to the script, e.g. if a class is not detected it will try to detect it as a method in the index class, if that fails it will use the index method.
Update 2: Now uses trim rather than rtrim, works better.
Update 3: Found out that $_SERVER['PATH_INFO'] does not seem to work with mod_rewrite on some servers, but can be emulated in combination with $_SERVER['REQUEST_URI'] and $_SERVER['PHP_SELF'].