A quick overview of PHP 5.2 OOP features. It is a good start for anyone who wants to understand about OOP but hasn’t started learning it so far.
PHP has evolved alot in the past few years. Especially from the appearance of PHP 5. A particular improvement of the 5th version of this awesome widely spread language is the OOP capabilities (which are not the best but they do serve well). If PHP 4 was the first version to offer O(bject) O(rientated) P(rogramming), it is considered by many (and specially the gurus of PHP) to be poorly designed. While the good thing and the bad thing of PHP is that you can still program using procedural methodology, those who have an OOP background will certainly feel like home developing fully OOP driven applications.
This article is not intended to those who don’t know the basics of OOP. Moreover, this article is targeted to those who wish to learn (or remember, relearn, see an example) about advanced OOP techniques such as polymorphism, interfaces and abstract classes but also using object return values to implement an unlimited number of methods for the same object, on the same line. The provided example will not be very complex, since the target is demonstrate the use of each type, rather then be simple, compact and make use of the basics from each methodology.
1. What will the example show you?
The below example will teach you how to use the polymorphism design, why to use interfaces and why & where to use abstract classes alongside showing how to make use of the return object type technique. The code is documented and you can read a small description for each class. We will build the example from scratch and speak about each class in particular having at the end, the full example which you can run on your home php server.
2. The making of…
First things are first: create a new php file called test_ my_ oop_ skills_ in_ the_ cool_ programming_ language_ PHP.php (joking! name it whatever you want) in your favorite IDE and put it somewhere where you can run it from your localhost (or whatever configuration you may have).
We will start with defining our scope: We want to create animals based on the $_GET user inputed data and make them move. Some animals may RUN and others may WALK depending on their envoirment. A simple task that might be treated in a few lines of code. Before starting to yell stuff like “ZOMG THIS IS SO COMPLICATED!! JUST DO AN IF..ELSE AND THAT IS THAT” remember that this is an example. A simple application requires simple coding (although OOP should be used!) but if you tend to get more complicated with your application, you will find that, simple code may not work for you and you will need (hopefully) some of the following techniques…
3. Interfaces.
So back to the code… Lets think in terms of reality vs code. From the given information we know that we will have animals in our project (neat huh?!) . These animals will have one common behaviour – the move action. Lets not think ahead. We might have animals on Earth or on Mars. We won’t discuss about animals on Mars (dooh!) but we are interested in the animals on Earth. However, since we can have alot of places we could find animals, we will define an “interface” for the Animals like this:
interface Animal {
public function move ();
}
This means that our default behavior for all animals in the universe is to move . We cannot have any animals that don’t move (ok ok, perhaps on mars we can have but bear with me). Getting technical, this means that we cannot have any class that will use this interface without implementing the move action exactly how it is (no parameters, same name, same access type). From here on, we defined our basic rule-set for all animals in the universe.
4. Abstract classes
So we just talked about all animals in the universe..This is a complex subject so we should probably treat a more special case: The Animals from earth or TerraAnimals. We know that TerraAnimals are animals themselves thus they can at least move. Thus we would have the following code:
abstract class TerraAnimals implements Animal {
public function move () {
//logic here
}
}
Now, you may wonder, why the hell did he use an abstract class here?! The answer is simple: because TerraAnimals cannot be an entity. Period. You cannot say TerraAnimals have 4 legs,bark and drink milk. TerraAnimals are more of a type of animals from the universe. Thus, they should never be treated as a self existent entity. In technical terms, you can never create an object of type TerraAnimals.
Let’s dive in the code more: we will want to store in a variable the type of animal we will use, the move action it will do (run or walk) and the full status of what we want to show the user which is something like $AnimalName .’ ‘.$actionType.
Moreover, since at the starting point, we won’t know what type of animal it is (since we are getting it from $_GET, we need to create an animal based on the user input. This means we need to create a method named (ex) getAnimal() which takes the $_GET input as a parameter and returns a new instance of the animal. The code would look as following:
abstract class TerraAnimals implements Animal {
public $animal = '';
public $currentStatus = '';
public $TerraAnimalstatus = '';
public function getAnimal ( $animal ) {
if ( class_exists ( $animal ) ) {
return new $animal ();
} else {
return false;
}
}
public function move () {
$this -> TerraAnimalstatus = $this -> animal . ' ' .
$this -> currentStatus;
return $this;
}
public function whatIsTheAnimalDoing () {
return $this -> TerraAnimalstatus;
}
}
The move action just creates our full animal status (as in $AnimalName . ‘ ‘ . $AnimalMoveType) and the action whatIsTheAnimalDoing will return us this full status. Preety simple huh?
Note that we are returning the object itself in the move method. You will see later on why.
We now have the Animal design and all the Animals from earth(TerraAnimals) defined. Lets go deeper now: What is the main difference between animals on earth? For the sake of simplicity, lets say that the only this is the environment they are living in. Some are wild animals and some are domestic. Also, for this example, we will assume that all domestic animals can only walk since they don’t have where to run while all the wild animals will run since they have the space to do it and don’t need to walk (why less then more right?)
We would then have the following (in terms of code):
abstract class HouseTerraAnimals extends TerraAnimals {
public function move () {
$this -> currentStatus = 'is walking';
parent::move ();
}
}
and
abstract class WildTerraAnimals extends TerraAnimals {
public function move () {
$this -> currentStatus = 'is running';
parent::move ();
}
}
Both are abstract classes since again, you cannot consider wild animals or domestic animals as an entity by itself. You need the reference (such as Dog, cat, wild boar etc). Thus we make sure we will never make the mistake of creating an animal that is not a particular race.
Also, we setup here what each type of animal can do. The wild ones will move running while the domestic ones will move walking.
Let’s move on…
5. Designing the polymorphism behavior and running the code
We come now to the actual concrete entities that will make use of our nice pattern. We will have 4 entities : A dog, a cat, a wild boar and a giraffe. 2 are domestic while 2 are wild. We will define them as following:
class Dog extends HouseTerraAnimals {
public function move () {
$this -> animal = 'Dog';
parent::move ();
return $this;
}
}
class Cat extends HouseTerraAnimals {
public function move () {
$this -> animal = 'Cat';
parent::move ();
return $this;
}
}
class WildBoar extends WildTerraAnimals {
public function move () {
$this -> animal = 'Wild boar';
parent::move ();
return $this;
}
}
class Giraffe extends WildTerraAnimals {
public function move () {
$this -> animal = 'Giraffe';
parent::move ();
return $this;
}
}
Basically each animal is a different race and we tell our application which is it through the $this -> animal variable.
We now have all our classes defined and we can see how all fit togheter now.
When we will run the application, we will not care about what animal the user inputed. We care only that it is an animal and we make it act with its default behavior. The move action. This is the principle of the polymorphism pattern. We design our application in such a way that, when we use it, the code does not worry about certain types or behaviors. It acts as a general action and based on the parameters, the application will know how to deal with each case.
To see the action concrete, we will have the following code:
$animal = TerraAnimals::getAnimal ( $_GET[ 'animal' ] );
if (!$animal) {
echo 'No animal was found';
} else {
echo $animal -> move () -> whatIsTheAnimalDoing ();
}
Now navigate to the file you created on your server and run this: filename.php?animal=dog and filename.php?animal=WildBoar
You will see that the dog is walking while the WildBoar is running.
As you can tell, the actual code is pretty simple . We create a new animal object based on the $_GET and then make it move.
Moreover, we make it move AND display its current moving status on the same line (this is the reason we are using return $this) since you cannot run a method of a non-object.
6. Conclusion
Lets see a fast resume of what we created.
If we have a dog :
We will create a new dog object. We tell the dog to move. This will automatically setup the dog race and, because it is a HouseAnimal, it will auto setup itself to WALK and not run. This whole process will return the same dog object (entity) but with the current attributes (race and move type). Once we have these, we make the dog tell us how is he moving and he will show us he is walking.
Hope the example helped and that it was detailed. If you something is unclear feel free to comment below.
Please comment below for any suggestions/mistakes i might have made.
UPDATE: The method getAnimal should be named factory since it is based on the Parameterized Factory Method (creating an object on demand).
