A simple PHP master Template framework

Julian Hansen
CERTIFIED EXPERT
Published:
Updated:

Overview

This article discusses how to create a simple PHP master template framework for a multiple page website. The template contains the container, navigation, footer etc for the site i.e. all the common elements and pulls in specific content based on the page being requested.
To make things a bit more intersting lets also base the template on a responsive framework - my preference is foundation so I will use that.

The Master Template File

We start with the index.php file. Let's start with a standard HTML document and add some markup

index.php
<!doctype html>
                      <html>
                      <head>
                      <title>The Title</title>
                      </head>
                      <body>
                      </body>
                      </html>

Open in new window

That's about as barebones as you can get - but functionally not really useful to us. Lets add some common elements we will need on all pages like stylesheets and javascript libraries as well as our masthead, nav and footer.
To be backwardly compatible with the unmentionable browser we will add the modernizr shim as well.
Here is a full source listing for a sample index.php file - we will go through it in detail later on in the article.

index.php
<?php
                        require_once('includes/common.php');
                        $page = empty($_REQUEST['page']) ? 'home' : preg_replace('/[^a-zA-Z0-9\-_]/', '', $_REQUEST['page']);
                        $pagepath = "includes/$page.class.php";
                        if (file_exists($pagepath)) {
                          require_once($pagepath);
                          $pageclass = $page . 'Class';
                        }
                        else {
                          $pageclass = 'BasePage';
                        }
                        $mainframe = new $pageclass($page);
                      ?>
                      <!doctype html>
                      <html>
                      <head>
                          <meta charset="utf-8" />
                          <meta name="viewport" content="width=device-width, initial-scale=1.0" />
                          <title><?php $mainframe->title();?></title>
                          <link rel="stylesheet" href="css/foundation.css" />
                          <link rel="stylesheet" href="css/custom.css" />
                          <script src="js/vendor/modernizr.js"></script>
                      </head>
                      <body id="<?php echo $page;?>_page">
                        <div id="wrapper">
                          <div id="topbar" class="small bottomShadow">
                            <div class="row">
                              <div class="large-2 columns"><a href="index.html" id="logo"><img id="logo" src="css/images/logo.png" alt="Experts Exchange"/></a></div>
                              <div class="large-10 columns" style="margin-top: 10px; text-align: right">
                                <a href="login.html" class="login">Login</a>
                                <nav>
                                  <ul>
                                    <li><a href="index.php?page=home">Home</a></li>
                                    <li><a href="index.php?page=aboutus">About US</a></li>
                                    <li><a href="index.php?page=products">Products</a></li>
                                    <li><a href="index.php?page=contact">Contact US</a></li>
                                  </ul>
                                </nav>
                              </div>
                            </div>
                          </div>
                          <div class="mainContent">
                        <!-- CONTENT AREA STARTS HERE -->
                        <?php $mainframe->render();?>
                        <!-- CONTENT AREA ENDS HERE -->
                          </div>
                        </div>
                        <div id="footer">
                          <div class="row">
                            <div class="large-9 columns">
                              <div class="row">
                                <div class="large-4 columns">
                                  <h6>NAVIGATE<h6>
                                  <ul>
                                    <li><a href="/" data-ga-label="Footer" data-ga-action="MasterPage" data-ga-category="HomePage" class="track-click" id="Body_lnkHome">Home</a></li>
                                    <li><a href="/" data-ga-label="Footer" data-ga-action="MasterPage" data-ga-category="HomePage" class="track-click" id="Body_lnkHome">Home</a></li>
                                    <li><a href="/" data-ga-label="Footer" data-ga-action="MasterPage" data-ga-category="HomePage" class="track-click" id="Body_lnkHome">Home</a></li>
                                    <li><a href="/" data-ga-label="Footer" data-ga-action="MasterPage" data-ga-category="HomePage" class="track-click" id="Body_lnkHome">Home</a></li>
                                  </ul>
                                </div>
                                <div class="large-4 columns">
                                  <h6>EVENTS<h6>
                                  <ul>
                                    <li><a href="/" data-ga-label="Footer" data-ga-action="MasterPage" data-ga-category="HomePage" class="track-click" id="Body_lnkHome">Home</a></li>
                                    <li><a href="/" data-ga-label="Footer" data-ga-action="MasterPage" data-ga-category="HomePage" class="track-click" id="Body_lnkHome">Home</a></li>
                                    <li><a href="/" data-ga-label="Footer" data-ga-action="MasterPage" data-ga-category="HomePage" class="track-click" id="Body_lnkHome">Home</a></li>
                                    <li><a href="/" data-ga-label="Footer" data-ga-action="MasterPage" data-ga-category="HomePage" class="track-click" id="Body_lnkHome">Home</a></li>
                                  </ul>
                                </div>
                                <div class="large-4 columns">
                                  <h6>NETWORK<h6>
                                  <ul>
                                    <li><a href="/" data-ga-label="Footer" data-ga-action="MasterPage" data-ga-category="HomePage" class="track-click" id="Body_lnkHome">Home</a></li>
                                    <li><a href="/" data-ga-label="Footer" data-ga-action="MasterPage" data-ga-category="HomePage" class="track-click" id="Body_lnkHome">Home</a></li>
                                    <li><a href="/" data-ga-label="Footer" data-ga-action="MasterPage" data-ga-category="HomePage" class="track-click" id="Body_lnkHome">Home</a></li>
                                    <li><a href="/" data-ga-label="Footer" data-ga-action="MasterPage" data-ga-category="HomePage" class="track-click" id="Body_lnkHome">Home</a></li>
                                  </ul>
                                </div>
                              </div>
                            </div>
                            <div class="large-4 columns"></div>
                          </div>
                        </div>
                        
                          <script src="js/vendor/jquery.js"></script>
                          <script src="js/foundation.min.js"></script>
                        <script src="js/scripts.js"></script>
                          <script>
                            $(document).foundation();
                          </script>
                        <?php $mainframe->dump_scripts();?>
                      </body>
                      </html>

Open in new window

That was a big jump but we will go through each of the sections one at a time.

Routing

To make the framework extensible it needs to have a routing function i.e a means to route the request to the code that can process it and render the correct page. The code below is a very simplistic example of how to implement routing.

Extract from index.php
<?php
                        // INCLUDE OUR COMMON LIBRARY CODE - WE WILL DEFINE THIS A BIT LATER */
                        require_once('includes/common.php');
                        
                        // GET OUR PAGE VARIABLE FROM THE URL (OR POST IF IT WAS POSTED). 
                        // IF THE $page PARAMETER EXISTS THEN SANITIZE IT i.e. STRIP IT OF
                        // INVALID CHARS. IF THE PARAMETER DOES NOT EXIST DEFAULT IT TO home
                        $page = empty($_REQUEST['page']) ? 'home' : preg_replace('/[^a-zA-Z0-9\-_]/', '', $_REQUEST['page']);
                        
                        // CREATE A PATH TO THE FILE THAT CONTAINS THE CODE FOR THIS PAGE
                        $pagepath = "includes/$page.class.php";
                        
                        // IF IT DOES EXIST SET THE PAGECLASS TO THE PAGE NAME
                        if (file_exists($pagepath)) {
                          require_once($pagepath);
                          $pageclass = $page . 'Class';
                        }
                        // OTHERWISE DEFAULT IT TO THE BASE
                        else {
                          $pageclass = 'BasePage';
                        }
                        
                        // AND INSTANTIATE THE CLASS
                        $mainframe = new $pageclass($page);
                      ?>

Open in new window

Rendering Specific Pages

Each page can potentially have a class to extend the functionality required for that page - but if a class is not found the framework fails over to the default (BasePage) functionality.
Why are we using classes here - that will become apparent in the next section but to give a spoiler - the reason is we want to put generic functionality in a parent class (contained in common) and then we want to override that for our specific page.

Actual content is stored in a view - which is plain old HTML - potentially with some script to popupluate the page with variables. You set these variables in the render() method of the page. Confused? Continue reading for a description of how the classes hang together.

The Base Class

First up lets look at the base class for our pages. The base class handles all the generic functionality across all pages. Extending the base class depends on your requirements. The base class is intended as a generic class to be used in any project. If you have specific generic functions you want to implement for a given project then it is better to create a subclass of the base class and add the generic functionality to the subclass. You can then subclass this new class for your pages. In this way you provide generic functionality for your project without compromising the generic (cross project) structure of the base.

includes/common.php
<?php
                      // OUR DEFAULT TITLE TO USE IF WE DON'T SPECIFICALLY SET ONE
                      define('gb_title', 'DEFAULT TITLE');
                      
                      // THE BasePage CLASS THAT CONTAINS THE DEFUALT FUNCTIONALITY
                      // FOR OUR PAGES.
                      class BasePage 
                      {
                        // THE TITLE PROPERTY THAT CAN BE OVERWRITTEN IN THE
                        // PAGE CLASS. REFER TO THE ProductClass FOR EXAMPLE
                        protected $title = gb_title;
                        
                        // PUBLIC PROPERTY TO STORE THE CURRENT PAGE
                        public $page;
                        
                        function __construct($page)
                        {
                          $this->page = $page;
                        }
                        
                        function title()
                        {
                          echo $this->title;
                        }
                        // METHOD TO RETURN A PATH TO OUR VIEW FILE
                        // OR THE ERROR VIEW IF PAGE NOT FOUND
                        function get_view()
                        {
                          $view = "view/" . $this->page . '.view.php';
                          if (file_exists($view)) {
                            return $view;
                          }
                          return 'view/error.view.php';
                        }
                        
                        // DEFAULT RENDER METHOD TO OUTPUT THE 
                        // HTML CONTAINED IN THE VIEW FILE
                        function render()
                        {
                          $view = $this->get_view();
                          require_once($view);
                        }
                      }
                      ?>

Open in new window


Fleshing it out

For this example we will define only the Home Page view i.e. no controller class, to demonstrate how the framework can return a view without the page having a controller class. We will use the Products Page to demonstrate how to build a page controller class.

The Home Page

A very simple piece of html to represent our home page content. Because this is inserted into the template the rest of the page detail (nav, footer, scripts) are alrady taken care of

view/home.view.php
<div class="row">
                        <div class="large-12 columns">
                          <div class="row">
                            <div class="small-6 large-2 columns">&nbsp;</div>
                            <div class="small-6 large-8 columns">
                              <h1>Home Page</h1>
                            </div>
                            <div class="small-12 large-2 columns">&nbsp;</div>
                          </div>      
                        </div>
                      </div>

Open in new window


The Products Page

As mentioned above the Products Page has a controller class and a view. The controller class is defined as follows.

includes/products.class.php
<?php
                      class ProductsClass extends BasePage
                      {
                        // DEMONSTRATE HOW TO OVERRIDE THE TITLE
                        // WE CAN ALSO MAKE THIS DYNAMIC BY SETTING
                        // THIS PROPERTY IN THE CONSTRUCTOR
                        protected $title = "Products Page";
                        
                        // CUSTOM RENDER FUNCTION DEMONSTRATING 
                        // HOW TO SET A VARIABLE FOR USE IN THE VIEW
                        function render()
                        {
                          $view = $this->get_view();
                          $pagevariable = "Products Page";
                          require_once($view);
                        }
                      }
                      ?>

Open in new window


The Products Page View

This code is very similar to the Home Page view except we are using script to output a variable in the rendered content.
<div class="row">
                        <div class="large-12 columns">
                          <div class="row">
                            <div class="small-6 large-2 columns">&nbsp;</div>
                            <div class="small-6 large-8 columns">
                              <h1><?php echo $pagevariable;?></h1>
                            </div>
                            <div class="small-12 large-2 columns">&nbsp;</div>
                          </div>      
                        </div>
                      </div>

Open in new window

The above will give us a working framework that is based on a master template, a base class containing default functionality that can then be extended in subclasses for particular pages.

Making it better

While the above works there are some small modifications we can make that will make it even better. One such modification is to get rid of the scripted urls and make our urls more human friendly.

To do this we need to create an .htaccess file with some routing rules and we will need to make a few changes to our master template.

The .htaccess file

While the .htaccess file can be used for many other functions other than routing we will only cover routing here.
AddType x-mapp-php5 .php
                      Options +FollowSymLinks
                      
                      RewriteEngine on
                      RewriteBase /
                      # REDIRECT REQUESTS FOR index.html TO OUR TEMPLATE
                      RewriteRule index\.html$ index.php [L,NC,QSA]
                      
                      #IF THE REQUESTED FILE DOES NOT EXIST PASS THIS
                      #TO index.php WITH THE PAGENAME AS A PARAMETER
                      RewriteCond %{REQUEST_FILENAME} !-f
                      RewriteRule (.*)\.html$ index.php?page=$1 [L,NC,QSA]

Open in new window

That's it - we can now request products.html and the request will be routed to

index.php?page=products

All we need to do is modify our internal links to be html pages instead of index.php requests. This means we need to update our <nav> section in our master template. While we are at it we might as well add some functionality to be able to mark which of our menu links is active so we can style active links separately from inactive ones.
          <nav>
                                  <ul>
                                    <li<?php echo ($page=='home') ? ' class="active"' : '';?>><a href="index.html">Home</a></li>
                                    <li<?php echo ($page=='aboutus') ? ' class="active"' : '';?>><a href="aboutus.html">About US</a></li>
                                    <li<?php echo ($page=='products') ? ' class="active"' : '';?>><a href="products.html">Products</a></li>
                                    <li<?php echo ($page=='contact') ? ' class="active"' : '';?>><a href="contact.html">Contact US</a></li>
                                  </ul>
                                </nav>

Open in new window

We could also extend our BasePage class to include a method called route_link that renders the <li> menu items for us complete with class and href values.
  function route_link($page, $class, $text) 
                        {
                          $classtext = ($this->page == $page) ? ' class="' . $class . '"' : '';
                          echo '<li' . $classtext . '><a href="' . $page . '.html">' . $text . '</a></li>';
                        }

Open in new window

 And modifying our <nav> section to look like this
          <nav>
                                  <ul>
                                  <?php 
                                    $mainframe->route_link('home','active','Home');
                                    $mainframe->route_link('About Us','active','About Us');
                                    $mainframe->route_link('products','active','Products');
                                    $mainframe->route_link('contact','active','Contact');
                                  ?>
                                  </ul>
                                </nav>

Open in new window

Installation

To test this code download and install the foundation framework and then copy the attached files to the locations shown
/root
      .htaccess [rename .txt]
    index.php
      /css
            custom.css
      /includes
            common.php
            products.class.php
      /view
            error.view.php
            home.view.php
            products.view.php

The foundation installation should create the following structure
/root
      /js
            foundation.min.js
            /vendor
                  jquery.js
                  mondernizr.js
      /css
            foundation.css
            normalize.css
            
That's all there is to it. Hopefully this has been informative in how to setup a simple master template framework.
index.php
common.php
products.class.php
error.view.php
home.view.php
products.view.php
custom.css
htaccess.txt
11
17,995 Views
Julian Hansen
CERTIFIED EXPERT

Comments (4)

Loganathan NatarajanLAMP Developer
CERTIFIED EXPERT

Commented:
Great!!, it is useful.
CERTIFIED EXPERT
Expert of the Year 2014
Top Expert 2014

Commented:
Nicely explained - gets my vote!
for the long time of this, it really help me. thank a lot. and also if you're using xampp as a local server you might need to set the  
RewriteBase / in the .htaccess file to the root  folder name of you're project e.g RewriteBase /MyprojectName else it will be redirecting to the localhost/dashboard.
i hope this will help someone as this post help too.
CERTIFIED EXPERT
Fellow
Most Valuable Expert 2017

Author

Commented:
Thank you Abbaty, if you read the article again you will see there is a section on .htaccess that includes the RewriteBase.

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.