Magazine PDF Issue Conference Forum Software & Support Verlag











from PHP Magazine - International Edition Issue: 01.2003

Taking PHP the OO way

Object Oriented Programming (OOP).
Leendert Brouwer

A concept of OOP is often overlooked, misinterpreted or barely touched by numerous books and articles about our favourite language, yet one that proves to be very powerful when used correctly. The future for applying Object Oriented Programming in PHP looks bright, when we look at the new OO features that PHP 5/Zend Engine 2 is going to bring us. It will make this approach much more convenient. With the right tools, all you need is the knowledge. This article will try to explain what objects really are and how they can be recognized, and it will familiarize you with the so called "3 Pillars" that form the foundation of Object Oriented Programming: Encapsulation, Inheritance and Polymorphism.

Object Oriented Programming, as the name implies, is about programming with objects. But what exactly is an object? Actually, let me start by stating what an object is not. An object is definitely not just a class stuffed with a bunch of functionalities. This might sound logical to you, but too many times, when I read code on the net, this is what I see. It is easy to make that mistake too: when you discover OO functionality in a procedural language such as PHP, sooner or later the temptation to use it becomes too strong, and you give it a shot without even knowing the first thing about the underlying theory. This is all very nice, but it is not what Object Oriented Programming is about. The textbox defines what an object is:

The definition of an object
An object is an entity that encapsulates properties and behaviour that is specific to that entity.

This might sound a little strange, but it really is not. Objects are all around you. Some objects can consist of even more objects. The magazine you are holding is an object. The articles in it are objects. The window in your room is an object. Your room is an object. Everything you can see is an object. But there is a lot more to OOP as a concept. Throughout the years, many methodologies in regard to the object oriented approach have been developed. We can even see three stages in the object oriented development process: Object Oriented Analysis (OOA), Object Oriented Design (OOD) and Object Oriented Programming (OOP), each with their own theory and their own rules. It is a huge concept, and it can take quite some time to fully master this approach, if that is even possible. For example, design patterns (they are basically proven solutions to standard design problems) are still progressing heavily. The end is certainly not in sight yet, and that makes it even more fascinating. It is by far not only a matter of syntax; it is a way of thinking, a way of practicing and a way of applying. Do not let these facts discourage you though - you can come a long way by understanding the basic principles of the OO methodology.
There are many advantages to the object oriented approach, of which reusability, extendibility and maintainability are the most important ones. I will explain why these advantages exist when going the OO way:
  • Reusability: Objects can stand on their own. They are abstracted; they represent one thing. This means that they can be combined in many ways, which makes for (and encourages) reusability. Reusing objects rather than having to reinvent the wheel over and over again can save a lot of time.
  • Extendibility: Instead of writing a completely new object every time you need one (which often takes quite some time), you can often extend one. It is in the nature of objects that they are extendable. One can derive an object from another object, and thus extend its functionality without having to rewrite the whole object and add the required functionality.
  • Maintainability: Because of the very natural way, objects (and their hierarchies) can be designed, they are easy to read, which makes it easier to analyse, and thus extend already existing applications. Because of the "pluggable" nature of objects, less code modification is needed to integrate new features into an application.

Misconceptions about applying OOP in PHP
Before I continue I would like to get a few misconceptions about OOP in PHP out of the way. Often there have been debates about the lack of support PHP(4) has when it comes to applying OOP. I would like to try to set the record straight on a few of those points of discussion.
  • Lack of access modifiers: Although this will change in PHP 5 (the private, public and protected modifiers will be introduced), in my opinion this is not a valid argument against being able to apply OOP in PHP. Access modifiers are in no way meant for data security, they are merely supposed to give the programmer the benefit of not having to worry about accidentally accessing object members that should not be touched. You just have to be a bit more careful if you do not have them. The interpreter not forcing you to properly apply OO theory does not mean you cannot apply the theory.
  • Lack of abstract classes: Again, this will not prevent you from coding the OOP way, even by theory. Having an abstract class basically means that you have to implement its abstract methods when subclassing it, and not being able to make an instance of it. Some languages (like Java) have built-in support for this. While this can be very comfortable, you can live without and simply conform to code conventions. You can simply override an empty method in a superclass in one of its subclasses (which I will show in the Polymorphism part later in this article).
  • No support for multiple inheritance: While some may find multiple inheritances comforting, it is often considered as bad practice. You can live without. When you take object oriented analysis in mind, how often do you run into situations where you want to naturally let an object derive from two objects at the same time, and still maintain an "is-a" relationship with its superclasses (in case you do not know, these relationships will be explained later in this article)?
Although opinions differ, I believe that these supposed limitations in PHP's OOP support are easily compensated, and are not a valid reason to discount using OOP style in PHP programming.

Recognizing objects
When learning new programming techniques, it is often helpful to use real-world analogies to demonstrate concepts - in this case, we will use a car. Let's take a look at what a car has (properties) and what it can do (behaviour).
What a car has (properties):
  • windows
  • wheels
  • an engine
  • doors
At this point, it is important to realize that the properties can be objects by themselves, with their own properties and behaviours. In OOP terminology, this is called composition. You could say that a car is 'composed' by putting together objects that a car must have. You could even take this further, and conclude that these objects are composed of other objects, and so on. When developing however, make sure that you stay focused when overanalyzing things and thus going outside the scope of your project; it is better to implement only what you need. Now let's take a look at some things a car can do.
What a car can do:
  • accelerate
  • brake
  • open doors
These sorts of things determine the behaviour of a car. They are specific to the car itself. You must be able to recognize objects in real life to be able to create a good OO architecture. It is not a silly thought to practice "thinking in objects" in real life. Look around you. The whole world can be seen as if it was made out of objects. Try to recognize objects around you; it will start to make sense when you do. Take your personal computer, for example. It is composed of many pieces of hardware, each with their own properties and behaviour. Your clothing closet - most likely composed with a number of drawers; and so on.

Pillar 1: Encapsulation
From here we must see things from a different angle. Can a car be driven by itself? Of course not, which is why it needs a driver. Thinking in forms of object, the driver would be a seperate entity. It is important to see this distinction: objects should have their own responsibilities. They should only be able to do the things that they are supposed to be doing, nothing more. Ideally, the properties of an object should only be able to be influenced by its behaviour. One object directly manipulating another object's properties is usually bad practice. It is up to the behaviour to determine how properties can be manipulated. The object should keep its details to itself, and only expose its interface (or: behaviour) to the outside. Another object can then influence its details through the interface. It is time to illustrate this theory by giving an example: a car manufacturer decides to offer a car, but only in 3 colors: red, green and blue. To clearly see where responsibility comes in, I will give the bad example first (note that syntax explanation is out of the scope of this article, this can be found in the PHP manual under Class/Object functions):

<?php
class Car
{
var $color;
}

$specificCar = new Car();
$specificCar->color = "yellow";
?>

Wrong. Our rule was that the car should only be either red, green or blue. However, we manipulated the object's colour property directly. It did not have an interface to take care of its responsibilities. You will find a better example in listing 1.

Listing 1

<?php
class Car
{
var $color;
var $possibleColors = array("red", "green", "blue");

function setColor($color)
{
if (in_array($color, $this->possibleColors)) {
$this->color = $color;
}
}
}

$specificCar = new Car();
$specificCar->setColor("yellow"); //would not alter the object

$specificCar->setColor("red"); //would alter the object
?>

Try to understand this code, and you will see why this is the better way. This time, our car has an interface through which we can modify its properties. The method (functions inside a class are called methods) setColor($color) takes care of what can and cannot happen to the car's properties. Given that the interface is programmed correctly, we know that nothing bad will happen to its properties, as in the preceding example, when assigning the wrong value to them. The interface will simply take care of that. The method setColor($color) is called a modifier. It modifies a property of the car object. Next to modifiers, we also have accessors. An accessor is used to return a property, instead of modifying it. Listing 2 shows an example of an accessor.

Listing 2

<?php
class Car
{
var $color;

/*
* constructor, code inside this function is executed on object
* initialisation
* note that in PHP 5 the constructor will have to have the name
* __construct(), instead of the class name
* although this way of constructing will still work as long as no
*___construct() is found
*/
function Car()
{
$this->color = "red";
}

// Accessor
function getColor()
{
return $this->color;
}
}
?>

In listing 2, the method getColor() is called an accessor. Accessors can also manipulate data before returning it. It is good practice to always use accessors instead of referring directly to the object's internal properties. This way, you can maintain a clear responsibility layer. Now, I just know that some of you are going to say: "Won't I still be able to manipulate an object's properties directly anyway?". This is correct. At the time of this writing, PHP has had no concept of access modifiers like private in for example Java or C++. This will change in PHP 5 however, where the private access modifier will be introduced, thus you can force restricted responsibility without having to worry about your private parts being manipulated.
It is important that the behaviour of your object is clearly defined through the interface (or: methods). Just good abstraction does not make for reusability. A well-written interface is also very important when the object is going to be (re)used. Only by looking at the interface, it should be clear what the object can do. Always try coding by using self-explanatory names for your classes, methods and variables. The name should briefly but clearly represent what it does. Do not be afraid to write a few extra characters if you think that would improve the readability of your interface.
To sum it up, encapsulation means that:
  • objects keep details to themselves
  • an object represents no more than it should
  • an object has its own responsibilities
  • an object has a clear interface

Pillar 2: Inheritance
In the preceding section you have read about composition, making an object from multiple objects. In that case, the relation your object has got with the objects that it is composed from is called a "has-a" relationship. A car "has-a" wheel, for instance. Your personal computer "has-a" processor. Your closet "has-a" drawer. You get the idea. Now let's continue with a different type of object relationship: the "is-a" relationship. An "is-a" relationship means that an object is or "can be seen as" another object. Thus, a derivative object can be seen as the object it was derived from. Here are some real world examples to give you a better idea of how you should interprete that:
  • a car is a vehicle
  • a bike is a vehicle
  • a man is a human being
  • a woman is a human being
Listing 3 illustrates this theory by implementing a (rather simplified) class that represents a human being.

Listing 3

<?php
class HumanBeing
{
var $arms;
var $legs;
var $brains;

/*
* constructor
* in PHP 5 this will be __construct() instead, although
* this code will still work if there's no
* __construct() available
*/
function HumanBeing($arms, $legs, $brains)
{
$this->arms = $arms;
$this->legs = $legs;
$this->brains = $brains;
}

function walk($from, $to)
{
// code for letting human walk goes here
}

function think($subject)
{
// code for letting human think goes here
}

function waveArms()
{
// code for letting human wave its arms goes here
}
}
?>

In OOP terminology, this would be called the baseclass for a human being. Let's pretend that it implements all the required properties and behaviour a human being should have. See it as a universal human being. Of course we cannot implement a complete human being in code (if you can, please raise your hand), but again, this is just by means of an example. We do know however, that man and woman are both human beings, thus, they can both derive from a human being object - they just need respective properties and behaviours added. This also means that if we want to implement a woman or man in code, we'd not have to implement all code necessary to indicate that this is a woman or man. We simply derive it from the human being object. This can save a lot of time and code. And that is also exactly what inheritance is about. To give you a better idea, listing 4 shows an implementation of a Woman class. Note that the superclass definition should always be available to the subclass when you want to extend from it.

Listing 4

<?php
/*
* note: make sure the definition of the HumanBeing class is
* included when implementing this
* code
* now that the definition for that class is available, we can extend from it
*/
class Woman extends HumanBeing
{

/*
* at this point, our class has all properties and behaviour that have
* been defined
* in HumanBeing
* let's add a property that is specific to a woman
*/
var $pregnant;

// constructor
function Woman($arms, $legs, $brains)
{
// make sure we initialize the superclass constructor
parent::HumanBeing($arms, $legs, $brains);

// set pregnant to false by default
$this->pregnant = false;
}

// create modifier for pregnancy attribute
function setPregnant($pregnantBool)
{
if ($pregnantBool != true && $pregnantBool != false) {
echo "Error: invalid parameter for: ";
echo "setPregnant(Boolean pregnantBool)";
} else {
$this->pregnant = $pregnantBool;
}
}

// create accessor so see if woman is pregnant
function isPregnant()
{
return $this->pregnant;
}
}
?>

The following example shows how to make a new instance of Woman which would also have the properties and behaviour of HumanBeing. When you want to apply this snippet, you have to make sure that the definition of the Woman class is available to the script. Also note that in this case, the arguments passed to the constructor are most likely to be objects themselves, but for the sake of simplicity we have used strings. You can see that we invoke HumanBeing's waveArms(), which is possible because we extended Woman from HumanBeing.

<?php
$aWoman = new Woman("the womans arms", "the womans legs", "the womans brains");

$aWoman->waveArms();

$aWoman->setPregnant(true); //easier than you thought, no?

if ($aWoman->isPregnant()) {
echo "Oh dear. She's pregnant.";
}
?>

Apart from being able to find a rather interesting alternative to the flowers and bees story this way, there is a lot to say about the preceding code. Firstly, we have implemented all the relevant (note: this means relevant to how we want to apply one) properties and behaviour for a human being. Then, we built upon that class by implementing a class that would represent a woman. In OOP-speak, the relationship between the HumanBeing class and the Woman class is as follows:
  • Woman is a subclass of HumanBeing
  • HumanBeing is the superclass of Woman
We can also still say that Woman "is-a" HumanBeing. Note that it does not work both ways. A HumanBeing is absolutely not per definition a Woman, so we could say that the "is-a" concept is only recognized when we go from the bottom to the top of the hierarchy. A superclass cannot be seen as one of its subclasses, only the other way around. A subclass inherits all properties and behaviour from its superclass by extending from it. This is often called "programming by difference". All you basically do when subclassing, is adding properties and behaviour that make the subclass different from its superclass. When deriving a subclass from a superclass however, it should be very clear that the subclass is in fact a derivative from its superclass. This sounds obvious, but it is often mistaken. Do not try to replace composition by inheritance, they are two different things. When your subclass cannot be seen as being its superclass, do not apply inheritance.
Now that you have seen a few obvious real world examples turning into objects, I think you enough understanding of what objects are to move on to some more webdevelopment oriented situations. Say, you want to create a formgenerator. A form obviously consists of form elements. Now, how would you go around turning your form elements into objects? First, we want to create a baseclass that encapsulates everything that any form element has in common.

In listing 5, the class FormElement represents what all form elements have in common. I added a label property to make sure that a form element can have a visible label in a html form, otherwise we would end up with a form with form elements, not knowing what each element is by just viewing the html document.
After this, we derive a few subclasses of the FormElement baseclass, that represents actual form elements that we can use.

Listing 5

<?php
class FormElement
{
var $name;
var $label;

function FormElement($name, $label)
{
$this->name = $name;
$this->label = $label;
}

// possibly define accessors, modifiers and other methods here
}

class TextFormElement extends FormElement
{
var $value;
var $maxLength;

function TextFormElement($name, $label, $value, $maxLength)
{
/*
* construct the TextFormElement, also make sure
* that the superclass' constructor is
* initiated properly
*/
parent::FormElement($name, $label);

$this->value = $value;
$this->maxLength = $maxLength;
}
// possibly define accessors, modifiers and other methods here
}

class CheckboxFormElement extends FormElement
{
var $value;
var $checked;

function CheckboxFormElement($name, $label, $value, $checked)
{
/*
* construct the CheckboxFormElement, also make sure that the
* superclass'
* constructor is initiated properly
*/
FormElement::FormElement($name, $label);

$this->value = $value;
$this->checked = $checked;
}
// possibly define accessors, modifiers and other methods here
}
?>

It should be obvious what we did in listing 5. We extended the FormElement baseclass and created two subclasses: TextFormElement, which represents an HTML textfield, and CheckboxFormElement, which represents an HTML checkbox. The derived classes just add functionality to the functionality they already inherit from their superclass, thus specializing themselves. In the following section, we will continue on the preceding examples and make them behave generically.

Pillar 3: Polymorphism
One of inheritance's good friends is called polymorphism. First of all, I have to say that applying this technique is not likely going to have a huge impact when used in a weakly typed language such as PHP (i.e. the interpreter does not care about the types of our objects as they do in for example Java). Polymorphism basically means that a single class, method or variable name may present itself in many different forms. While this is interesting, PHP's nature makes sure that a lot of this happens automatically, since it has no concept of strong typing, thus all objects will be treated equally anyway. Conventionally however, it can make sense. Listing 6 presents an example of polymorphism.

Listing 6

<?php
class Base
{
// empty constructor
function Base() {}

function displayStr()
{
echo "hello from base";
}
}

class DerivedOne extends Base
{
// empty constructor
function DerivedOne() {}

// overriding displayStr() in Base
function displayStr()
{
echo "hello from derived class one";
}
}

class DerivedTwo extends Base
{
// empty constructor
function DerivedTwo() {}

// overriding displayStr() in Base
function displayStr()
{
echo "hello from derived class two";
}
}

$objects = array (
$baseInstance = new Base(),
$derivedOneInstance = new DerivedOne(),
$derivedTwoInstance = new DerivedTwo()
);

foreach ($objects as $object) {
$object->displayStr(). "<br />\n";
}
?>

What we have done in listing 6 is simple. We define a method displayStr() in a baseclass, and derive two classes from it, namely DerivedOne and DerivedTwo. We then define an array and put instances of the baseclass and its two subclasses into it, after which we loop over the array with the stored objects, and call their displayStr() method. This last part is what makes it interesting. We could say that DerivedOne and DerivedTwo both maintain an "is-a" relationship with Base, but as we can see, the displayStr() is what specializes them. In other words, these subclasses from the Base class behave differently while they are both "a Base". Once again, there is nothing wow-ish about this code, but it is the theory that matters here. When you think a bit further about this concept, you might realize that this technique can be used to create generic code. I did not even keep track of the number of times I was reading code online where people used switch() statements in object oriented code. The usage of switch() in an OO situation is hardly ever needed, and even if it is, you might want to reconsider it. Using switch() to determine what to do with an object can become a maintenance nightmare. If you use switch() to determine of which type an object is, after which you are going to do things with the object in every different case, requires that you update your switch() statement every time a new object is added to your application. We do not want that, do we? In case you do not really know what I am ranting about, the bad example is shown in listing 7. Say, we want to build a stack of the form elements explained in the "inheritance" section, loop through them, and based on what exact form element (type) we run into, we want to print the HTML code.

Listing 7

<?php
// note: make sure the definitions of the form element classes are available here

// instantiate some form elements
$myTextfield = new TextFormElement(
"myTextfield",
"My Textfield",
"a value",
20
);
$myCheckbox = new CheckboxFormElement(
"myCheckbox",
"My Checkbox",
"a value",
true
);
$mySecondCheckbox = new CheckboxFormElement(
"mySecondCheckbox",
"My Second Checkbox",
"a value",
true
);

// make an array of all the elements so we can loop through them
$objects = array($myTextfield, $myCheckbox, $mySecondCheckbox);

foreach ($objects as $object) {
//see of what type the current object is
switch (get_class($object)) {
case "textformelement":
// print html code for current text element here
break;
case "checkboxformelement":
// print html code for current checkbox element here
break;
default:
break;
}
}
?>

The code in listing 7 is dramatic. Wouldn't it be nicer if we were able to invoke just one method on the object currently in the loop, and if that object just knew what it is supposed to do? Yes, that would be very convenient. And this is where polymorphism comes in very nicely. In listing 8, we have modified our FormElement class and its subclasses.

Listing 8

<?php
class FormElement
{
/*
* implementation we used earlier goes here
*/

/*
* let's add a method showHTML(), which should be overridden
* by all subclasses
*/
function showHTML()
{
echo "Warning: you did not override showHTML() ";
echo "in your FormElement class";
}
}

class TextFormElement extends FormElement
{
/*
* implementation we used earlier goes here
*/

/*
* overriding FormElement's showHTML() method, specialized
* for this object type
*/
function showHTML()
{
// implement code to show this particular form element
echo "{$this->label} <input type='text'";
echo "name='{$this>name}'value='{$this>value}' ";
echo "maxlength='{$this->maxlength}'>";
}
}

class CheckboxFormElement extends FormElement
{
/*
* implementation we used earlier goes here
*/

/*
* overriding FormElement's showHTML() method, specialized for
* this object type
*/
function showHTML()
{
// implement code to show this particular form element
$checked = "";
if ($this->checked) {
$checked = " checked";
}
echo "<input type='checkbox' name='{$this->name}'";
echo "value='{$this->value}'{$this->checked}> ";
echo $this->label;
}
}
?>

The new implementation for the FormElement syntax is not very exciting, I know. Let's draw a conclusion about the bigger context, though. We now know that every class that inherits from our FormElement baseclass, automatically inherits the showHTML() method. While this is not very surprising, it becomes more interesting if we override the showHTML() method in every subclass of FormElement. Aha. Now our subclass objects know what to do when their showHTML() method is being called, without us having to worry of what type our subclass object is. Let us now fix the bad switch() code we saw earlier, in listing 9.

Listing 9

<?php
// note: make sure the definitions of the form element classes are available here

// instantiate a textfield and a few checkboxes
$myTextfield = new TextFormElement(
"myTextfield",
"My Textfield",
"a value",
20
);
$myCheckbox = new CheckboxFormElement(
"myCheckbox",
"My Checkbox",
"a value",
true
);
$mySecondCheckbox = new CheckboxFormElement(
"mySecondCheckbox",
"My Second Checkbox",
"a value",
true
);

// make an array of all the elements so we can loop through them
$objects = array($myTextfield, $myCheckbox, $mySecondCheckbox);

foreach ($objects as $object) {
// no switch needed!
$object->showHTML();
}
?>

Isn't that much easier to maintain? I thought so. This is a good way to apply polymorphism. However, there is one remark I cannot leave untouched when dealing with this particular code. In the implementation of showHTML() in our FormElement class, I put a warning message inside the method body, which would show up in case a subclass did not override that method. While this works, it is not very elegant. Unfortunately, PHP has no concept of abstract classes (a mechanism that lets you programmatically force implementation rules when subclassing). If it did, we could quite simply force every subclass to implement showHTML(). Make sure to document properly what needs to be done when you want subclasses to override certain methods of their superclass, for yourself or for your team. It can save debugging time.

Conclusion
Object Oriented Programming is a powerful and often elegant approach to analyze, design, and implement a solution to a problem, but not something you master in days, weeks, or even years. It is certainly not the golden key to solve every problem, that would be too good to be true. It is not realistic to apply OOP theory to that tiny script that makes sure the form is being emailed. It might help you when designing average to large scale applications though.
When I wrote this article, I knew that writing about "real" OOP in a procedural language by origin, could be nearly controversial. Nonetheless, I wanted to familiarize the reader about what OOP is really about, and possibly have him/her taking advantage of it. As long as it is fun and appliable at the same time, it is all good, right?

Links and Literature

Software & Support Verlag - Global Alliance Program!







-- Advertisement --
Kelkoo price comparison in Germany
- Mobiles
- Furniture
- Notebooks
- Hotels
- Flights
- Digital cameras
Software & Support Verlag GmbH