PDA

View Full Version : OOP in PHP Part Two: Con/Destructors and Encapsulation



Moonbat
07-27-2008, 03:46 PM
If you haven't read the first part of my tutorial, do so now.

Now we will get into two new ideas that come with classes. They are called constructors and destructors. Let's refer back to our Cheese class.


class Cheese {
var $type;
var $flavor;
var $color;

function giveDetails ($thetype, $theflavor, $thecolor) {
$this->type = $thetype;
$this->flavor = $theflavor;
$this->color = $thecolor;
}

function showType() {
return $this->type;
}
function showColor() {
return $this->color;
}
function showFlavor() {
return $this->flavor;
}
}

In order to assign Cheese the properties of type, flavor, and color, we would have to call the giveDetails() function after declaring the new Cheese object. But what if we could do this all in one step? Here is the value of a constructor.


function __construct($atype, $aflavor, $acolor) {
$this->type = $atype;
$this->flavor = $aflavor;
$this->color = $acolor;
}

You initialize a constructor by making a function named '__construct'. It doesn't have to take arguments; it could just echo a statement like "Object created successfully!". But in this case, we want it to take arguments. Let's see it in action.


<?php

class Cheese {
var $type;
var $flavor;
var $color;

function __construct($atype, $aflavor, $acolor) {
$this->type = $atype;
$this->flavor = $aflavor;
$this->color = $acolor;
}

function giveDetails ($thetype, $theflavor, $thecolor) {
$this->type = $thetype;
$this->flavor = $theflavor;
$this->color = $thecolor;
}

function showType() {
return $this->type;
}
function showColor() {
return $this->color;
}
function showFlavor() {
return $this->flavor;
}
}

class MoreCheese extends Cheese {
var $cost;

function giveCost($f) {
$this->cost = $f;
}

function showCost() {
return $this->cost;
}
}

$zargento = new Cheese("Cheddar", "Good", "Yellow");
echo $zargento->showType();
echo "<br>";
echo $zargento->showFlavor();
echo "<br>";
echo $zargento->showColor();

?>

Output:

Cheddar
Good
Yellow

We entered the flavor, color, and type in one step, and it worked successfully. We can still use the giveDetails function later in the script if we want to change the properties of the Cheese object. Before we move on, I want to cover an aspect of older PHP versions. When wanting to use a constructor with PHP versions lower than PHP5, you must name the constructor the same name as you named the class. So for our Cheese class, instead of using __construct, you would use:


function Cheese($atype, $aflavor, $acolor) {
$this->type = $atype;
$this->flavor = $aflavor;
$this->color = $acolor;
}

Now then, let's move on to destructors. These are called when you are finished with your object. It is useful in situations where you want to take certain actions just before the object is finished with. Suppose you have a script that is supposed to run for 20 seconds then display a message just before terminating. Someone takes that script and changes the run time to 50 seconds. Your script will display your 'termination' message after 20 seconds but the script will keep going. But by using a destructor, you are sure that this message will be displayed only when the object in question has truly served it's purpose. Here is what a destructor looks like.


function __destruct() {
echo "Cheese has been eaten";
}

It looks just like the constructor, except the keyword destruct is used. A destructor usually won't take any arguments. Let's see it in action.


<?php

class Cheese {
var $type;
var $flavor;
var $color;

function __construct ($atype, $aflavor, $acolor) {
$this->type = $atype;
$this->flavor = $aflavor;
$this->color = $acolor;
}

function giveDetails ($thetype, $theflavor, $thecolor) {
$this->type = $thetype;
$this->flavor = $theflavor;
$this->color = $thecolor;
}

function showType() {
return $this->type;
}
function showColor() {
return $this->color;
}
function showFlavor() {
return $this->flavor;
}

function __destruct() {
echo "Cheese has been eaten";
}
}

class MoreCheese extends Cheese {
var $cost;

function giveCost($f) {
$this->cost = $f;
}

function showCost() {
return $this->cost;
}
}

$zargento = new Cheese("Cheddar", "Good", "Yellow");
echo $zargento->showType();
echo "<br>";
echo $zargento->showFlavor();
echo "<br>";
echo $zargento->showColor();
echo "<br>";

?>

Output:

Cheddar
Good
Yellow
Cheese has been eaten

It worked exactly how we wanted it to work.

So far, we have covered classes, constructors and destructors, and inheritance. Now we can move on to a concept called encapsulation.

Encapsulation is the idea of hiding information. For example, if you have class Car, and it has a function called giveBrand(). Now suppose you make a subclass of Car called Toyota. A Toyota object, when giveBrand() is used, will return 'Toyota'. But what if someone makes a Car object? Since a car is a generic term, giveBrand() won't really serve much purpose. What do you do?

Encapsulation helps solve this problem. There are three keywords involved with encapsulation. These are:

Public - The method is available to the base class and all subclasses
Protected - The method is available to only subclasses
Private - The method is available only to the base class

Let's see an example using our Cheese and MoreCheese classes:

<?php

class Cheese {
var $type;
var $flavor;
var $color;

function __construct($atype, $aflavor, $acolor) {
$this->type = $atype;
$this->flavor = $aflavor;
$this->color = $acolor;
}

protected function giveDetails ($thetype, $theflavor, $thecolor) {
$this->type = $thetype;
$this->flavor = $theflavor;
$this->color = $thecolor;
}

function showType() {
return $this->type;
}
function showColor() {
return $this->color;
}
function showFlavor() {
return $this->flavor;
}

function __destruct() {
echo "Cheese has been eaten";
}
}

class MoreCheese extends Cheese {
var $cost;

function giveCost($f) {
$this->cost = $f;
}

function showCost() {
return $this->cost;
}
}

$zargento = new Cheese("Cheddar", "Good", "Yellow");
$zargento->giveDetails("Gorgonzola", "Awful", "Green and white");
echo $zargento->showType();
echo "<br>";
echo $zargento->showFlavor();
echo "<br>";
echo $zargento->showColor();
echo "<br>";

?>

Output:

Fatal error: Call to protected method Cheese::giveDetails() from context '' in \yada\yada\yada on line 48

See what happens? Since we protected the giveDetails() function, that means base class Cheese can't access it. We have to do the following:

*. Make another function in MoreCheese to access the protected function
2. Change $zargento from Cheese to MoreCheese.

Step #* is done like so:


class MoreCheese extends Cheese {
var $cost;

function newGiveDetails ($thetype, $theflavor, $thecolor) {
parent::giveDetails($thetype, $theflavor, $thecolor);
}

function giveCost($f) {
$this->cost = $f;
}

function showCost() {
return $this->cost;
}
}

As you see, we used 'parent::' to get the function details of the giveDetails() function and put them in the newGiveDetails() function.

Step #2 is just a simple change. Let's see the final result:


<?php

class Cheese {
var $type;
var $flavor;
var $color;

function __construct($atype, $aflavor, $acolor) {
$this->type = $atype;
$this->flavor = $aflavor;
$this->color = $acolor;
}

protected function giveDetails ($thetype, $theflavor, $thecolor) {
$this->type = $thetype;
$this->flavor = $theflavor;
$this->color = $thecolor;
}

function showType() {
return $this->type;
}
function showColor() {
return $this->color;
}
function showFlavor() {
return $this->flavor;
}

function __destruct() {
echo "Cheese has been eaten";
}
}

class MoreCheese extends Cheese {
var $cost;

function newGiveDetails ($thetype, $theflavor, $thecolor) {
parent::giveDetails($thetype, $theflavor, $thecolor);
}

function giveCost($f) {
$this->cost = $f;
}

function showCost() {
return $this->cost;
}
}

$zargento = new MoreCheese("Cheddar", "Good", "Yellow");
$zargento->newGiveDetails("Gorgonzola", "Awful", "Green and white");
echo $zargento->showType();
echo "<br>";
echo $zargento->showFlavor();
echo "<br>";
echo $zargento->showColor();
echo "<br>";

?>

Output:

Gorgonzola
Awful
Green and white
Cheese has been eaten

Due to length constraints, we will cover the opposite of protected, private, in part 2.5

Moonbat
07-29-2008, 10:08 PM
Your explanation of encapsulation is better; it explains why encapsulation would help, in a general sense. But you forgot one other reason:

#* - It makes people think your an awesome coder implementing the newest coding paradigms. :D