A simple Router in PHP

Post all your tuts or request for tuts here.
Post Reply
Xaleph
Posts: 897
Joined: Mon Feb 07, 2011 2:55 am

A simple Router in PHP

Post by Xaleph »

Dispite the time, i did want to add a little tutorial for rerouting URL`s. It`s fairly simple in usage as well as reproducing it.

The default routes ( as symlinks are ) are nothing more then simple header redirects to pages right? /page.php redirects to.. .. page.php, geez, Xaleph, we didn`t know that! Well, they do! Anyhow, what if you wanted to strip away the .php? Hmm ok, now it`s getting a little tougher right?

What if you wanted something like: game/city-view/workshop/buy .. Seems hard to get something like this set up. Well, fortunatly, it`s not :)

This tutorial will cover some basic .htaccess and some basic PHP understanding. And, since this is something i miss around here, some OO. OOoohh, what`s OO? OO stands for Object Orientation. Don`t get yourself confused right now, i will try and explain along the way.

Before i start, i want to make clear that , in order for this to work, you need to meet some requirements. These are:
Have PHP installed ( preferably PHP 5.3 > )
Have an apache server with the module mod_rewrite ( this is key!! )

Ok, enough talk, time to get some code`n done!

First of, lets start by creatin the .htaccess file:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d [OR]
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]

Basicly, what this does is tell the apache server to listen what is being said here. The first statement: rewriteEngine On means you are turning on the mod_rewrite module, which we need to execute the other lines.

The next four lines are simple, each line is specifically checking if the current REQUEST is NOT a file, dir etc. If this is the case ( meaning, it`s not a file, dir, executable, what have you ) you can go to line 5.

Line 5 is a simple redirect to the root of your document. Which in most cases is the root where this file is being placed. If you have directory structure which consists of something like: C:/xampp/htdocs/ and you would have placed the .htacess file right in the htdocs, then all requests which did not meet the conditions, will be relocated to the root. Root is seen as the dot.

The last line is what`s doing the magic. Instead of routing only the root, we finally tell it route to the index.php, which is being stripped away, which is normal. But we explicitly tell it to.

So, now we have a simple handler for all requests to our index.php. You can still access all your directory`s and files, try it out.

If you verified this, we can move on :)

The next step: creating a class to handle our requests!

As you may have noticed, i used the word class. Which is probably uncommon for some people here, so i will elaborate. A class is a blueprint for a set of functions. You can group functions which all relate to one another, together. Which is called a class. There are different kinds of classes available, but for now, we`ll use the normal class. Now that you somewhat know what a class is, i`ll tell you a little secret: a class is the blueprint for an object! A default class will allways be an object, meaning you can access all functions inside an object by reference. Uhh, reference? That`s right!

Anyway, i will write a follow up tutorial on OO i suppose. Anyway, lets move on:

the Router.class.php:

Code: Select all

<?php

// The class followed by the name of the class, Router in our case.. :)
class Router
{
    // This is the default constructor, which means, this function will ALWAYS execute if you create 
    // a new object from this class, there are some exceptions, but i will not go in to that now
    public function Router ()
    {
         echo "Hello, this is a router object!";   
    }

}
Ok, now that you have seen the basic layout for a class, we can start writing in some code, yeah!

For the constructor, which always executes, we want to do some stuff right? We need to decide on using a syntax for our router.
The best way is to simply use the / as seperator. It`s easy and everyone knows it.

So here we go:
Router.class.php

Code: Select all

   <?php


class Router
{
    private $index;
    private $root;
    private $prefix;
    private $uri;
    private $nodes;

    /*
     * The global constructor for our object. It currently accepts 1 parameter
     * which is the prefix. If you work on a different level then the root of
     * the document root, you can use the prefix to pass a string.
     *
     * @example $prefix = "Game/Document/"
     *
     */
    public function Router ($prefix = false)
    {
        // Our initial request is something like:
        // www.domain.com/yourwebsite/home/user-profile/123/

        $this->index    = strrpos( $_SERVER['PHP_SELF'] , "index.php" );
        $this->root     = substr( $_SERVER['PHP_SELF'] , $this->index , -0 );

        // if prefix was given
        // in the test case you should specify /yourwebsite/
        if( $prefix ) {
            $this->prefix = $prefix;
        }else{
            $this->prefix = false;
        }

        // Let`s rebuild the actual request, we strip the $prefix
        // and we are left with a working request like:
        // $new = "/home/user-profile/123/";
        $new = str_replace($this->prefix, "", $_SERVER['REQUEST_URI']);

        // This time, we are going to replace the root with nothing.
        // Based on our $new string. An example would be:
        // $argument = "home/user-profile/123/";
        $argument   = str_replace( $this->root,  "" , $new);

        // Now to finalize our request, all we do is trim away the last /
        // and call a function / METHOD < called in OO to set the complete URI.
        // Btw, a URI is our request. 
        $this->setUri(trim($argument, "/"));

        // Now we are going to split our argument up in seperate
        // elements. We can use the explode function in PHP to explode a string
        // on a given character, and it will return an array containing all elements.
        // We also call in on the function / METHOD setNodes();
        $this->setNodes(explode("/", $this->getUri()));

        // As you can see, that`s about all it takes for the constructor. But we
        // are not done yet, we still need to create at least 2 methods for this class in
        // order for it work. These 2 are setUri(); and setNodes();

        // However, we are still not there, what use does it have if we can`t get
        // information from it? That`s why we`ll also use a method to get nodes.
    }

    /**
     * Sets the proper URI for this page request
     * @param <type> String  the actual URI
     */
    private function setUri ($uri)
    {
        // all this does is set this object field uri the good URI.
        $this->uri = $uri;
    }

    /**
     * This method will accept an array and will go through all elements within this
     * array and add them to the object field called: nodes
     * @param <type> $array 
     */
    private function setNodes ($array)
    {
        // Check wether the $array given is actually an array xD
        if(is_array($array))
        {
            // loop through all elements using the foreach()
            foreach($array as $node)
            {
                // add all elements to the field nodes. 
                array_push($this->nodes, $node);
            }
        }else{
            return false;
        }
    }


    public function getNodes ($number = false)
    {
        // if a number was given, return the node with the corresponding number
        // Using this means you can get the get node by calling getNodes(1);
        if( is_numeric( $number ) and $number <= count($this->nodes))
        {
            // Geef het huidige array nummer terug
            return $this->nodes[$number-1];
        }
        else
        {
            // if no number was given, or number was out of scope, return the
            // array with all nodes
            return $this->nodes;
        }
    }
}
and a simple index.php:

index.php

Code: Select all

<?php

// now if we want to use this object or class we have to call it and get it first.

// include the file
include("Router.class.php");

// Now that we have it, we can create a new object from this like:
// also note that Router is the classname, but indirectly points to the
// Router function, which accepts 1 parameter, the prefix. If you don`t need
// a prefix, you can use as down below:
$router = new Router();

// if however, you do need a prefix, you can use this:
$router2 = new Router("yourwebsite/document");

// Now, to get it some random node. you can type in your address bar: 
// something like: www.website.com/home/article

// to call it, or in case it`s empty
if(is_string($router->getNodes(1))){
    echo $router->getNodes(1);
}else{
    echo 'No request was made';
}
// that`s basicly it, it can be fully customizable etc. but for the scope of the tutorial
// and due to the fact it`s getting too late ( 4:am ) i`m gonna quit.
// BTW DIDNT TEST THE CODE YET, so if you run into an error, post it, ill see it soon enough :)
?>
User avatar
PaxBritannia
Posts: 680
Joined: Sun Apr 18, 2010 1:54 pm

Re: A simple Router in PHP

Post by PaxBritannia »

A .htaccess and OOP tutorial in one? Has Xaleph done the impossible? :)

I'm actually in the process of implementing some thing similar to this, albiet a little bit more specific and for nginx instead of apache.

Great tutorial!

pax.
Xaleph
Posts: 897
Joined: Mon Feb 07, 2011 2:55 am

Re: A simple Router in PHP

Post by Xaleph »

Haha thanks, i wrote it at 4 am so meh, i think theres bound to be some errors. I`ll test it tonight, since im at work right now lol. If people like it, i can also attah my most common used Router class, which has all options for extra parameters, something like ?debug and ?admin and is infinite in a way that doesn`t bother. Anyway, I use it mostly for simple MVC apps. node 1 is by default controller, second node is by default method in within controller, if i hit that, i can start loading specific views etc.

All parameters passed can be used for textual confirmation, which is cool. something like .logout/ if you type yes, you logout, that stuff. It`s the same url as the button "yes" which goes there anyway, but url is faster xD
User avatar
kaos78414
Posts: 507
Joined: Thu Jul 22, 2010 5:36 am

Re: A simple Router in PHP

Post by kaos78414 »

Note that in PHP 5.3.3 the function named Router will not be treated as a constructor, might want to rename it to __construct().
w00t
Xaleph
Posts: 897
Joined: Mon Feb 07, 2011 2:55 am

Re: A simple Router in PHP

Post by Xaleph »

In fact, it does work as a constructor. Where a lack of their __construct is missing, it will fall back to old syntax, which is why i choose Router, for the tutorials sake, as well as the option to run this in older PHP versions. For the end user`s sake. But you are right, __construct() is better, as well as a _destruct() method should be in there.

If i get really bored, i might write a simple OO tut, but then again, i won`t , since there`s already so many out there.
User avatar
kaos78414
Posts: 507
Joined: Thu Jul 22, 2010 5:36 am

Re: A simple Router in PHP

Post by kaos78414 »

Ah you're right. I read it wrong - "As of PHP 5.3.3, methods with the same name as the last element of a namespaced class name will no longer be treated as constructor. This change doesn't affect non-namespaced classes." from http://php.net/manual/en/language.oop5.decon.php

It only has to do with namespaced classes. Woops! My bad! :lol:
w00t
Xaleph
Posts: 897
Joined: Mon Feb 07, 2011 2:55 am

Re: A simple Router in PHP

Post by Xaleph »

Haha still, __construct() is better nonetheless.

Now, talking of namespaces, it`s messed up in PHP too. Bootstrappers work OK, but anything dynamicly loaded which require namespaced classes seem to work messed up. Also, autoloading $var() is messed up now, since namespaces are declared paths, not strings and PHP does not keep an index for all declared namespaces.
User avatar
kaos78414
Posts: 507
Joined: Thu Jul 22, 2010 5:36 am

Re: A simple Router in PHP

Post by kaos78414 »

Well said. You should definitely do an OO tutorial - I know of some users who have gone past the video tutorials and could use some intermediate skill building.
w00t
User avatar
PaxBritannia
Posts: 680
Joined: Sun Apr 18, 2010 1:54 pm

Re: A simple Router in PHP

Post by PaxBritannia »

Most OOP tutorials out there are don't do such a great job of explaining it.

One OOP tutorial I later stumbled on in nettuts which I thought did a better job at explaining it then how I learn it:
http://net.tutsplus.com/tutorials/php/r ... and-mysql/

This one actually does something useful (mysql CRUD classes).

Still, what works for me doesn't work for everyone: I'm sure another OOP tutorial would help. :D

pax.
Xaleph
Posts: 897
Joined: Mon Feb 07, 2011 2:55 am

Re: A simple Router in PHP

Post by Xaleph »

A quick overview and it seems to work pretty OK for a normal CRUD class indeed. The simpler, the better, if you ask me. I would prefer an abstraction between different database drivers. Having that out of the way, only using an CRUD class with a db interface, but if you know you are going to work with MySQL or pg i guess why bother writing for the other one anyway?

Anyway, now that we are on the subject, what`s the "best" database driver for a game? I`m thinking MySQL for it`s speed, since no stored procedures are required, so PG is a bottleneck. I dare say mysql would even beat a noSQL implementation here, why bother with file I/O if mysql drivers using I/O can do it faster?

Any comments ?
Post Reply

Return to “Tutorials”