The hardest concept I've tried to understand since beginning to use PHP
was that of classes. I'd never used a database engine but learning to
use MySQL, at least for the more basic functions, was a breeze. Having
never used OOP before, classes were novel as well, but understanding the
theory and why it was useful escaped me. I knew they must be powerful
as "everything" is programmed using OOP, but for the life of me,
although I thought I understood the mechanics, I couldn't see the
usefulness. Then, just a few days ago, while trying to figure out how
to do something with regular functions it hit me just how doing it with
objects would make my job much simpler! I'm going to try to explain
about them in plain English and hopefully help others like myself.
Classes are nothing more than a collection of variables and functions
acting on those variables. They provide a means of thinking about
things in real world terms. In other words they describe an object. An
object, or instance, of a class is an actual "living, breathing"
structure of that class. Let's say we want to describe a bicycle. A
proper class of a bicycle might have the variables $pedals, $chain,
$front wheel, $rear wheel, $brakes, and $handle_bars. Functions of the
bicycle would include Stop(), Accelerate(), Coast(), TurnLeft() and
TurnRight(). You can think of your script as the entity operating that
bike. The function Accelerate() could be passed an argument such as
$Braking_Force and use that information along with the defined instance
variables (probably $brakes and $wheels)
and output some result back to your script.
Interesting, but couldn't all this be accomplished using regular
variables and functions? Yes it could, and if you only had one bike in
your script it probably wouldn't make much sense to define a class just
for it. But what if you needed several bicycles? It could become quite
complex keeping track of all those variables and making sure you pass
the correct variables to the different functions, and using objects cuts
down on the number of variables you need to pass because the function
automatically has available to it all the variables describing the
object the function is acting upon. Also, class definitions are easily
included in different scripts and you'll be assured that a bicycle works
the same way in each script!
Let's create a class that I actually use on almost every page on my site
and you might find useful, too.
I don't know about you, but when I'm writing a dynamic web page I hate
to have to stop thinking about the logical flow to worry about properly
formatting my HTML. As a consequence of this, I often end up with not so
attractive pages because I don't want to worry about font faces and
sizes, or background and text colors. The solution: using a PHP class
to set HTML output attributes with functions to format the text!
I call the class "Style". It contains the following variables that set
important HTML attributes for formatting the output:
<?php
class Style {
var $text;
var $alink;
var $vlink;
var $link;
var $bgcol;
var $face;
var $size;
var $align;
var $valign;
}
?>
I'm sure you're familiar with HTML so the variables should be self-
explanatory. Next I created a function for Style called Style:
<?php
class Style {
function Style ($text="#000000",$alink="#AA00AA",
$vlink="#AA00AA",$link="#3333FF",
$bgcol="#999999",$face="Book Antiqua",$size=3,$align="CENTER",$valign="TOP") {
$this->text=$text;
$this->alink=$alink;
$this->vlink=$vlink;
$this->link=$link;
$this->bgcol=$bgcol;
$this->face=$face;
$this->size=$size;
$this->align=$align;
$this->valign=$valign;
}
}
?>
When you create a function within a class with the same name as the
class that function will execute whenever you create an object of that
class. This is called a 'constructor.' It allows me to have default
values for each attribute automatically whenever I create an object:
<?php $Basic = new Style; ?>
You define an instance of a class by giving it a name ($Basic) and
assigning it "=new ClassName;".
You could also send different values for the variables as arguments when
declaring the new Style, but if you declare a value you have to declare
all of them that occur to the right of the one you declare (class
functions work just like regular functions in this respect). Meaning if
you set 'text' to something different than the default given in Style,
then you have to declare all the variables. There is an easier way. We
can create a function that changes a single variable within the class:
<?php
Function Set($varname,$value) {
$this->$varname=$value;
}
?>
So to change the value of particular variable of an instance we can:
<?php $Basic->Set('size','2'); ?>
You use the "->" operator to refer to a variable or a function of an
instance. So the above tells the interpreter "Run function 'Set()' of
instance '$Basic'." It knows that "$Basic" is an instance of class
"Styles" because we declared it so. Similarly we can refer to variables
of an instance in the same manner (e.g. $Basic->text).
Let's create a Style for table headers that has some slightly different
attributes.
<?php
$Theader= new Style;
$Theader->Set('text','#0000FF');
$Theader->Set('bgcol','#000000');
?>
There, that's good enough. Now my table header has blue text on a black
background. I want my table body to be a slightly lighter gray than my
main page, black is good for text, but maybe I'll make the text smaller:
<?php
$Tbody=new Style;
$Tbody->Set('bgcol','#AAAAAA');
$Tbody->Set('size',2);
?>
Great, now what to we do with it? I'm glad you asked. We need to
create a few more functions within Style to actually accomplish
anything. The first thing I'd like to do is set up my page body so I
did this:
<?php
function Body() {
PRINT "<BODY BGCOLOR=\"$this->bgcol\" ".
"TEXT=\"$this->text\" ".
"LINK=\"$this->link\" VLINK=\"$this->vlink\" ".
"ALINK=\"$this->alink\"><FONT ".
"FACE=\"$this->face\" SIZE=$this->size>\n";
}
?>
This sets up the page body for us. It also illustrates a new variable
"$this." When used inside of a class function it lets the interpreter
know we are referring to a variable of THIS instance. In other words,
it's assigned the value of the name of the instance in the calling line
(e.g. $this would be == $Basic when $Basic->Body() is the calling
statement.) Also, notice we are doing something here that's much
simpler than is possible in regular functions. We're referring to
variables that were not passed to the function. Remember, all functions
and variables of an instance are available to all functions of that
instance. To do this with regular functions you'd have to set up
several global arrays.
Try this in your php script (assuming you've included the Style class,
created the style objects above and sent the <HTML> and <HEAD></HEAD>
tags):
<?php $Basic->Body(); ?>
Now, we're ready to print something out. We could do it the old
fashioned way, but I'm going to do something different... that's right
another function:
<?php
function TextOut($message=" ") {
PRINT "<FONT FACE=\"$this->face\" ".
"SIZE=$this->size COLOR=\"$this-> ".
"text\">$message</FONT>\n";
}
?>
This function will take a message passed in the argument and print it
out in the appropriate style object. So to print a message we can:
<?php
$Basic->TextOut('This is my test message');
$Tbody->TextOut(' -- kinda neat, huh?');
?>
Notice, there are no <BR> between the two function calls so they will
print on the same line. Also, I just wanted a smaller font for the
second part of the output and I had already declared that in $Tbody so I
used that. This is safe in this instance as the only other difference
between $Basic and $Tbody is "bgcol" and that isn't used in this
function. Notice the " " in the function declaration? That is
there so if no message is passed the function will print out a non-
breaking space. Why will become clear later on.
So far we haven't saved a lot of work. The last example is easier if
you want to change font color and/or size in the middle of a sentence
but still doesn't justify writing an entire class. How about we add to
the functions:
<?php
function TDOut ($message=" ",$colspan=1) {
PRINT "<TD COLSPAN=$colspan BGCOLOR=\"$this->bgcol\" ".
"ALIGN=\"$this->align\" VALIGN=\"$this->valign\">";
$this->TextOut($message);
PRINT "</TD>\n";
}
?>
Now, we're getting somewhere! Remember, I wanted to have different
background colors for my tables. Now I can do this:
<TABLE>
<TR>
<?php
$Theader->TDOut("Name",2);
$Theader->TDOut("Location",3);
?>
</TR>
<tr>
<?php
$Theader->TDOut("Last");
$Theader->TDOut("First");
$Theader->TDOut("City");
$Theader->TDOut("State/Province");
$Theader->TDOut("Country");
?>
</tr>
There. See how the colspan argument works. If it's not declared it
defaults to 1. So in the first table row "Name" spans 2 columns and
"Location" spans 3. In the second row all of them cover a single
column.
Let's do the table body now:
<TR>
<?php
$Tbody->TDOut("Kreisler");
$Tbody->TDOut("Rod");
$Tbody->TDOut("Cortlandt");
$Tbody->TDOut("New York");
$Tbody->TDOut("USA");
?>
</TR>
But this is still getting kind of lengthy. Couldn't we save some more
steps? How about trying this:
<?php
function TROut($message) { /*And NO comments about fish, please! ;) */
PRINT "<TR>\n";
$cells=explode("|",$message);
$iterations=count($cells);
$i=0;
while ($i<$iterations) {
list($message,$span)=explode(":",$cells[$i]);
if (strlen($message)<1) $message=" ";
if ($span){
$this->TDOut ($message,$span);
}else{
$this->TDOut ($message);
}
$i++;
}
PRINT "</TR>\n";
}
?>
Wow! That's a little more complicated. Let's break it down:
- Line 3 splits the message on pipes and stores the pieces in the array
$cells.
- Line 4 stores the number of items (number of cells) in $iterations.
- Line 6 begins our loop to go through these cell items.
- Line 7 splits the cell data at the colon and stores them into the
variables $message and $span.
- Line 8 checks to see if a message was included. If not then it sets
message to the default.
- Line 9 checks to see if there was a span listed (i.e. the cell data had
a colon with something behind it.
- if so, Line 10 calls TDOut with the message and the number of cells it
spans.
- if not, Line 12 calls TDOut with just the message (TDOut will set
$colspan to the default 1).
Lastly, we close out the row.
What this means is we can pass a single string to TROut that will
contain all the necessary information to print out the entire row as
long as the string is in the format
celldata[:colspan]|celldata[:colspan]|......celldata[:colspan].
So, instead of all the work we did before, the headers and body of the
table could be called like this:
<TABLE>
<?php
$Theader->TROut("Name:2|Address:3");
$Theader->TROut("First|Last|City|State/Province|Country");
$Tbody->TROut("Rod|Kreisler|Cortlandt|New York|USA");
?>
</TABLE>
Wow. That's a lot easier. But what if the data is in variables? Simply
join the array:
<?php
$message=join($arry,"|");
$Tbody->TROut($message);
?>
Of course, you can't enter column span information with one join, but
you can with extra joins. Let's say your array consists of six elements
and the third and fifth elements need to span 2 and 3 columns
respectively. To do this you can join the ":#" to an intermediate
array:
$newarray=$arry;
$newarray[2]=join(list($newarray[2],"2"),":");
$newarray[4]=join(list($newarray[4],"3"),":");
$message=join($newarray,"|");
$Tbody->TROut($message);
Obviously, even more functionality can be added to the class. Also, the
code is not idiot proof. If enough people ask, I will add more
functionality (email me with your ideas), idiot proof the code and post
it.
--Rod