PHP, PATH_INFO and url-friendly site
April 9th, 2006
These days I had to redesign a company’s site layout, due to hosting services upgrades. Before, they were simple static HTML pages: now, they are (not really, they are about to be…) dynamic generated ones. I cannot use mod_rewrite since the hoster disabled it due to security issues (?).
So I had to look for other ways to get a URL-friendly site. The answer came from the phpinfo(); function. I looked at the generated page if there were some parameter that well fits to have a URL friendly site. So I discovered PATH_INFO.
I’m using PHP since a long time but I never heard such a feature, so I had to learn something new.
We can have, in this way, a URL like this: http://www.site.com/page.php/section/subsection/subsubsection that can easily become using apache .htaccess (if the hoster supports it, obviously) like this: http://www.site.com/page/section/subsection/subsubsection, hence by removing the .php extension and by make the php page as another subdirectory (I’ll describe how to do this later).
So let start looking at how PATH_INFO works.
First of all, let create a page called page.php that will handle the parameter passing. Now let suppose that we are passing a URL like this: /page.php/section/subsection. We will found in PATH_INFO something like this: /section/subsection. Hence, suppose we want to show the file named “section_subsection.php” in the pages/ directory. Simply, a $array = explode('/', $_SERVER['PATH_INFO']); will give us “section” into $array[0] and “subsection” into $array[1]. Now you just need to concatenate the two array elements and adding .php (or whatever you want) extension and, after checking the file existance, you’ll have the work done.
Thats the code I used in order to achieve such effect (the code that’s in production, I am still working on it so it may have some error):
<?php
require(dirname(__FILE__) . "/config.php");
require(dirname(__FILE__) . "/functions.php");
$exclude_list = array("img", "frontpage");
if (!isset($_SERVER['PATH_INFO']))
header("Location: {$_CONFIG['url']}{$_CONFIG['pager']}/home");
else $tpath = $_SERVER['PATH_INFO'];
$tpath = explode('/', $tpath);
$path = $tpath[1];
if (isset($tpath[2]))
$subpath = $tpath[2];
else $subpath = "";
$exclude = FALSE;
foreach($exclude_list as $exc)
if ($exc == $path)
$exclude = TRUE;
if (!$exclude) {
if ((count($tpath) > 3) || (!file_exists(dirname(__FILE__) . "/pages/" . $path . ".php")))
$page = "/pages/404.php";
else {
// if $subpath exists, we have /path/subpath, traslated as path_subpath.php
if ($subpath !== "")
$page = "/pages/" . $path . "_" . $subpath . ".php";
else $page = "/pages/$path.php";
}
ob_start("ob_gzhandler");
require(dirname(__FILE__) . "/head.php");
require(dirname(__FILE__) . "/title.php");
require(dirname(__FILE__) . "/menu.php");
echo "<div id="content">n";
require(dirname(__FILE__) . $page);
echo "</div> <!--/content -->n";
require(dirname(__FILE__) . "/footer.php");
ob_end_flush();
} else {
$fullpath = implode('/', $tpath);
$fp = fopen(dirname(__FILE__) . $fullpath, "r");
$contents = fread($fp, filesize(dirname(__FILE__) . $fullpath));
echo $contents;
}
?>
Let analize it. Firstly, why do we have an $exclude_list? This is needed because with this way of URL handling, every file that we will request under the site path will pass from that script. So, since we want to display images and other things, just take the file as they are and print them out if such path is in the exclude list.
I used a couple of $_CONFIG values (that I define in config.php) to have a centralized configuration system. Specifically, $_CONFIG['url'] will contain the site’s URL, while $_CONFIG['pager'] will contain the filename of the script that will handle the PATH_INFO data, in our case page.php.
Successively, there’s a check about the file existence into the /pages directory. The rest is just the use of the buffered output since I sometimes use a header() call in the other pages both to redirect the user to some other page and to set the right errorcode for a 404 page.
Of course there are some improvements that could be done. Probably, the most important limitation in the script now is that it has a limit of two subsections (like /section/subsection). This limit could be expanded by doing something like this: implode('_', explode('/', $tpath)); but will need more checks for consistency.
Another thing: as I said before, you could want to delete the .php suffix on the page.php. This can be done by putting this lines in the .htaccess:
<files page>
ForceType application/x-httpd-php
</files>
In this way we will force apache to call page.php if it is called as page only.
That’s all :) I have to make shorter posts, I know.
That’s a nice post. Thanks a lot.
Comment by pcdinh — October 7th, 2006 at 15:16