Tuesday, September 18, 2007

Object Oriented Programming in PHP: The Way to Large PHP Projects

This arcticle introduces Object Oriented Programming (OOP) in PHP. I will show you how to code less and better by using some OOP concepts and PHP tricks. Good luck!
Concepts of object oriented programming: There're differences between authors, I can mention that a OOP language must have:
  • Abstract data types and information hidding
  • Inheritance
  • Polymorphism
The encapsulation is done in php using classes:


class Something {
// In OOP classes are usually named starting with a cap letter.
var $x;

function
setX($v) {
// Methods start in lowercase then use lowercase to seprate
// words in the method name example getValueOfArea()
$this->x=$v;
}

function
getX() {
return
$this->x;
}
}

?>
Of course you can use your own nomenclature, but having a standarized one is useful.
Data members are defined in php using a "var" declaration inside the class, data members have no type until they are assigned a value. A data member might be an integer, an array, an associative array or even an object. Methods are defined as functions inside the class, to access data members inside the methods you have to use $this->name, otherwise the variable is local to the method.
You create an object using the new operator:
$obj=new Something;
Then you can use member functions by:
$obj->setX(5);
$see=$obj->getX();
The setX member function assigns 5 to the x datamember in the object obj (not in the class), then getX returns its value: 5 in this case.
You can access the datamembers from the object reference using for example: $obj->x=6; however, this is not a very good OOP practice. I enforce you to set datamembers by defining methods to set them and access the datamembers by using retrieving methods. You'll be a good OOP programmer if you consider data members inaccesible and only use methods from the object handler. Unfortunately PHP doesn't have a way to declare a data member private so bad code is allowed.
Inheritance is easy in php using the extend keyword.


class Another extends Something {
var
$y;
function
setY($v) {
// Methods start in lowercase then use lowercase to seperate
// words in the method name example getValueOfArea()
$this->y=$v;
}

function
getY() {
return
$this->y;
}
}

?>
Objects of the class "Another" now has all the data members and methods of the parent class (Something) plus its own data members and methods. You can use:
$obj2=new Something;
$obj2->setX(6);
$obj2->setY(7);
Multiple inheritance is not supported so you can't make a class extend two or more different classes.
You can override a method in the derived class by redefining it, if we redefine getX in "Another" we can't no longer access method getX in "Something". If you declare a data member in a derived class with the same name as a data member in a Base class the derived data member "hides" the base class data member when you access it.
You might define constructors in your classes, constructors are methods with the same name as the class and are called when you create an object of the class for example:


class Something {
var
$x;

function
Something($y) {
$this->x=$y;
}

function
setX($v) {
$this->x=$v;
}

function
getX() {
return
$this->x;
}
}

?>
So you can create an object by:
$obj=new Something(6);
And the constructor automatically asigns 6 to the data member x. Constructors and methods are normal php functions so you can use default arguments.
function Something($x="3",$y="5")
Then:
$obj=new Something();    // x=3 and y=5
$obj=new Something(8); // x=8 and y=5

When an object of a derived class is created only its constructor is called, the constructor of the Parent class is not called, this is a gotcha of PHP because constructor chaining is a classic feature of OOP, if you want to call the base class constructor you have to do it explicitely from the derived class constructor.It works because all methods of the parent class are available at the derived class due to inheritance.


function Another() {
$this->y=5;
$this->Something(); //explicit call to base class constructor.
}

?>
A nice mechanism in OOP is to use Abstract Classes, abstract classes are classes that are not instanciable and has the only purpose to define an interface for its derived classes. Designers often use Abstract classes to force programmers to derive classes from certain base classes so they can be certain that the new classes have some desired functionality. There's no standard way to do that in PHP but:
If you do need this feature define the base class and put a "die" call in its constructor so you can be sure that the base class is not instanciable, now define the methods (interface) putting "die" statements in each one, so if in a derived class a programmer doesn't override the method then an error raises. Furthermore you might need to be sure since php has no types that some object is from a class derived from you base class, then add a method in the base class to identify the class (return "some id"), and verify this when you receive an object as an argument. Of course this doesn't work if the evil programmer oveerides the method in the derived class but genrally the problem is dealing with lazy programmers no evil ones! Of course is better to keep the base class unreachable from the programmers, just print the interface and make them work!
There're no destructors in PHP.

$obj=new Something(8,9); // x=8 and y=9
Default arguments are used in the C++ way so you can't pass a value to Y and let X take the default value, arguments are asigned form left to right and when no more arguments are found if the function expected more they take the default values.

Overloading (which is different from overriding) is not supported in PHP. In OOP you "overload" a method when you define two/more methods with the same name but different number or type of parameters (depending upon the language). Php is a loosely typed language so overloading by types won't work, however overloading by number of parameters doesn't work either.
It's very nice sometimes in OOP to overload constructors so you can build the object in different ways (passing different number of arguments). A trick to do something like that in PHP is:


class Myclass {
function
Myclass() {
$name="Myclass".func_num_args();
$this->$name();
//Note that $this->$name() is usually wrong but here
//$name is a string with the name of the method to call.
}

function
Myclass1($x) {
code;
}
function
Myclass2($x,$y) {
code;
}
}

?>
With this extra working in the class the use of the class is transparent to the user:
$obj1=new Myclass('1');      //Will call Myclass1
$obj2=new Myclass('1','2'); //Will call Myclass2
Sometimes this is very nice.

Polymorphism
Polymorphism is defined as the ability of an object to determine which method to invoke for an object passed as argument in runtime time. For example if you have a class figure which defines a method draw and derived classes circle and rectangle where you override the method draw you might have a function which expects an argument x and then call $x->draw(). If you have polymorphism the method draw called depends of the type of object you pass to the function. Polymorphism is very easy and natural in interpreted languages as PHP (try to imagine a C++ compiler generating code for this case, which method do you call? You don't know yet which type of object you have!, ok this is not the point). So PHP happily supports polymorphism.


function niceDrawing($x) {
//Supose this is a method of the class Board.
$x->draw();
}

$obj=new Circle(3,187);
$obj2=new Rectangle(4,5);

$board->niceDrawing($obj); //will call the draw method of Circle.
$board->niceDrawing($obj2); //will call the draw method of Rectangle.

?>
OOP Programming in PHP
Some "purists" will say PHP is not truly an object oriented language, which is true. PHP is a hybrid language where you can use OOP and traditional procedural programming. For large projects, however, you might want/need(?) to use "pure" OOP in PHP declaring classes, and using only objects and classes for your project. As larger and larger projects emerge the use of OOP may help, OOP code is easy to mantain, easy to understand and easy to reuse. Those are the foundations of software engineering. Applying those concepts to web based projects is the key to success in future web sites.
Advanced OOP Techniques in PHP
After reviewing the basic concepts of OOP I can show you some more advanced techniques:
Serializing
PHP doesn't support persistent objects, in OOP persistent objects are objects that keep its state and funcionality across multiple invocations of the application, this means having the ability of saving the object to a file or database and then loading the object back. The mechanism is known as serialization. PHP has a serialize method which can be called for objects, the serialize method returns a string representation of the object. However serialize saves the datamembers of the object but not the methods.
In PHP4 if you serialize the object to string $s, then destroy the object, and then unserialize the object to $obj you might still access the object methods! I don't recommend this because (a) The documentation doesn't guarrantee this beahaviour so in future versions it might not work. (b) This might lead to 'illusions' if you save the serialized version to disk and exit the script. In future runs of the script you can't unserialize the string to an object an expect the methods to be there because the string representation doesn't have the methods! Summarizing serializing in PHP is VERY useful to save the data members of an object just that. (You can serialize asociative arrays and arrays to save them to disk too).
Example:


$obj
=new Classfoo();
$str=serialize($obj);
// Save $str to disk

//...some months later

//Load str from disk
$obj2=unserialize($str)

?>
You have the datamembers recovered but not the methods (according to the documentation). This leads to $obj2->x as the only way to access the data members (you have no methods!) so don't try this at home.
There're some ways to fix the problem, I leave it up to you because they are too dirty for this neat article.
Full serialization is a feature I'd gladly welcome in PHP.


No comments: