Skip to content
 

Better Managing Your PHP Application Via Modularization And Abstraction

Most Web sites are designed within a template or two. That is, the layout, typography and basic design of every page is fundamentally the same for most, if not all, pages.

Also, most Web pages tend to need the same resources. If you have a database-driven site, many pages need to use the same connection; other objects, such as user-made classes, functions and the like, often need to be used by many pages.

Templates are nothing new. Adobe has even built a dedicated Web authoring application, Contribute, that lets non-designers update content in templates made in Dreamweaver. Most open-source Web applications, such as Zen Cart, WordPress and phpBB, all allow you to “skin” their applications.

Thanks to the power of PHP, we can accomplish these goals on our own custom Web designs with abstraction and modularization. I’ll explain how I use both, in a typical Web application, to streamline coding and provide, as much as possible, the ability to edit something once and have that change appear globally.

Abstraction And Modularization Explained

In programming, an abstraction is one of what may be many ways to achieve a desired end result, by leveraging a fundamental way of going about things.

For example, suppose I tell you to go to the store.

You have several ways you can do that: You could drive in your car. Or call a cab. Or ride your bicycle. Or walk. All of these are abstractions of movement.

In order to go to the store, you must move. That’s the fundamental way of getting to the store; moving toward it until you are there.

But you have many different ways of moving, and each of those ways is an abstraction of the fundamental of moving.

Each way has benefits and detriments compared to the others. For example, walking and biking are slower, but more healthy; a car or cab ride allows you to more easily transport whatever you buy at the store.

The same is true in computing. If you want to get data out of a database, the fundamental requirements are connecting to the server, sending it a request it understands, and getting your program to understand what the database server sends back.

You might use OLE for this purpose, or ODBC, or any of many other objects available. Sometimes you have no choice — only one data transport object is available to you — but often, which you use is a matter of personal preference, based on what the object provides and what you need to do.

Modularization, also called encapsulation, is a way to break your code into small parts, each of which can be employed as you need it.

For example, many Web designers use external stylesheets. That’s modularization: the , and the Web pages that use it, are physically separate, so that when you change code on a stylesheet, the change is applied to all the pages that use that stylesheet. One code change can have far-reaching effect.

This is the heart of : Using as little code as possible, keeping things simple. Both modularization and abstraction are code components of the object-oriented programming approach.

Modularization And Abstraction In

While a application can be written entirely as object-oriented code, that’s not the roots of the language, nor the way most people go about writing it.

It’s often a bad idea to use as an object-oriented language. Many applications need to accomplish small tasks — say, echoing out a line or two of text — and the overhead of defining, instantiating and invoking objects is sometimes serious overkill.

But that doesn’t mean we can’t often use the concepts of object-oriented programming when it helps us make our applications better.

For example, modularization not only allows us to only include the code we need when we need it; modularization also makes for very efficient, fully functional templates.

Modularization Of Design: Templates

We’ve already discussed using modularization to separate from Web pages. Another useful tactic is to use modules to create templates.

Most sites I create have common header and footer elements, standard navigation controls, and a single area of the page that actually holds content.

For example, consider this basic layout: http://www.dougv.com/demo/php_modularization/index.html

All of the content appears in the left-hand DIV with the contextual ID of #mainContent. Otherwise, the header, footer and navigation elements will be the same for all pages.

So, what I would do here is create a file that contains all the elements up to, and including, the opening div#mainContent tag:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title><?php echo PAGE_TITLE; ?></title>
        <link href="twoColFixRtHdr.css" rel="stylesheet" type="text/css" />
        <meta name="description" content="<?php echo PAGE_DESC; ?>" />
	<meta name="keywords" content="<?php echo PAGE_KEYWORDS; ?>" />
        <!--[if IE 5]>
            <style type="text/css"> 
                /* place css box model fixes for IE 5* in this conditional comment */
                .twoColFixRtHdr #sidebar1 { width: 220px; }
            </style>
        <![endif]-->
        <!--[if IE]>
            <style type="text/css"> 
                /* place css fixes for all versions of IE in this conditional comment */
                .twoColFixRtHdr #sidebar1 { padding-top: 30px; }
                .twoColFixRtHdr #mainContent { zoom: 1; }
                /* the above proprietary zoom property gives IE the hasLayout it needs to avoid several bugs */
            </style>
        <![endif]-->
    </head>
 
    <body class="twoColFixRtHdr">
        <div id="container">
            <div id="header">
            	<h1>Header</h1>
            <!-- end #header -->
            </div>
            <div id="sidebar1">
                <h3>Navigation</h3>
                <p>Home</p>
                <p>Header page</p>
                <p>Footer page</p>
            <!-- end #sidebar1 -->
            </div>
            <div id="mainContent">

Notice that in the code above, I have commands to echo undefined constants named PAGE_TITLE, PAGE_DESC and PAGE_KEYWORDS. More on those in a bit.

And then, I would create another file, this one beginning with the closing tag for div#mainContent, and the rest of my template page markup:

            <!-- end #mainContent -->
            </div>
            <!-- This clearing element should immediately follow the #mainContent div in order to force the #container div to contain all child floats -->
            <br class="clearfloat" />
            <div id="footer">
                <p>Footer</p>
            <!-- end #footer -->
            </div>
        <!-- end #container -->
        </div>
    </body>
</html>

Using The Template Files Via require_once()

Having saved the files above as header. and footer., respectively, I can now include them onto a content page.

Actually, rather than using the function include(), I prefer to use require_once(). They accomplish the same task — incorporating another file into a page — but require_once() will throw an error if a given function name appears in both the page and the included file.

For example, if header. contains a function named foo(), and index. also contains a function named foo(), require_once() will throw an error if you include header. in index..

<?php
define('PAGE_TITLE', 'PHP Modularization And Abstraction');
define('PAGE_DESC', 'Demonstrates how to use include files in PHP to modularize and abstract basic design and tasks.');
define('PAGE_KEYWORDS', 'PHP modularization abstraction programming');
 
require_once('header.php');
?>
<h1>Hello World!</h1>
<p>If all went according to plan, you should see this page appear in the <a href="index.html">same page design</a> we broke into header and footer elements, but with unique content, a proper title, and good <code>META</code> information.</p>
<?php
require_once('footer.php');
?>

You can see this page in action at http://www.dougv.com/demo/php_modularization/index.php.

Notice that in the code above, I have defined the constants that I placed inside the header. file:

  1. PAGE_TITLE is the title of the page;
  2. PAGE_DESC is the META description tag content for the page, which is important for search engine optimization; and
  3. PAGE_KEYWORDS is the META keyword tag content for the page, which most search engines ignore but I am adding for completeness.

I call these constants before I include header.. That way, my constants have scope within the included page; if I declared these constants after I included header., they wouldn’t be available to header.’s code.

If you don’t want to dynamically declare your page’s title, description and / or keywords, you can simply remove the relevant constants from header. and not declare them in your pages. However, if you leave these constants in your header. file, but don’t define them before including header. in your page, the names of the constants will appear as the values for the undefined constants.

For example, if you don’t define PAGE_TITLE, but you leave it in header., when your page is rendered, its title will be PAGE_TITLE.

Extending Modularization: Connections

This technique can be used not only for templates, but for common code that needs to run on all pages, or just certain pages.

An obvious example of this is connecting to a database server. As I noted before, in most of my applications, I only need to connect to on a few pages; and when those pages do need , they connect to the same server and use the same database as other pages in the site.

So, if we create a page called connect., which contains only our connection code:

$link = mysql_connect('localhost', 'user', 'password') or die('Cannot connect to db host');
mysql_select_db('database') or die('Cannot select database');

We can then use require_once() to add this code to any page that needs it:

<?php
require_once('connect.php');
 
define('PAGE_TITLE', 'With a MySQL connection');
define('PAGE_DESC', 'Demonstrates how to use include a connection file in PHP to modularize and abstract basic design and tasks.');
define('PAGE_KEYWORDS', '');
 
require_once('header.php');
?>
<h1>Hello World!</h1>
<p>If all went according to plan, you should see this page appear in the <a href="index.html">same page design</a> we broke into header and footer elements, but with unique content, a proper title, and good <code>META</code> information.</p>
<?php
require_once('footer.php');
?>

Notice that I call connect. before I define my page information constants and include header.. I do it that way because if I can’t establish a connection, I don’t want the rest of the page to render at all.

You can see what I mean at http://www.dougv.com/demo/php_modularization/index2.php.

Using header() With A Modularized Approach

Another benefit of this approach is that it makes it easier to track when you are sending headers to the user — that is, when you first send content.

Most new programmers have received the dreaded warning, “headers already sent”, when they are trying to either redirect a user, change the content type of the page or otherwise manipulate what tells the Web browser about the page it is rendering.

If you use a template module like header., it’s generally easy to tell when you are about to send headers: If you include it, you’ve sent headers.

So, any code that appears before you include header. would be processed before headers are sent.

This, of course, doesn’t mean you won’t accidentally echo something out, or include a file that sends text to the user, or otherwise accidentally send header information before you call header.. It means it makes it easier to manage where your code appears, because you now have a clear marker of the point where you intend to start sending content to the browser.

Here’s an example of what I mean:

  1. You have a page named department. that has a form; this form contains a select list of company departments.
  2. The user needs to select a department from that pull-down menu, then click Submit.
  3. The department. page then sends that data to a page named staff.
    1. If staff. doesn’t get the department name variable from department., you want to send the user back to department..
    2. If staff. can’t connect to the database or has another problem, you want to send the user to a page named dberror..
    3. If staff. can’t find employee records for the department, it should say so.
    4. Otherwise, staff. should list the staff members for the selected department in a simple table.

The requirements above will necessitate us sending a header() command in the event condition 3.1 or 3.2 exists, so the code to do that needs to appear before we send any content to the Web browser.

However, conditions 3.3 and 3.4 require us to send content after we’ve called header., because we want it to appear within our template.

To do that, we would use code similar to this:

<?php
//make sure department variable exists and has value
if(!isset($_POST['department']) || $_POST['department'] == '') {
	header('Location: department.php');
	exit;
}
else {
	//escape variable
	$dept = mysql_real_escape_string($_POST['department']);
}
 
require_once('connect.php');
 
if(!$rs = mysql_query("SELECT * FROM staff_table WHERE department = '$dept'")) {
	//redirect on db error
	header('Location: dberror.php');
	exit;
}
 
define('PAGE_TITLE', 'Where Your Code Appears Is Important');
define('PAGE_DESC', 'It's important to call code in the proper order.');
define('PAGE_KEYWORDS', '');
 
require_once('header.php');
?>
<h1>Staff Listing</h1>
<?php
if(mysql_num_rows($rs) == 0) {
	//no records? report that fact
	echo "<p>Sorry, we couldn't find any staff members for $_POST[department].</p>";
}
else {
	//show all staff members in table
	echo "<table>n";
	echo "<tr><th>Name</th><th>Job Title</th><th>Extension</th><th>E-mail</th></tr>n";
 
	while($row = mysql_fetch_array($rs)) {
		echo "<tr>";
		echo "<td>$row[staff_name]</td>";
		echo "<td>$row[staff_title]</td>";
		echo "<td>$row[staff_extension]</td>";
		echo "<td>$row[staff_email]</td>";
		echo "</tr>n";
	}
 
	echo "</table><br />n";
}
 
require_once('footer.php');
?>

Notice that we only call code, and only include files, if and when they are needed. We don’t want the page to do things it doesn’t need to do, work harder than it has to work or incur overhead it doesn’t need to use.

I’m not the first person to use this approach, and there are valid points against using this approach — for example, there’s a valid argument against sending a page to the preprocessor at all if the page only contains static HTML.

And there are other ways to use templates or control resource / code allocation, other than this.

However, I find this approach to be very efficient, especially if I am working with predicable content (that is, I know what is going to appear on each page, and I’ve planned my design to be able to accommodate that content).

There’s only one more note I would add: When using this approach, sometimes you will want to buffer the output, especially if you are calling lots of include files or have lots and lots of code on the page that is also including your template files.

I buffer output in , most commonly, with ob_start() and ob_end_flush(). I always call them as the very first, and very last, functions invoked by the page.

Technically, ob_start() can appear at any point prior to header information being sent to the client, and ob_end_flush() can be called at any point after you have created content you want to have appear in the Web browser. In fact, there are certain cases — such as when a page takes a long time to process — where you want to send your output before the page is done processing.

But to cut down on errors, I create the buffer first, and clear it out last; 99 percent of the time, that approach is fine.

<?php
ob_start();
 
define('PAGE_TITLE', 'PHP Modularization And Abstraction');
define('PAGE_DESC', 'Demonstrates how to use include files in PHP to modularize and abstract basic design and tasks.');
define('PAGE_KEYWORDS', 'PHP modularization abstraction programming');
 
require_once('header.php');
?>
<h1>Hello World!</h1>
<p>If all went according to plan, you should see this page appear in the <a href="index.html">same page design</a> we broke into header and footer elements, but with unique content, a proper title, and good <code>META</code> information.</p>
<?php
require_once('footer.php');
 
ob_end_flush();
?>

You can download all the code noted here:

Better Managing Your PHP Application Via Modularization And Abstraction Demo Code

I license all code under the GNU GPL version 3.

Comments (1)

  1. JonB says:

    Nice job! I enjoyed this and your one about creating a Web Career, and will be passing them on to a young colleague

    :-)

Leave a Reply

Spam Protection by WP-SpamFree