URL rewriting with PHP URL rewriting with PHP php php

URL rewriting with PHP


You can essentially do this 2 ways:

The .htaccess route with mod_rewrite

Add a file called .htaccess in your root folder, and add something like this:

RewriteEngine onRewriteRule ^/?Some-text-goes-here/([0-9]+)$ /picture.php?id=$1

This will tell Apache to enable mod_rewrite for this folder, and if it gets asked a URL matching the regular expression it rewrites it internally to what you want, without the end user seeing it. Easy, but inflexible, so if you need more power:

The PHP route

Put the following in your .htaccess instead: (note the leading slash)

FallbackResource /index.php

This will tell it to run your index.php for all files it cannot normally find in your site. In there you can then for example:

$path = ltrim($_SERVER['REQUEST_URI'], '/');    // Trim leading slash(es)$elements = explode('/', $path);                // Split path on slashesif(empty($elements[0])) {                       // No path elements means home    ShowHomepage();} else switch(array_shift($elements))             // Pop off first item and switch{    case 'Some-text-goes-here':        ShowPicture($elements); // passes rest of parameters to internal function        break;    case 'more':        ...    default:        header('HTTP/1.1 404 Not Found');        Show404Error();}

This is how big sites and CMS-systems do it, because it allows far more flexibility in parsing URLs, config and database dependent URLs etc. For sporadic usage the hardcoded rewrite rules in .htaccess will do fine though.


If you only want to change the route for picture.php then adding rewrite rule in .htaccess will serve your needs, but, if you want the URL rewriting as in Wordpress then PHP is the way. Here is simple example to begin with.

Folder structure

There are two files that are needed in the root folder, .htaccess and index.php, and it would be good to place the rest of the .php files in separate folder, like inc/.

root/  inc/  .htaccess  index.php

.htaccess

RewriteEngine OnRewriteRule ^inc/.*$ index.phpRewriteCond %{REQUEST_FILENAME} !-fRewriteRule ^(.*)$ index.php [QSA,L]

This file has four directives:

  1. RewriteEngine - enable the rewriting engine
  2. RewriteRule - deny access to all files in inc/ folder, redirect any call to that folder to index.php
  3. RewriteCond - allow direct access to all other files ( like images, css or scripts )
  4. RewriteRule - redirect anything else to index.php

index.php

Because everything is now redirected to index.php, there will be determined if the url is correct, all parameters are present, and if the type of parameters are correct.

To test the url we need to have a set of rules, and the best tool for that is a regular expression. By using regular expressions we will kill two flies with one blow. Url, to pass this test must have all the required parameters that are tested on allowed characters. Here are some examples of rules.

$rules = array(     'picture'   => "/picture/(?'text'[^/]+)/(?'id'\d+)",    // '/picture/some-text/51'    'album'     => "/album/(?'album'[\w\-]+)",              // '/album/album-slug'    'category'  => "/category/(?'category'[\w\-]+)",        // '/category/category-slug'    'page'      => "/page/(?'page'about|contact)",          // '/page/about', '/page/contact'    'post'      => "/(?'post'[\w\-]+)",                     // '/post-slug'    'home'      => "/"                                      // '/');

Next is to prepare the request uri.

$uri = rtrim( dirname($_SERVER["SCRIPT_NAME"]), '/' );$uri = '/' . trim( str_replace( $uri, '', $_SERVER['REQUEST_URI'] ), '/' );$uri = urldecode( $uri );

Now that we have the request uri, the final step is to test uri on regular expression rules.

foreach ( $rules as $action => $rule ) {    if ( preg_match( '~^'.$rule.'$~i', $uri, $params ) ) {        /* now you know the action and parameters so you can          * include appropriate template file ( or proceed in some other way )         */    }}

Successful match will, since we use named subpatterns in regex, fill the $params array almost the same as PHP fills the $_GET array. However, when using a dynamic url, $_GET array is populated without any checks of the parameters.

    /picture/some+text/51    Array    (        [0] => /picture/some text/51        [text] => some text        [1] => some text        [id] => 51        [2] => 51    )    picture.php?text=some+text&id=51    Array    (        [text] => some text        [id] => 51    )

These few lines of code and a basic knowing of regular expressions is enough to start building a solid routing system.

Complete source

define( 'INCLUDE_DIR', dirname( __FILE__ ) . '/inc/' );$rules = array(     'picture'   => "/picture/(?'text'[^/]+)/(?'id'\d+)",    // '/picture/some-text/51'    'album'     => "/album/(?'album'[\w\-]+)",              // '/album/album-slug'    'category'  => "/category/(?'category'[\w\-]+)",        // '/category/category-slug'    'page'      => "/page/(?'page'about|contact)",          // '/page/about', '/page/contact'    'post'      => "/(?'post'[\w\-]+)",                     // '/post-slug'    'home'      => "/"                                      // '/');$uri = rtrim( dirname($_SERVER["SCRIPT_NAME"]), '/' );$uri = '/' . trim( str_replace( $uri, '', $_SERVER['REQUEST_URI'] ), '/' );$uri = urldecode( $uri );foreach ( $rules as $action => $rule ) {    if ( preg_match( '~^'.$rule.'$~i', $uri, $params ) ) {        /* now you know the action and parameters so you can          * include appropriate template file ( or proceed in some other way )         */        include( INCLUDE_DIR . $action . '.php' );        // exit to avoid the 404 message         exit();    }}// nothing is found so handle the 404 errorinclude( INCLUDE_DIR . '404.php' );