Page 1 of 1

A very simple url manager/rewriter/router

Posted: Wed Feb 13, 2013 4:23 pm
by s.dot
This fit my needs perfectly and is very simple and allows for a great amount of flexibility. This will allow me to write any amount of pretty url's with unlimited depth while maintaining a single directory structure for my files.

Example URL: example.com/foo-bar/ would use /foo-bar.php
Example URL: example.com/foo-bar/string/ would use /foo-bar_string.php
Example URL: example.com/foo-bar/string/subsection would use /foo-bar_string_subsection.php
Example URL: example.com/a/b/c/d/e/f/g/h/i would use /a_b_c_d_e_f_g_h_i.php

This file should be used as index.php.

The following .htaccess rules should be used (thanks to requinix @ forums.devnetwork.net)
[text]RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^/?(.*) index.php [L][/text]


$_GET and $_POST will still be abailable in their usual manner

Code: Select all

<?php

/**
 * A very simple URL manager/rewriter/router, set this file as index.php
 * With .htaccess rule, allows any length of "pretty URL's" to be written and 
 * funneled to this index.php and routed to their actual scripts.
 *
 * For example, a URL of /foo-bar/ would be directed to /foo-bar.php
 * For example, a URL of /foo-bar/example/ would be directed to 
 *     /foo-bar_example.php
 * For example a URL of /foo-bar/example/detail/subsection/ would be directed 
 *     to /foo-bar_example_detail_subscection.php
 *
 * The .htacess file should look like this: (thanks to requinix @ forums.devnetwork.net)
 * 
 * RewriteEngine On
 * RewriteCond %{REQUEST_FILENAME} !-f
 * RewriteCond %{REQUEST_FILENAME} !-d
 * RewriteRule ^/?(.*) index.php [L]
 *
 * @file	index.php
 * @author	Scott Martin <sjm.dev1 -[at]-gmail-[dot]- com>
 * @date	Feb 13th, 2013
 * @package	None
 */
 
if (!empty($_SERVER['REQUEST_URI']))
{
	//to avoid recursion, actual index content must be in separate script
	if ($_SERVER['REQUEST_URI'] == '/' || $_SERVER['REQUEST_URI'] == '/index')
	{
		require_once 'index_content.php';
		exit;
	}
	
	//request uri will be available and stored in $fileName
	$fileName = $_SERVER['REQUEST_URI'];
	
	//get rid of the query string if present (it is still available in $_GET)
	if (strpos($fileName, '?') !== false)
	{
		$fileName = strtok($fileName, '?');
	}
	
	//replace beginning and trailing slashes
	$fileName = trim($fileName, '/');
	
	//replace slashes with underscores, and lowercase
	$fileName = strtolower(str_replace('/', '_', $fileName));
	
	//check for appropriate characters and file exists
	if (preg_match('#^[a-zA-Z0-9_-]+$#', $fileName) && file_exists($_SERVER['DOCUMENT_ROOT'] . '/' . $fileName . '.php'))
	{
		require_once $_SERVER['DOCUMENT_ROOT'] . '/' . $fileName . '.php';
		exit;
	}
}

//if we reach this point, we send a 404 header
header('HTTP/1.0 404 Not Found');
require_once '404.php';
exit;

Re: A very simple url manager/rewriter/router

Posted: Wed Feb 13, 2013 4:53 pm
by Christopher
s.dot wrote:Example URL: example.com/a/b/c/d/e/f/g/h/i would use /a_b_c_d_e_f_g_h_i.php
This part confuses me. Why not just have the URL example.com/a_b_c_d_e_f_g_h_i and use the / for actual directory separators? Seems confusing. Or reverse it and use pear style naming so example.com/a_b_c_d_e_f_g_h_i would load /a/b/c/d/e/f/g/h/i.php?

Re: A very simple url manager/rewriter/router

Posted: Wed Feb 13, 2013 5:06 pm
by s.dot
I am doing a quick just for fun site. I just wanted a single directory for the files, and a way to map the structured url to the file it would use. /Category/Pagename would look much better than /category_pagename and allows me to maintain a pseudo directory structure.

Works for my purposes, even if it's not standard practices.

Re: A very simple url manager/rewriter/router

Posted: Wed Feb 13, 2013 6:29 pm
by twinedev
There is a problem with your preg_match in that it is checking for a single instance of the class of [a-zA-Z0-9_-] to match ANYWHERE in $filename.

You should do:

Code: Select all

preg_match('/^[a-z0-9_-]+$/i', $fileName)
which says match one or more of the class, anchored to both the start and end of the string.

Re: A very simple url manager/rewriter/router

Posted: Wed Feb 13, 2013 6:35 pm
by s.dot
Good catch!

I wanted to use ctype_alnum but it won't allow for _ and -. I don't like using a regex there. I'll update the main post now.

Re: A very simple url manager/rewriter/router

Posted: Wed Feb 13, 2013 11:33 pm
by Christopher
Here is one I have used in the past, but it uses PATH_INFO instead. I modified it to look/work more like yours and added the / - translation you do.

Code: Select all

$ConfigArray = array(
	'path' => dirname(__FILE__) . '/pages/',
	'index' => 'index_content',
	'error' => '404',
	);

$fileName = isset($_SERVER['PATH_INFO']) ? trim($_SERVER['PATH_INFO'], '/.') : '';
if ($fileName == '') {
	$fileName = $ConfigArray['index'];
}

//replace slashes with underscores, and lowercase
$fileName = strtolower(str_replace('/', '_', $fileName));

//check for appropriate characters
if (preg_match('/^[a-zA-Z0-9_-]*$/', $fileName)) {
	//check if file exists
	if (!file_exists($ConfigArray['path'] . $fileName . '.php')) {
		$error = 2;		// route does not exist
		$fileName = $ConfigArray['error'];
	}
} else {
	$error = 1;			// illegal route name
	$fileName = $ConfigArray['error'];
}
require_once $ConfigArray['path'] . $fileName . '.php';
This is more of a Front Includer rather than a Front Controller that dispatches Action Controllers. I think it would probably double the size of the code to support standard /class/method/ style routes that loaded and instantiated a class and called a method.

Re: A very simple url manager/rewriter/router

Posted: Mon Feb 25, 2013 3:26 pm
by s.dot
Nice Chris. :)

Today, after i left my computer on for a few days, I went to go browse my website and everything went to the index page. I did a print_r() on $_SERVER and every page was REQUEST_URI = '/'.

Then my browser crashed.

I restarted it and then it started working fine again. Was this just something screwy with my browser, or is request_uri unreliable?

Re: A very simple url manager/rewriter/router

Posted: Mon Feb 25, 2013 6:37 pm
by Christopher
s.dot wrote:or is request_uri unreliable?
I don't remember. I know you need to be careful of values in $_SERVER because some come from the user via Apache/webserver without much validation/filtering. Those values cannot be fully trusted, but I don't know if they can get corrupted.

Re: A very simple url manager/rewriter/router

Posted: Wed Feb 27, 2013 4:05 pm
by s.dot
Corruption wouldn't matter in this case because it would just send a 404 based on the rewriter script. But as long as REQUEST_URI is always set I'm good. I know the value comes from the server, and has seemed to be set in every instance except this one where the browser crashed.

Re: A very simple url manager/rewriter/router

Posted: Wed Feb 27, 2013 9:54 pm
by Christopher
The code I posted does an isset() check on the $_SERVER value first. You should probably do that as well.