View All Development Resources

Creating a simple dynamic website (and more!) with PHP

This tutorial expects you to have a PHP web server setup and ready for testing. Preferably this would be a local server, so that as soon as you save/create a file you can visit it. Not much PHP knowledge is required, but as usual every little helps.

If you have your own static website, you may be getting sick of updating every single page when something in your header changes, a logo, a menu item, or even a single character - just to name a few examples.

This simple tutorial is going to help you step away from the perils of having to do this for every minor change, and leaving you just having to update a single file to affect many different (if not every!) page on your website.

Now let me say that there are a lot of tutorials like this on the internet already, but some of them will only take you to a very basic point and then stop. This one won't, I'm going to, after showing you the very basics, how to make your system much more advanced and dynamic like a real website is. There's no point only learning a bit, if you're essentially just going to have another static website.

So to expand, this is what we will be learning, in order of "difficulty":

  1. How to create a dynamic website with header/footer
  2. How to make your links prettier + friendlier (With Apache/IIS7)
  3. How to make your menu dynamic
  4. How to secure your system further
  5. How to make your title dynamic
  6. If you need more control, how to make your system into a minimal implementation of a 'VC' (MVC without the M) architecture

That's quite a lot if you ask me, so now to get started..

How to create a dynamic website with header/footer

Starting out with the very basic stuff, what we're going to accomplish here is splitting our website pages up from 1 file (for now let's use/call it index.htm) into 3 files. This file is going to become header.htm, index.htm (of course with amended contents) and footer.htm.

The first thing you need to do is identify, from the contents of index.htm, which part of your page is the header, which part is the content, and which part is the footer. This shouldn't be too bad as most sites generally have code like this (simplified):

<html>
<head>
    <title>My cool website</title>
</head>
<body>
    <div id="header">
        <img src="logo.png" alt="Cool Website Logo" />
    </div>
    <div id="content">
        <p>Awesome content goes here</p>
    </div>
    <div id="footer">
        <p>Copyright 2011 Cool Site.</p>
    </div>
</body>
</html>

If it is, congratulations your site is written in a beautiful way and it should be fairly obvious how to split it up, your header file (header.htm) should contain everything from the top down to (and including) <div id="content">. In the same light, everything underneath (not including) <p>Awesome content goes here</p> should go into footer.htm. Finally, the single line <p>Awesome content goes here</p> will hang around in index.htm.

Now the final thing you need to do once you have those pages is move them into a folder you're going to create called templates.

Once they're in templates, you're done with the splitting, and should simply repeat the process for the rest of your pages. Remember, all you need in the files is the content, you don't need to put the header/footer in there any more, so all you'll need to do is delete contents from the other files.

The Dynamic (PHP) bit

What we're trying to accomplish here is very simple, we're basically going to include the 3 files in order, first the header, then the content, then the footer. It's simple, and can be acheived with code such as the following:

<?php
include("templates/header.htm");
include(
"templates/index.htm");
include(
"templates/footer.htm");

It's the simplest thing on earth, and even without any PHP knowledge you would be able to figure out exactly what it's doing. So okay, let's go ahead and place that code in a file called index.php, in the web root.

Now, when you go to your website's address, you should see your homepage exactly as it was before we made these changes. If you do, that's great, and we're making good progress!

Now comes the good stuff, we of course need to make that dynamic now, so we're going to first discuss how we're going to do that.. Firstly, the parameter that makes our site show a different page is going to be called action, this is a GET parameter, which means that it's going to be passed through the URL. So when you visit your site, instead of going to, for example, http://site/about.htm, you will now to go to http://site/?action=about. Don't worry if that looks "worse", I've included a section to clean these up later on, you can even make them the exact same if you wanted to.

So, first of all we need to grab the GET parameter into our code, so, underneath our line that includes the header template, add the following:

$action $_GET['action'];

We now have the variable $action available for use to load our template page. But there's a problem, this is untrusted data from the user, if we were to simply go along now and do something such as include("templates/" . $action), we would be in big trouble. See, the user could visit a URL like this: http://site/?action=../../../../../../../../../etc/passwd, and download your password file (on a *nix system).

What we first need to do is sanitise this data. PHP has a function called basename, which we're going to use to ensure any malicious user can't change directory no matter how hard he tried. So, underneath where you're defining $action, add the following:

$action basename($action);

Basically, what basename is going to do is take, well, the base name of the path. This essentially translates to the filename, without any leading directory, which is exactly what we want!

So, now that we have a sanitised action name to include, let's go ahead and do it. Change index in your code (from templates/index.htm) to be $action, and we're done! Your code should now look a little something like this:

<?php
include("templates/header.htm");

$action $_GET['action'];
$action basename($action);
include(
"templates/$action.htm");

include(
"templates/footer.htm");

So let's try it out, visit http://site/?action=index, and you should see your homepage again. Now try going to http://site/?action=about (provided you have an about.htm in templates/, otherwise something else), you should see your about page!

If you do, that's perfect, if not, you've done something wrong along the way, so read back over it and see if you can figure it out.

What if there's no ?action=

People visiting your website are not going to know that they need to use ?action=index to view your homepage, so we need to cater for people who visit without a GET parameter. This will be our default case, and by default we will be showing your home page, index.htm.

So, back to your code, you will notice that we don't check if $_GET['action'] exists before we use it, so let's do that using PHP's empty function, which will check if a variable doesn't exist, or if it's empty.

Change your code to this:

<?php 
include("templates/header.htm"); 

if (!empty(
$_GET['action'])) {
    
$action $_GET['action']; 
    
$action basename($action); 
    include(
"templates/$action.htm"); 
}

include(
"templates/footer.htm");

empty() will return true if $_GET['action'] is empty or non-existant, so our dynamic code will only be ran if it does exist, as we're using ! to flip false to true, and vice versa. The code within if ([expression]) is only ran if [expression] evaluates to true, which, based on that code above, will happen when ?action is passed across.

So now that we're only running our code when there is a parameter present, we need to write some code that's going to be ran when it's not present. We will do this in the else block of the if statement, like so:

<?php  
include("templates/header.htm");  

if (!empty(
$_GET['action'])) { 
    
$action $_GET['action'];  
    
$action basename($action);  
    include(
"templates/$action.htm");  
} else {
    include(
"templates/index.htm");
}

include(
"templates/footer.htm");

So now what's happening is that, when there is no ?action sent over in the URL, we will include templates/index.htm, simple! When they do pass over ?action=something, then it will run out first set of code, just as we'd expect. Try that out now by first visiting http://site/, and then http://site/?action=about!

Once again, if everything's working, let's continue on...

http://site/?action=furballs

Now then, assuming your website doesn't have a page named furballs, visiting this URL is going to cause a nasty surprise to the user as it will throw an error saying files cannot be found.

This is because, according to your code, anything that's passed over should be loaded, so visiting this URL will attempt to load templates/furballs.htm!

We obviously don't want that, so we need to check if the file exists first, we can do this using the aptly named file_exists function provided by PHP. All we need to do is pass it a filename, and it will return a boolean value based on whether it exists or not!

So we're now going to amend our code to trick it into using the default if the file doesn't exists. Add an if statement as below, that will amend $action to have the value index when the file doesn't exist:

<?php
include("templates/header.htm");

if (!empty(
$_GET['action'])) {
    
$action $_GET['action'];
    
$action basename($action);
    if (
file_exists("templates/$action.htm")
        
$action "index";
    include(
"templates/$action.htm");
} else {
    include(
"templates/index.htm");
}

include(
"templates/footer.htm");

Yippee! Try visiting http://site/?action=furballs, and you should get to your homepage, if you do, move on!

http://site/?action=header and http://site/?action=footer

Argh! What happens if we go to these URLs, we'll either get the header or footer on our page twice, that's not good, so we're going add another if () statement to our code that will once again redirect users to the default page if they visit the above.

<?php 
include("templates/header.htm"); 

if (!empty(
$_GET['action'])) { 
    
$action $_GET['action']; 
    
$action basename($action); 
    if (
file_exists("templates/$action.htm"
        
$action "index"
    if (
$action == 'header' || $action == 'footer')
        
$action "index";
    include(
"templates/$action.htm"); 
} else { 
    include(
"templates/index.htm"); 


include(
"templates/footer.htm");

Once again, try out your new code! Simply visit http://site/?action=footer and http://site/?action=header, and you should get the same as when you visit http://site/ on it's own!

Tidying it all up

That's it, we've done everything that we need to, but the code just got longer and longer, so we're going to look at how to tidy it up now. Basically what we'll do is add a default value for $action, and always use the dynamic code. The only time that $action is set is when the $_GET parameter meets certain conditions.

Rather than explain it here, I'll just update the code to contain the changes, and comment it to so that you can see what's going on clearly...

<?php  
include("templates/header.htm");  

// Set the default name
$action 'index';
// Specify some disallowed paths
$disallowed_paths = array('header''footer');
if (!empty(
$_GET['action'])) {
    
$tmp_action basename($_GET['action']);
    
// If it's not a disallowed path, and if the file exists, update $action
    
if (!in_array($tmp_action$disallowed_paths) && file_exists("templates/{$tmp_action}.htm"))
        
$action $tmp_action;
}
// Include $action
include("templates/$action.htm");

include(
"templates/footer.htm");

Now then, this is possibly going to look a bit scarier, but it's commented, and it's basically just grouped 2 things into 1 if statement. Rather than having individual if conditions for the disallowed paths, we've moved it into an array so it's easier to manage, we've also moved the default page into a variable so that we can change it at any point.

Overall, despite being a bit longer, it's more manageable!

How to make your links prettier + friendlier (With Apache/IIS7)

You've probably heard of a .htaccess or web.config file before now, and that's exactly what we're going to use. If you're using Apache as your web server, you'll need to make sure that mod_rewrite is installed. Likewise, if you're using IIS, you need to ensrue that URL Rewrite 2 is installed.

Apache

RewriteEngine On
# Simple passthru for existing files (so we can still get to files that exist!) RewriteCond %{REQUEST_FILENAME} -s [OR] RewriteCond %{REQUEST_FILENAME} -l [OR] RewriteCond %{REQUEST_FILENAME} -d RewriteRule ^.*$ - [NC,L] # Rewrite anything to index.php?action=anything (http://site/test -> http://site/index.php?action=test) RewriteRule ^(.*)$ index.php?page=$1

IIS

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <rule name="CodeHook Website Rewrite" enabled="true" stopProcessing="true">
                    <match url="^(.*)$" ignoreCase="true" />
                                <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
                                    <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                                    <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                                </conditions>
                                <action type="Rewrite" url="index_.php/{R:1}" appendQueryString="false" logRewrittenUrl="true" />
                        </rule>
            </rules>
        </rewrite>
    </system.webServer>
</configuration>

The rest..

The rest will be coming soon... I need to rethink my strategy for posting tutorials, will probably be making a microsite soon.

This format makes the page wayyy too long and people won't read it!