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 PHP Web application, to streamline coding and provide, as much as possible, the ability to edit something once and have that change appear globally.

Feature photo by Alexas_Fotos via Pixabay, in the public domain.
Feature photo by Alexas_Fotos via Pixabay, in the public domain.

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 CSS stylesheets. That’s modularization: the CSS, and the Web pages that use it, are physically separate, so that when you change CSS 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 elegance: 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 PHP

While a PHP 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 PHP as an object-oriented language. Many PHP 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 PHP 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 PHP templates.

Modularization Of Design: Templates

We’ve already discussed using modularization to separate CSS 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 CSS layout: http://demo.dougv.com/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 PHP 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 PHP 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 PHP 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 PHP files above as header.php and footer.php, respectively, I can now include them onto a content page.

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

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

<?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://demo.dougv.com/php_modularization/index.php.

Notice that in the code above, I have defined the constants that I placed inside the header.php 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.php. That way, my constants have scope within the included page; if I declared these constants after I included header.php, they wouldn’t be available to header.php’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.php and not declare them in your pages. However, if you leave these constants in your header.php file, but don’t define them before including header.php 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.php, when your page is rendered, its title will be PAGE_TITLE.

Extending Modularization: MySQL 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 MySQL database server. As I noted before, in most of my PHP applications, I only need to connect to MySQL on a few pages; and when those pages do need MySQL, 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.php, 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.php before I define my page information constants and include header.php. I do it that way because if I can’t establish a MySQL connection, I don’t want the rest of the page to render at all.

You can see what I mean at http://demo.dougv.com/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 PHP 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 PHP tells the Web browser about the page it is rendering.

If you use a template module like header.php, 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.php 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.php. 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.php 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.php page then sends that data to a page named staff.php
    1. If staff.php doesn’t get the department name variable from department.php, you want to send the user back to department.php.
    2. If staff.php can’t connect to the database or has another problem, you want to send the user to a page named dberror.php.
    3. If staff.php can’t find employee records for the department, it should say so.
    4. Otherwise, staff.php 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.php, 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 PHP 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 PHP, 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();
?>

This code on Github: https://github.com/dougvdotcom/php-modularization

3 Comments

  1. 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

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  • Check out the Commenting Guidelines before commenting, please!
  • Want to share code? Please put it into a GitHub Gist, CodePen or pastebin and link to that in your comment.
  • Just have a line or two of markup? Wrap them in an appropriate SyntaxHighlighter Evolved shortcode for your programming language, please!