picture of Mark Willams

Overview

OK, so you've heard all about it, you think you've got your head around it. Everyone you speak to says it's the way real developers develop. How do you do it? What's in it for you? And how the hell do you do it with PHP?
Broadly speaking, all of the above statements are true, but I guess if you're not familiar with OO you want to see a concrete example - and ideally something, which you can fit into your own page(s). Well, here goes.
Let's start off by seeing exactly what we're aiming for.
[Image:] Preview of Results
Basically, we have three boxes (F1 Teams, Drivers Championship and Constructors Championship).

F1 Teams

Quite simply, this is a box with an un-ordered list in it. Each item is a link to the Team's web site.

Drivers Championship

This is an informational box, showing the current points standing in the F1 Drivers Championship. There are no links within this box.

Constructors Championship

Like the Drivers Championship box, this is also an informational box, showing the current points standing fo the F1 Constructors Championship.
Now, before you say it, I know you probably don't want details on the current F1 championship, but this does serve as a sample application. At the end of the day, the data is simply taken from a database (in this case I am using MySQL), so you can use whatever data you like. The point is these boxes are "great" portal-type tools for showing lots of focused data. Whether you are a fan of them or not, they do work.

How do we create them?

In my mind there are a number of ways we can create these:
  1. Hard code the data into a web page. There is nothing wrong with this method if you just want the box(es) to appear on one page, but once you start spreading the across pages, the administrative overhead does become tedious.
  2. Set the boxes up as a server side include file. Again, there is nothing wrong with this; you can then include the box(es) on any number of pages simply by referencing the include file.
  3. Create the boxes as Objects. Initially, this does take a little longer to build, but it does make for portable code (I'm talking about code which walks from site to site, not just page to page!). Additionally, we create interfaces to both the data source and the layout of the box(es), which means we have "easier" control of their data and layout.
As you've probably guessed, we're going with the OO method. What would be the point otherwise?
So, let's start at the top...
Setting the requirements. For the purpose of this exercise, I'm going to define my requirements as:
There are a number of ways we can meet these criteria; and for the latter two, it is probably easiest to make use of CSS, which is what we'll do. As for the first two, so the story begins:
To build this box we are going to use 7 separate files:
Standards
All .obj files are class declarations. I use one file per class/subclass. The .phtml file is the file we are gong to insert our box(es) into. The .inc file is a generic server side include file and the CSS file is exactly that - a Style Sheet.

Development Process

As always, we will start by defining our global constants, then we'll move onto create our data schema (and database), followed by our data class, then our box classes and finally, we'll create the .phtml file, which will bring it all together into a working example - or at least, that's the plan at the moment!
Constants
This is a straight forward PHP file, which I create to define Constant values I can use across a whole site. By using this - appropriately - I can change the look/feel of a site from one place, rather than having to chase variable values across multiple pages. Here's our constants.inc sample:

<?php

// Database Constants

$HOST             =     "localhost";
$DB            =    "testing";
$WEBUSER        =    "root";
$WEBPASSWORD        =    "";

// Colour Constants

$COLOR_PRIMARY        =    "#037B0B";
$COLOR_SECONDARY    =    "#FFFFC0";
$COLOR_TERTIARY        =    "#ECED81";

// Value Constants

$TRUE            =    1;
$FALSE            =    0;
    
// Application Specific
$TITLE            =    "Object Orientation Demonstration";
$ADMINEMAIL        =    "webmaster@domin.com";
    
// CSS Plug-in Values
    
$CSSBOXTITLE        =     "boxtitle";
    
?>

Database Schema

Under no circumstances should this be taken as a de-facto database schema, but it works for the purposes of this example:
[Image:] Entities & Relationships
As you can see, the data for News is in a table in its own right, whilst the race, driver, team and points details are the result of related tables. Suffice to say, if you do not understand this schema, you should go and read up on some RDBMS introductory material before going any further.

mysqldb Class

So, now we have defined our RDBMS and we know where our data is stored. It's time to start playing around with OO!!!!
If you have played around with PHP for any length of time, you will be well aware there are many functions available for interfacing to a number of RDBMS. Essentially, this class is going to serve as a "wrapper" to some of the MySQL functions - of course, if you are writing for Postgres (or Oracle, or any other RDBMS) you can modify these accordingly.
Here is the code:

<?php

class mysqldb {

    
//set up the object
    
var $host;
    var 
$db;
    var 
$dbuser;
    var 
$dbpassword;
    var 
$sql;
    var 
$numberrows;
    var 
$dbopenstatus;
    var 
$dbconnection;
    
/*
        Use these functions to get and set the values of this object's
        variables. This is good OO practice, as it means that datatype 
        checking can be completed and errors raised accordingly. 

    */

    // Property Get & Set
        
    
function gethost() {
        return 
$this->dbhost;
    }
            
    function 
sethost($req_host) {
        
$this->dbhost $req_host;
        
    }
            
    function 
getdb() {
        return 
$this->db;
    }
            
    function 
setdb($req_db) {
        
$this->db $req_db;
    }
            
    function 
getdbuser() {
        return 
$this->dbuser;
    }
            
    function 
setdbuser($req_user) {
        
$this->dbuser $req_user;
    }
            
    function 
getdbpassword() {
        return 
$this->dbpassword;
    }
            
    function 
setdbpassword($req_password) {
        
$this->dbpassword $req_password;
    }
            
    function 
getsql() {
        return 
$this->sql;
    }
            
    function 
setsql($req_sql) {
        
$this->sql $req_sql;
    }
            
    function 
getnumberrows() {
        return 
$this->numberrows;
    }
            
    function 
setnumberrows($req_numberresults) {
        
$this->numberesults $req_numberresults;
    }
        
    function 
setdbconnection($req_dbconnection) {
        
$this->dbconnection $req_connection;
    }
            
    function 
getdbconnection() {
        return 
$this->dbconnection;
    }
        
    
/*
        This is the constructor for the object. In this case I have set
        the initial values of a number of the object properties to those 
        values declared in the global constants.inc. By doing this, I 
        only need to change the values of these properties for specific
        operations, which we will not need to do throughout this example

    */        
    
function mysqldb() {
    
        global 
$HOST$DB$WEBUSER$WEBPASSWORD;
        global 
$TRUE$FALSE;
        
        
$this->sethost($HOST);
        
$this->setdb($DB);
        
$this->setdbuser($WEBUSER);
        
$this->setdbpassword($WEBPASSWORD);
        
$this->setdbconnection($FALSE);
    
    }
            
    
/*
        These are the methods for the object. They provide for opening a
        connection to the database, closing a connection and executing a 
        SELECT query. Of course, these can be expanded upon to allow for 
        INSERT's, UPDATE's and DELETE's etc...
    */
    
function opendbconnection() {

        global 
$TRUE$FALSE;
                
        
$this->dbconnection mysql_connect("$this->dbhost""$this->dbuser""$this->dbuserpassword");
        if (
$this->dbconnection == $TRUE) {
            
$this->db mysql_select_db("$this->db");
            
$this->setdbconnection($TRUE);
        } else {
            
$this->setdbconnection($FALSE);
            return 
false;
        }
        return 
true;
    }
            
    function 
closedbconnection() {

        if (
$this->dbconnection $TRUE) {
            
mysql_close($this->dbconnection);
        }
                
    }
    
    function 
selectquery() {
        
        global 
$TRUE$FALSE;
    
        if (
$this->dbconnection == $FALSE) {
            
$this->opendbconnection();
        }
    
        
$this->qry mysql_query($this->sql);
        if (!
$this->qry) {
            return 
false;
        } else {
            
$this->numberrows mysql_num_rows($this->qry);
            if (
$this->numberrows 0) {
                for(
$x 0$x $this->numberrows$x++) {
                    
$this->result[$x] = mysql_fetch_row($this->qry);
                }
            } else {
                echo(
"[Error:] Retrieving data");
                return 
false;
            }
            return 
true;
        }
    }
    
}
    
?>
That's it for the data class. Now we need to set up the class's for the actual layout - the boxes!
genericinfo Class
In this particular scenario, we are going to create three classes. That's right, three classes. One for managing the data and two sub-classes for drawing the boxes. Let's start with the managing data class (which we'll call genericinfo). Here's the code:

<?php

class genericinfo {

    
//Set up the Object, reserving memory space for variables
            
    
var $outerwidth;
    var 
$outerbordercolor;
    var 
$outerborderwidth;
    var 
$titlebgcolor;
    var 
$innerwidth;
    var 
$innerbgcolor;
    
    
// Textual variables
    
var $title;
            
    
// Style vairables
    
var $cssboxtitle;
            
    
/*
        Use these functions to get and set the values of this 
        object's variables. This is good OO practice, as it means 
        that datatype checking can be completed and errors raised accordingly.
    */    
    
function setouterwidth($req_outerwidth) {
        
$this->outerwidth $req_outerwidth;
    }
            
    function 
getouterwidth() {
        return 
$this->getouterwidth;
    }
            
    function 
setouterbordercolor($req_outerbordercolor) {
        
$this->outerbordercolor $req_outerbordercolor;
    }
            
    function 
getouterbordercolor() {
        return 
$this->outerbordercolor;
    }
            
    function 
setouterborderwidth($req_outerborderwidth) {
        
$this->outerborderwidth $req_outerborderwidth;
    }
    function 
getouterborderwidth() {
        return 
$this->outerborderwidth;
    }
            
    function 
settitlebgcolor($req_titlebgcolor) {
        
$this->titlebgcolor $req_titlebgcolor;
    }
            
    function 
gettitlebgcolor() {
        return 
$this->titlebgcolor;
    }
            
    function 
setinnerwidth($req_innerwidth) {
        
$this->innerwidth $req_innerwidth;
    }
            
    function 
getinnerwidth() {
        return 
$this->innerwidth;
    }
            
    function 
setinnerbgcolor($req_innerbgcolor) {
        
$this->innerbgcolor $req_innerbgcolor;
    }
    function 
getinnerbgcolor() {
        return 
$this->innerbgcolor;
    }
            
    function 
settitle($req_title) {
        
$this->title $req_title;
    }
            
    function 
gettitle() {
        return 
$this->title;
    }
            
    function 
setcssboxtitle($req_cssboxtitle) {
        
$this->cssboxtitle $req_cssboxtitle;
    }
            
    function 
getcssboxtitle() {
        return 
$this->cssboxtitle;
    }
            
    
/*
        This is the constructor for the object. In this case I have set the initial
        values of a number of the object properties to those values declared in the 
        global constants.inc. By doing this, I only need to change the values of 
        these properties for specific operations, which we will not need to
        do throughout this example
    */    
    
function genericinfo() {
                
        global 
$COLOR_PRIMARY$COLOR_SECONDARY$COLOR_TERTIARY;
        global 
$CSSBOXTITLE;
    
        
$this->setouterwidth(150);
        
$this->setouterbordercolor($COLOR_TERTIARY);
        
$this->setouterborderwidth(1);
        
$this->settitlebgcolor($COLOR_PRIMARY);
        
$this->setinnerwidth(146);
        
$this->setinnerbgcolor($COLOR_SECONDARY);
    
        if (isset(
$CSSBOXTITLE)) {
            
$this->setcssboxtitle($CSSBOXTITLE);
        }
            
    }
    
    
// Methods
            
}

?>
You will no doubt notice from this that there are no methods for the class. Why? Well, I guess in a practical situation you might well choose to combine these three objects into one, simply changing the methods according to the desired box. However, this is a real-life example of using classes and sub-classes ("inheritance") and the reason I have chosen to sub-class is so that as I add more flavours of box (eg, curved corners), I can create further sub-class(es); increasing the portability of the right code.

linkbox Class

This class will take the data which it is supplied with and then generate the linkbox for us. Essentially, a multi-part array is parsd to the linkbox. This means the array will consist of each piece of data, which is made up of two parts - the bit you want the user to see and the bit which is the actual URL.

<?php

class linkbox extends genericinfo {
    
/*
         Set up the Object. You will notice, we have not reserved 
        memory space for variables. In this circumstance it is not necessary.
    */

    /*
        This is the constructor for the linkbox. The only thing this does 
        is to call the constructor of the parent class. Why? Well, whilst 
        PHP manages a certain part of OO, one of the bits it falls down on 
        (at the moment) is constructors within sub-classes. So, to 
        be sure that the sub-class is instantiated with the constructor of 
        the parent class, I simply call the parent constructor. Of course, 
        if I then wanted to override any of the values, I could easily do so.
    */
    
function linkbox() {
        
$this->genericinfo();
    }
    
/*
        This is the only method within the class. Quite simply, as you can see 
        it draws the table(s), placing the required data in the appropriate place.
    */
    
function drawlinkbox() {

        echo(
"<TABLE BORDER=\"$this->outerborderwidth\" CELLPADDING=\"0\" CELLSPACING=\"0\" WIDTH=\"$this->outerwidth\" BORDERCOLOR=\"$this->outerbordercolor\" BGCOLOR=\"$this->titlebgcolor\">");
        echo(
"<TR>");
        echo(
"<TD>");
        if (isset(
$this->cssboxtitle)) {
            echo(
"<DIV CLASS=\"" $this->getcssboxtitle() . "\">");
            echo(
$this->title);
            echo(
"</DIV>");
        } else {
            echo(
$this->title);
        }
        echo(
"</TD>");
        echo(
"</TR>");
        echo(
"<TR>");
        echo(
"<TD>");
        echo(
"<TABLE BORDER=\"0\" CELLPADDING=\"0\" CELLSPACING=\"0\" WIDTH=\"$this->innerwidth\" BGCOLOR=\"$this->innerbgcolor\">");
        echo(
"<TR>");
        echo(
"<TD>");
        echo(
"<UL>");
        for (
$x 0$x count($this->data); $x++) {
            echo(
"<LI><A HREF=\"" $this->data[$x][1] . "\">" $this->data[$x][0] . "</A></LI>");
        }
        echo(
"</UL>");
        echo(
"</TD>");
        echo(
"</TR>");
        echo(
"</TABLE>");
        echo(
"</TD>");
        echo(
"</TR>");
        echo(
"</TABLE>");
    }
}

?>

resultbox Class

This is not dissimilar to the linkbox class, except there is not HREF link invovled. instead we create a two-column nested table and place the parsed data into it.

<?php
class resultbox extends genericinfo {
    
/*
        Set up the Object. You will notice, we have not reserved memory 
        space for variables. In this circumstance it is not necessary.
    */

    //constructor
    
function resultbox() {
        
$this->genericinfo();
    }
    
/*
        This is the only method within the class. Quite simply, as you can see 
        it draws the table(s), placing the required data in the appropriate place.
    */    
    
function drawresultbox() {

        echo(
"<TABLE BORDER=\"$this->outerborderwidth\" CELLPADDING=\"0\" CELLSPACING=\"0\" WIDTH=\"$this->outerwidth\" BORDERCOLOR=\"$this->outerbordercolor\" BGCOLOR=\"$this->titlebgcolor\">");
        echo(
"<TR>");
        echo(
"<TD>");
        if (isset(
$this->cssboxtitle)) {
            echo(
"<DIV CLASS=\"" $this->getcssboxtitle() . "\">");
            echo(
$this->title);
            echo(
"</DIV>");
        } else {
            echo(
$this->title);
        }
        echo(
"</TD>");
        echo(
"</TR>");
        echo(
"<TR>");
        echo(
"<TD>");
        echo(
"<TABLE BORDER=\"0\" CELLPADDING=\"0\" CELLSPACING=\"0\" WIDTH=\"$this->innerwidth\" BGCOLOR=\"$this->innerbgcolor\">");
        for (
$x 0$x count($this->data); $x++) {
            echo(
"<TR>");
            echo(
"<TD>");
            echo(
$this->data[$x][0]);
            echo(
"</TD>");
            echo(
"<TD>");
            echo(
$this->data[$x][1]);
            echo(
"</TD>");
            echo(
"</TR>");
        }
        echo(
"</TABLE>");
        echo(
"</TD>");
        echo(
"</TR>");
        echo(
"</TABLE>");
    }
}

?>
Index.phtml
How do all these fit together? Well, this is probably best shown by building the page, which we are going to use to present the content to our users, namely index.phtml.
1:	<?
2:		include "constants.inc";
3:		include "mysqldb.obj";
4:		include "genericinfo.obj";
5:		include "linkbox.obj";
6:		include "resultbox.obj"
7:	?>
8:	<HTML>
9:		<HEAD>
10:			<TITLE>
11:				<? echo($TITLE); ?>
12:			</TITLE>
13:			<LINK TYPE="text/css" REL="stylesheet" HREF="main.css">
14:		</HEAD>
15:		
16:		<BODY BGCOLOR="#FFFFFF">
17:		
18:		<TABLE BORDER="0" CELLPADDING="10" CELLSPACING="10">
19:			<TR VALIGN="top">
20:				<TD>
21:		<?
22:		
23:			$db0 = new mysqldb();
24:			$db0->setsql("SELECT tem_team, tem_url FROM team ORDER BY tem_id");
25:			if ($db0->selectquery()) {
26:				$lnk = new linkbox();
27:				$lnk->settitle("F1 Teams");
28:				$lnk->data = $db0->result;
29:				$lnk->drawlinkbox();
30:			} else {
31:				echo("[Error:] Unable to connect");
32:			}
33:		
34:		?>
35:				</TD>
36:				<TD>
37:		<?
38:		
39:			$db1 = new mysqldb();
40:			$db1->setsql("	SELECT 
41:						CONCAT(\"<B>\",UPPER(driver.drv_surname), \" \", driver.drv_forename),
42:						SUM(points.pts_teampoints) as totdriverpoints
43:					FROM points 
44:						LEFT JOIN driver ON points.drv_id = driver.drv_id
45:					GROUP BY driver.drv_surname, driver.drv_forename
46:					HAVING totdriverpoints <> 0
47:					ORDER BY totdriverpoints DESC");
48:			
49:			if ($db1->selectquery()) {
50:				$rst = new resultbox();
51:				$rst->setouterwidth(175);
52:				$rst->setinnerwidth(171);
53:				$rst->settitle("F1 Drivers Championship");
54:				$rst->data = $db1->result;
55:				$rst->drawresultbox();
56:			} else {
57:				echo("[Error:] Unable to connect");
58:			}
59:		
60:		?>
61:				</TD>
62:				<TD>
63:			<?
64:		
65:			$db2 = new mysqldb();
66:			$db2->setsql("	SELECT 
67:						team.tem_team, 
68:						SUM(points.pts_teampoints) as totteampoints
69:					FROM points 
70:						LEFT JOIN team ON points.tem_id = team.tem_id
71:					GROUP BY team.tem_team
72:					HAVING totteampoints > 0
73:					ORDER BY totteampoints DESC");
74:			
75:			if ($db2->selectquery()) {
76:				$rst = new resultbox();
77:				$rst->setouterwidth(175);
78:				$rst->setinnerwidth(171);
79:				$rst->settitle("F1 Constructor's Championship");
80:				$rst->data = $db2->result;
81:				$rst->drawresultbox();
82:			} else {
83:				echo("[Error:] Unable to connect");
84:			}
85:		
86:		?>
87:				</TD>
88:			</TR>
89:		</TABLE>
90:		</BODY>
91:	</HTML>
Explanation:
Lines 1 - 7 These lines bring in all the files we have previously written, thereby making their contents available. Notice, how the constants.inc file is the first to be included. If it's not, it's not going to be global!
Lines 8 - 20 Here we break out into good ol' HTML, to setup the page and bring in the style sheet.
Lines 21 - 34 Back into PHP to start using the objects.
The first item we are going to create is the F1 Teams box.
Line 23 Instantiates the mysqldb class, creating an object of type mysqldb; to be referenced by $db0. This will therefore execute the constructor function in the mysqldb class, setting initial values to those set by the global constants in constants.inc.
Line 24 Calls the setsql($req_sql) function to set the sql property on the $db0 object.
Line 25 Is an interesting line. Basically, if the call to selectquery() within the mysqldb object fails (or returns as false), execution skips to line 30.
Assuming line 25 returns true, we have the data! Not so bad eh? Now let's work with drawing the box.
Line 26 instantiates the linkbox() class creating an object of type linkbox to be referenced by $lnk. This will therefore execute the constructor function of the linkbox class, which in turn executes the constructor function of the parent class (genericinfo).
Line 27 sets the title of the linkbox, by calling the settitle($req_title) of the parent class.
Line 28 makes sure that the data available to the $lnk is the same as that which is currently in the $db0 object.
Line 29 calls the drawlinkbox() function of the linkbox class. Because $lnk now has direct access to all of the data, it is able to complete the drawing of the linkbox.One point to note in the drawlinkbox function is on lines 16 - 22. If the global constant $CSSBOXTITLE is set, then the style will be included, otherwise it won't.
That's it! You now have the News box drawn on screen!
Lines 35 - 36 Back into HTML to close the table cell and open a new one.
At this point, you should be able to walk through the remainder of the page to see how the other objects are drawn.
main.css
Finally, here is the Style Sheet, which will be used throughout! Not much more to say on this ;-)
1:	<STYLE>
2:		.bugresolver {
3:		
4:		}
5:		TD {
6:			font-family:		verdana, arial, courier;
7:			font-size:		10;
8:		}
9:	
10:		P {
11:			font-family:		verdana, arial, courier;
12:			font-size:		20;
13:		}
14:		
15:		A {
16:			font-family:		verdana, arial, courier;
17:			font-size:		12;
18:			color:			#000084;
19:			text-decoration:	none;
20:			font-weight:		bold;
21:			text-align:		right;
22:		}
23:	
24:		A:hover {
25:			font-family:		verdana, arial, courier;
26:			font-size:		12;
27:			color:			#990000;
28:			background-color:	#DCDADA;
29:			text-decoration:	none;
30:			font-weight:		bold;
31:			text-align:		right;
32:		}
33:	
34:		UL {
35:			margin-left:		25;
36:		}
37:		.boxtitle {
38:			font-family:		verdana, arial, courier;
39:			font-size:		14;
40:			color:			#FFFFFF;
41:			font-weight:		bold;
42:			text-align:		center;
43:		}
44:		
45:		.lnkBox {
46:			font-family:		verdana, arial, courier;
47:			font-size:		10;
48:			color:			#000084;
49:			margin-left:		5px;
50:			text-align:		left;
51:		}
52:	</STYLE>
Example/Download
For those of you who would like to download this example and give it a try for yourself, a ZIP archive is attached.
To Those Of You With OO Expereice
I am well aware there are a number of "holes" in ths code. For example, I have not completed and data validation when setting and returning property values. However, bear in mind the purpose of this exercise is to give people a "real-world" example (which they can follow) of the process and function of OO within PHP.

To Those Of You Without OO Experience

This is a "real-world" example of the process and function of OO within PHP. There are a number of "holes" within the code. That is no to sat the code is incorrect or wrong, but its function is to show you how to work with OO.
If you're looking for further guidance on developing this code into your site, you should revisit many of the get and set functions within these pages and make sure you develop routines to handle the data being parsed; make sure it is of the correct data-type (eg it is numeric when it should be and it is a valid SQL statement when it should be). Bear in mind, the data these classes are working with is from a pre-populated database, where I knew the data was valid etc....
For more examples of where these type of boxes are used, please visit e-sphere.net, f1circle.com and markaw.com.

About the Author

Professinally, Mark Williams, is, at the moment, the Senior Technical Consultant to one of Europe's top 10 Insurance/Assurance companies. he is working on a number of Internet/e-commerce initiatives inlcluding WAP technology, Video Conferencing, Windows 2000, Linux etc....
To relax, mark is a great F1 fan and on every other Sunday (during the season) you will always find him keeping up to date with the latest events.
More information is available from markaw.com, e-sphere.net and f1circle.com.