Main Page Content
Php Intro To Objects And Classes
PHP's Object Oriented capabilities may not be complete, but the benefits of
convenience and code re-use from using Objects and Classes are here now. If you are already dabbling in PHP, but haven't yet checked out Classes, have a look at this simple (yet useful) example of an Error Message Class.Beginners Guide to Object Oriented Terminology
When I first came across OOP, the terminology served as a barrier - partly
because the people explaining it were trying to stress what is different about OOP, rather than saying what is the same. I was a tad disappointed when I discovered that OOP was mainly a way of packaging existing good ideas and practices, but cheered up when it transpired that a lot of languages now include OOP constructs that make it much easier to code in this way.There are four terms that you should feel comfortable with: Class, Object,
Property and Method.A Class is a blueprint for creating an Object. It tells you what variables
(a.k.a. Properties) the Object can have, and what functions (a.k.a. Methods) can be used to manipulate the Object's Properties.None the wiser? I don't pretend that the previous couple of sentences constitute
a proper definition of anything - but it was definitions that created the barrier for me when I first looked at OOP. Don't get hung up on it - take a look at this non-computing example:A Real-World Example
Consider an old-fashioned radio, with an on/off switch, and a tuning dial.
The Class is the circuit diagram and assembly instructions that enable the
worker to build the radio, and also the User Manual.Each and every such radio built is an Object.
The methods would be something like:
Build Radio ( )
Turn Radio Off ( )
Tune Radio ( )
The Properties might be:
serial_number - to uniquely identify the radio.
current frequency - to which the radio is tuned.
Every Object has the same methods available. Every Object has values for the
same Properties - but these values may be different. Indeed in our radio example, the serial number should always be different.Still none the wiser? Well try working through the following example, and see
if things click into place.A Problem to Solve
If you have any complexity at all in your PHP pages, then you will most likely
have run into this irritating little scenario: You have just found a database error perhaps, or a user input error, but you cannot immediately output an error message. What do you do? Set a flag and check it later on when you are ready to show the error message? OK, that should work. Might get a bit messy if you find a lot of errors. You could stick all the error messages in an array, then print out the array when you are ready. Yeah, that sounds good. Lets do that - but lets do it using a Class, which we will call ErrorMessages.So What Does This Class Need to Look Like?
Well what do we want to do with it? Let me suggest the following:
We want to be able to identify errors and store error messages for outputting
later, when we are ready.We might have different types of errors - e.g. System errors such as unavailable
databases, or User errors such as invalid data put in input fields on forms. It would be nice to report these separately.The output should fit in with the look and feel of the rest of the website.
No doubt you are already concocting more complicated requirements, but lets
leave it at that for the purposes of this article. What you do in the privacy of your own room is up to you.Right then. That doesn't look too complicated. Seems to me like we want three
Methods here. One to create a bucket which we can keep error messages in. Another to drop an error message into the bucket, and a final one to tip all the error messages out of the bucket and onto the screen.I'm anticipating output that will look something like this:
*** System Errors ***
2. Database error - no such column 'fifth' in table 'usa'.
If we stick a few [CSS] class names in the HTML tags that we output, then we
should be able to use a stylesheet to format the output to fit in with whatever site we use it on.The Class Framework
It ain't obligatory, but most PHP developers put their classes in separate
files that they can include in whichever pages it is needed. You can put as many Classes as you like inside one include file, but for this example we will just have the one.OK - so create a new file to use as an include: say error_class.inc.
Open the include file and type in:
<?phpclass ErrorMessages{}
?>
This is just the framework that tells PHP that it is dealing with a Class definition.
Now we need to tell PHP what the variables are that we may choose to populate
for each Object. Now what was it that they were called again? Properties. OK, between the curly wurlies, add this:var $errorMsgBlockLabel; var $errorCount; var $errorMsg;
$errorMsgBlockLabel is the 'title' that we will show on the screen above the
errors that have occured.$errorCount is just going to keep track of the number of errors.
$errorMsg is going to be an array of errors that have occured. Each array element
will itself be an array (containing a label and a message) but we don't need to be that specific at this stage.The Constructor
We now need to add the only Method that a Class MUST have. It has the same
name as the Class itself (in this case ErrorMessages) and is refered to as the 'Constructor'. This Method will do whatever is necessary to create a new instance of an ErrorMessages Object.So what do we need to do to create our new Object? Not a lot, really. We need
to give it a 'title' (i.e. specify a value for $errorMsgBlockLabel) and initialise the $errorCount to 0.OK - so after your var definitions, before the final curlie wurlie, insert
this Constructor function:function ErrorMessages($x){ $this->errorCount = 0; $this->errorMsgBlockLabel = $x;}
$x, rather obviously, is the 'title' string, which we will pass when we request
a new ErrorMessages Object.$this
Whats all this $this->errorCount
business?
In normal PHP you would refer to a variable as something like $errorCount
,
addError($label,$message)
. When we are dealing with Properties and Methods we must use a slightly different
notation. e.g.$SysErrs->errorCount
and $SysErrs->addError($label,$message)
where $SysErrs is the name of the Object that we are interested in.Inside the Class definition we have absolutely no idea what the Objects based
on this Class are going to be called, so we use the special pseudo-variable$this
to mean the current Object. It just allows us to use a similar notation inside and outside the Class definition.The exception to this object name->method name( )
rule
object name = new class name&(params)
e.g. $UserErrs = new ErrorMessages("User Bloopers");
Can't wait to use your class?
Create a php file - say 'error_class_test.php'. I'll assume you create it in
the same directory as the include file. Type this in:<?phpinclude ("error_class.inc");$UserErrs = new ErrorMessages("User Bloopers");echo ("<P>We have $UserErrs->errorCount user errors</P>");?>
Load up both files to your webserver, then run the php file. It should just
say "We have 0 user errors".Not very exciting, but as it stands that is the limit of our Classes functionality.
Lets change that.Now some Methods
First, a method to add an error message. Immediately after the Constructor,
before the last curlie wurlie, add this:function addError($label, $message){ $this->errorCount ++; $this->errorMsg[$this->errorCount]["label"] = $label; $this->errorMsg[$this->errorCount]["message"] = $message;}
This Method increments the errorCount, then creates an element errorMsg[n]
where [n] is the current errorCount. This element is itself an associative array ("label" => $label , "message" => $message).Now we can add an error, e.g.
$UserErrs->addError("Dodgy Input","This does not appear
and we could even show that error using something like print_r
- but even if
<pre></pre>
tags around it, that would still look naff. No, we definitely need a Method to output these error messages in a decent format. So add this immediately before the last curlie wurlie:function showErrors(){ if ($this->errorCount >0) { ?> <div class="errorblock"> <span class="errorblocklabel"><?php echo $this->errorMsgBlockLabel ?></span> <?php for ( $i = 1; $i <= $this->errorCount; $i++ ) { ?> <p> <span class="errornumber"><?php echo "$i:"?></span> <span class="errorlabel"><?php echo $this->errorMsg[$i]["label"] . " - " ?></span> <span class="errormessage"><?php echo $this->errorMsg[$i]["message"] ?></span> </p> <?php } ?> </div> <?php }}
Nothing too tricky here. The if
statement ensures we only get any output
To test it, you can add a couple of lines to your php file, so it looks like
this:<?phpinclude ("error_class.inc");$UserErrs = new ErrorMessages("User Bloopers");$UserErrs->addError("Invalid Input","This does not appear to be a valid email address");$UserErrs->addError("Careless","You appear to have forgotten something.");$UserErrs->showErrors();echo ("<P>We have $UserErrs->errorCount user errors</P>");?>
Eye Candy
OK so far? But it doesn't look very nice. We could just put a bit of formatting
into the showErrors() function - but being a CSS fan, I reckon its better to use styles. I'm not going to explain it, 'cos this article is about Classes not CSS.Stick the following stylesheet in (right at the start of your php file, before
the opening php tag). It should really go in the <head> section - but if you've been following the instructions you won't have one!<style type="text/css">.errorblock { border: 1px solid #900 ; margin-bottom: 5px; margin-top: 5px; font-size: 10pts; background: #fee; }.errornumber { background: #fff; color: #900; padding-left: 3px; padding-right: 1px; border: 1px solid #900 ; }.errorlabel { font-weight: bold; color: #900; padding-left: 3px; }.errormessage { color: #090; }.errorblock p { display: block; padding: 3px; margin-bottom: 0; margin-top: 0; padding-top: 2px; padding-bottom: 2px; }.errorblocklabel { display: block; padding: 3px; background: #900; font-weight: bold; font-size: 12pts; color: #fff; }</style>
Did I mention Re-usability?
Well if I didn't, shame on me. I'm certainly not going to claim that Classes
are the only way of writing reusable code - but they certainly make it an attractive proposition.Suppose we might encounter System errors and/or User Errors while running our
PHP code. We might not want to lump all our errors in together. No problem. Instead of just creating one ErrorMessages Object, we can create as many as we want (but two will do for us).Add a few extra lines to the php part of your .php page, so it looks like this:
<style type="text/css">... snip ...</style><?phpinclude ("error_class.inc");$UserErrs = new ErrorMessages("User Bloopers");$SysErrs = new ErrorMessages("*** System Errors ***");$UserErrs->addError("Invalid Input","This does not appear to be a valid email address");
$UserErrs->addError("Careless","You appear to have forgotten something.");$SysErrs->addError("Missing Table","could not locate database table 'awol'");$SysErrs->addError("Database error","no such column 'fifth' in table 'usa'.");$UserErrs->showErrors();
$SysErrs->showErrors();echo ("<P>We have $UserErrs->errorCount user errors</P>");
echo ("<P>We have $SysErrs->errorCount system errors</P>");?>
Stick that on your server and give it a whirl.
Neat?
But the real re-use comes when you use the Class in another project. All you
need to do is copy the Class definition include file and include it wherever you need it.Using our Objects within Functions and other Objects
Right then, but what if you already have lots of functions or even other Classes,
within which you might generate error messages? Can you still use the ErrorMessages Class?Of course. The Object you create can be treated much like a variable. If you
want to use it in a function (or Method in another Class) you can declare it as global. Note that it is the Object not the Class that is declared as global - so you must know its name. e.g.function fooBar() { global $SysErrs; ... do something that goes wrong ... if (error has occured) { $SysErrs->addError("Woopsie","Function fooBar failed"); }}
But what would happen if you hadn't already created the $SysErrs Object? You
would get an error. Ironic really.What can you do about it? Test for the existence of the Object. If it exists,
fine. If not, handle it some other way - maybe by creating the object - but you can only do that if the Class definition itself exists.So we have a few conditions to test for before we know exactly how we are going
to deal with our error. How you deal with each failure depends on your situation, and the anticipated situations where your code might be used. So I'll just give you the syntax for checking whether the ErrorMessages Class exists: if (class_exists("ErrorMessages")) { ..... }
and for checking if the Object $SysErrs exists:
if($SysErrs) { ..... }
If the Class doesn't exist, then you could include the Class definition file
- as long as you are sure where it is!If the Object doesn't exist, then you could create it, or you could cop out
and handle the error some other way.Of course - if you create an ErrorMessages Object, you would be wasting your
time if the showErrors() method was never called for that Object. Crikey, in my crystal ball I can see a routine that looks through all the Objects in your script, and calls showErrors() for every Object with a Class of ErrorMessages. Stop now, I've woffled enough.Possible advantages of Classes over Procedural Code
Treading very carefully now, as some folks seem awfully touchy about this.
The scribblings that follow are definitely IMHO. So please don't give me a kicking. And if you must give me a kicking, then please, not the face - not the face!Nothing in this example could not be done with a set of functions in an include
file. Hey, it is an easy example, because it gets hard to write articles when the examples are big and complex.Quite probably, even with big, complex Classes, you could still do everything
with a set of functions instead.So why do I like Classes?
Keeping variables as Object Properties saves you from having to use global
variables. Good for security. It also avoids accidentally using the same variable across two different sets of functions.All Object Properties are available to all functions (a.k.a. Methods) within
the Class. So you are not having to specify lots of variables as global within each function, or pass them in as parameters.Class definitions make me keep everything to do with the Class in one place.
With collections of functions it is easier to write code that is harder to maintain.Just thinking in terms of Classes, Properties and Methods seems to help get
my mind around the problem.If you are using Sessions, by registering the Object with the Session, all
the Object's Properties are automatically serialized, stored in the session and then unserialized for you on the next page. Handy.Want to know more?
Here is a list of useful links.
Enjoy.Feeling Lazy?
Personally, I often get benefit from actually typing things in - particularly
when dealing with some new notation such as that used by PHP for classes and objects. If that doesn't float your boat, here is a zip file with the class definition and the test page.Even less effort would be to look at the demo
page.