Object-oriented programming in PHP
In this part of the PHP tutorial, we will talk about object oriented programming in PHP.
There are three widely used programming paradigms there: procedural programming, functional programming and object-oriented programming. PHP 5 supports both procedural and object-oriented programming. Earlier versions of PHP had limited or no support for OOP.
Object-oriented programming (OOP) is a programming paradigm that uses objects and their interactions to design applications and computer programs. (Wikipedia)
There are some basic programming concepts in OOP:
- Abstraction
- Polymorphism
- Encapsulation
- Inheritance
The abstraction is simplifying complex reality by modeling classes appropriate to the problem. The polymorphism is the process of using an operator or function in different ways for different data input. The encapsulation hides the implementation details of a class from other objects. Theinheritance is a way to form new classes using classes that have already been defined.
Objects
Objects are basic building blocks of a PHP OOP program. An object is a combination of data and methods. In a OOP program, we create objects. These objects communicate together through methods. Each object can receive messages, send messages and process data.
There are two steps in creating an object. First, we create a class. A class is a template for an object. It is a blueprint, which describes the state and behavior that the objects of the class all share. A class can be used to create many objects. Objects created at runtime from a class are called instances of that particular class.
<?php class Simple {} $object = new Simple(); print_r($object); echo gettype($object), "\n"; ?>
In our first example, we create a simple object.
class Simple {}
This is a simple class definition. The body of the template is empty. It does not have any data or methods.
$object = new Simple();
We create a new instance of the
Simple
class. For this we have the new
keyword. The $object
variable is the handle to the created object.print_r($object); echo gettype($object), "\n";
We use the
print_r()
function to get information about the object and the gettype()
function, to get the type of the variable.$ php class.php Simple Object ( ) object
We don't get much info, since the class definition was empty. The type of the variable is
object
.Object attributes
Object attributes is the data bundled in an instance of a class. The object attributes are calledinstance variables or member fields. An instance variable is a variable defined in a class, for which each object in the class has a separate copy.
<?php class Person { public $name = ""; } $p1 = new Person(); $p1->name = "Jane"; $p2 = new Person(); $p2->name = "Beky"; echo $p1->name . "\n"; echo $p2->name . "\n"; ?>
In the above PHP script, we have a
Person
class with one member field.$p1 = new Person(); $p1->name = "Jane";
We create an instance of the Person class. And set the $name variable to "Jane". We use the ->operator to access the attributes of objects.
$p2 = new Person(); $p2->name = "Beky";
We create another instance of the Person class. Here we set the variable to "Beky".
echo $p1->name . "\n"; echo $p2->name . "\n";
We print the contents of the variables to the console.
$ php memberfields.php Jane Beky
We see the output of the script. Each instance of the
Person
class has a separate copy of the $name
member field.Methods
Methods are functions defined inside the body of a class. They are used to perform operations with the attributes of our objects. Methods are essential in encapsulation concept of the OOP paradigm. For example, we might have a
connect()
method in our AccessDatabase class. We need not to be informed, how exactly the method connect()
connects to the database. We only know that it is used to connect to a database. This is essential in dividing responsibilities in programming. Especially in large applications.<?php class Circle { public $radius; function setRadius($radius) { $this->radius = $radius; } function area() { return $this->radius * $this->radius * M_PI; } } $c = new Circle(); $c->setRadius(5); echo $c->area(), "\n"; ?>
In the code example, we have a
Circle
class. We define two methods.public $radius;
We have one member field. It is the radius of the circle. The
public
keyword is an access specifier. It tells that the variable is fully accessible from the outside world.function setRadius($radius) { $this->radius = $radius; }
This is the
setRadius()
method. It is a normal PHP function. We call functions defined inside classes methods. The $this
variable is a special variable, which we use to access the member fields from methods.function area() { return $this->radius * $this->radius * M_PI; }
The
area()
method returns the area of a circle. The M_PI
is a built-in constant.$ php circle.php 78.5398163397
Running the example.
Access modifiers
Access modifiers set the visibility of methods and member fields. PHP 5 has three access modifiers:
public
, protected
, and private
. The public
members can be accessed from anywhere. The protected
members can be accessed only within the class itself and by inherited and parent classes. The private
members may only be accessed by the class that defines the member.
Access modifiers protect data against accidental modifications. They make the programs more robust.
<?php class Person { public $name = ""; private $age; } $p = new Person(); $p->name = "Jane"; #$p->age = 17; echo $p->name . "\n"; ?>
In the above PHP script, we have two member fields. One is declared public, the other private.
$p->name = "Jane"; #$p->age = 17;
We access the
$name
member from the outside world. By the outside world, we mean 'not in the class'. It is OK, since the $name
variable is declared public
. Accessing the $age
member is not possible. The private
modifier prohibits this. If we uncomment the code line, we will get the 'Fatal error: Cannot access private property Person::$age' error.<?php class Base { public $name = "Base"; protected $id = 6124; private $is_defined = "yes"; } class Derived extends Base { public function info() { echo "This is Derived class\n"; echo "Members inherited: \n"; echo $this->name . "\n"; echo $this->id . "\n"; echo $this->is_defined . "\n"; } } $derived = new Derived(); $derived->info(); ?>
In this PHP script, we have a
Derived
class, which extends the Base
class. The Base
class has three member fields. All with different access modifiers. The $is_defined
member is not inherited. Theprivate
modifier prevents this.public function info() {
The
info()
method has a public
access modifier. This means, it can be called outside the class environment.$ php access.php This is Derived class Members inherited: Base 6124 $
Running the PHP script, we receive this output. The public and protected members are inherited, the private member is not.
<?php class SysInfo { private function get_date() { return date("Y/m/d"); } private function get_version() { return phpversion(); } public function getInfo() { $date = $this->get_date(); $version = $this->get_version(); echo "The date is: $date\n"; echo "The PHP version is: $version\n"; } } $sys = new SysInfo(); $sys->getInfo(); #$sys->get_date(); ?>
In this PHP script, we have a
SysInfo
class. It outputs some system information to the console. We have two private functions and one public. The private methods are here only for internal working of the SysInfo
class. They are not meant to be called outside the class.$sys = new SysInfo(); $sys->getInfo(); #$sys->get_date();
We create an instance of the
SysInfo
class and call the publicly available getInfo()
method. ThegetInfo()
method uses internally the private methods to do its job. Uncommenting the last code line yields to error.Method overloading
Method overloading allows the creation of several methods with the same name which differ from each other in the type of the input.
What is method overloading good for? The Qt4 library gives a nice example for the usage. The
QPainter
class has three methods to draw a rectangle. Their name is drawRect()
and their parameters differ. One takes a reference to a floating point rectangle object, another takes a reference to an integer rectangle object and the last one takes four parameters, x, y, width, height. If the C++ language, which is the language in which Qt is developed, didn't have method overloading, the creators of the library would have to name the methods like drawRectRectF()
,drawRectRect()
, drawRectXYWH()
. The solution with method overloading is more elegant.<?php class Sum { public function getSum() { return 0; } public function getSum($x) { return $x; } public function getSum($x, $y) { return $x + $y; } } $s = new Sum(); echo $s->getSum() . "\n" ; echo $s->getSum(5) . "\n" ; echo $s->getSum(3, 4) . "\n" ; ?>
This is a method overloading, we know from languages like C#, Java or C++. But this does not work in PHP. Running this example, we get the following error: 'Fatal error: Cannot redeclare Sum::getSum()'. PHP functions can take arbitrary number of variables by default.
To simulate method overloading in PHP, we use the
func_get_args()
function.<?php class Sum { public function getSum() { $args = func_get_args(); if (empty($args)) return 0; foreach ($args as $arg) { $sum += $arg; } return $sum; } } $s = new Sum(); echo $s->getSum() . "\n" ; echo $s->getSum(5) . "\n" ; echo $s->getSum(3, 4) . "\n" ; echo $s->getSum(3, 4, 7) . "\n" ; ?>
This time, the script will run.
$args = func_get_args();
The
func_get_args()
function returns an array comprising a function's argument list.foreach ($args as $arg) { $sum += $arg; }
We go throught all members of the array, and calculate the sum.
echo $s->getSum() . "\n" ; echo $s->getSum(5) . "\n" ; echo $s->getSum(3, 4) . "\n" ; echo $s->getSum(3, 4, 7) . "\n" ;
We call the same method name with different number of inputs.
The constructor
A constructor is a special kind of a method. It is automatically called, when the object is created. The purpose of the constructor is to initiate the state of the object. The name of the constructor in PHP 5 is always
__construct()
. (Two underscores)<?php class Song { function __construct() { echo "Song object is created \n"; } } $song = new Song(); ?>
We have a
Song
class. This class has a constructor that prints message to the console.$song = new Song();
This is the time, when the object is created and the constructor is called. We get a message in the console.
$ php constructor.php Song object is created
This is the output of the script.
Constructors often take arguments.
<?php class Song { function __construct($song) { echo "Song $song is created \n"; } } $song = new Song("Bad romance"); ?>
We modify the previous example a bit. We pass a value to the constructor.
function __construct($song) { echo "Song $song is created \n"; }
The passed argument is stored in the local
$song
variable.$ php constructor2.php Song Bad romance is created
Now we have a message with a song title printed to the console.
In the next example, we initiate data members of the class. Initiation of variables is a typical job for constructors.
<?php class Friend { private $born; private $name; function __construct($name, $born) { $this->name = $name; $this->born = $born; } function getInfo() { echo "My Friend $this->name was born in $this->born\n"; } } $friend = new Friend("Lenka", 1990); $friend->getInfo(); ?>
We have a
Friend
class with data members and methods.private $born; private $name;
We have two variables in the class definition. The
private
keyword is an access modifier. It is a form of encapsulation. The private
keyword is the most restrictive modifier. It allows only the object in question to access the variable. No descendants, no other objects. More about this topic later.function __construct($name, $born) { $this->name = $name; $this->born = $born; }
In the constructor, we initiate the two data members. The
$this
variable is a handler used to reference the object variables.$friend = new Friend("Lenka", 1990); $friend->getInfo();
We create a Friend object with two arguments. Then we call the
getInfo()
method of the object. To call object methods, we use the -> operator.$ php friend.php My Friend Lenka was born in 1990
Class constants
PHP enables to create class constants. These constants do not belong to a concrete object. They belong to the class. By convention, constants are written in uppercase letters.
<?php class Math { const PI = 3.14159265359; public function getPI() { echo self::PI; } } $math = new Math(); echo Math::PI, "\n"; echo $math->getPI(), "\n"; ?>
We have a Math class with a PI constant.
const PI = 3.14159265359;
The
const
keyword is used to define a constant.public function getPI() { echo self::PI; }
Class constants are accessed from within methods using the
self
keyword followed by two colons.echo Math::PI, "\n"; echo $math->getPI(), "\n";
We print the
PI
constant to the console. In the first case, we get the constant value by referring to the class name, followed by two colons and a constant name. Note that no object was needed to get the class constant. In the second case, we use the object method.The instanceof keyword
The
instanceof
keyword is used to determine whether a PHP variable is an instantiated object of a certain class.<?php class Cat {} class Dog {} class Bird {} $objects = array(new Cat(), new Dog(), new Cat(), new Bird(), new Bird(), new Dog(), new Dog(), new Cat(), new Bird() ); shuffle($objects); foreach ($objects as $object) { if ($object instanceof Cat) { echo "It is a Cat\n"; } elseif ($object instanceof Dog) { echo "It is a Dog\n"; } else if ($object instanceof Bird) { echo "It is a Bird\n"; } } ?>
In the above script, we have three classes:
Cat
, Dog
, and Bird
. We traverse the array and print the class for each array value.$objects = array(new Cat(), new Dog(), new Cat(), new Bird(), new Bird(), new Dog(), new Dog(), new Cat(), new Bird() );
We create an array of these objects.
shuffle($objects);
We shuffle the array. At this point, we don't know the class types for the values of the array.
if ($object instanceof Cat) { echo "It is a Cat\n"; }
Here we use the
instanceof
keyword to find out the type of the class.$ php instanceof.php It is a Bird It is a Cat It is a Cat It is a Dog It is a Dog It is a Cat It is a Dog It is a Bird It is a Bird
You might get this output.
The __toString() method
When we use the
print
or the echo
keyword with the object instance, the __toString()
special method is called. We will demonstrate this in the following example.<?php class Cat { public $name; public $age; function __construct($name, $age) { $this->age = $age; $this->name = $name; } function __toString() { return "Cat: $this->name, Age: $this->age \n"; } } $missy = new Cat("Missy", 6); $lucky = new Cat("Lucky", 4); print $missy; echo $lucky; ?>
We have a Cat class with a
__toString()
special method defined.function __toString() { return "Cat: $this->name, Age: $this->age \n"; }
The method prints the basic info about the object.
$missy = new Cat("Missy", 6); $lucky = new Cat("Lucky", 4);
We create two objects of the Cat class.
print $missy; echo $lucky;
And we use the
print
or the echo
keywords on them.$ php tostring.php Cat: Missy, Age: 6 Cat: Lucky, Age: 4
This is what we get, when we run the script.
Inheritance
The inheritance is a way to form new classes using classes that have already been defined. The newly formed classes are called derived classes, the classes that we derive from are called baseclasses. Important benefits of inheritance are code reuse and reduction of complexity of a program. The derived classes (descendants) override or extend the functionality of base classes (ancestors).
<?php class Base { function __construct() { echo "Construction of Base class \n"; } } class Derived extends Base { function __construct() { parent::__construct(); echo "Construction of Derived class \n"; } } $obj1 = new Base(); $obj2 = new Derived(); ?>
In this PHP script, we have two classes. A
Base
class and a Derived
class. The Derived
class inherits from the Base
class.class Derived extends Base {
In PHP, we use the
extends
keyword to create inheritance relations.function __construct() { parent::__construct(); echo "Construction of Derived class \n"; }
In the constructor of the Derived class, we call the parent constructor. We use the
parent
keyword, followed by two colons and the __construct()
method. The constructors of the parent classes must be called explicitly.$obj1 = new Base(); $obj2 = new Derived();
We instantiate both the
Base
and the Derived
classes.$ php derived.php Construction of Base class Construction of Base class Construction of Derived class
This is the output of the PHP script.
A more complex example follows.
<?php abstract class Being { protected $isAlive = true; public function isAlive() { if ($this->isAlive) { echo "Being is alive\n"; } else { echo "Being is not alive\n"; } } public function kill() { $this->isAlive = false; } } abstract class Animal extends Being { protected $age; public function __construct($age) { $this->age = $age; } protected function setAge($age) { $this->age = $age; } public function getAge() { return $this->age; } } class Cat extends Animal { private $name; public function __construct($name, $age) { $this->name = $name; parent::__construct($age); } public function getName() { return $this->name; } } $cat = new Cat("Cici", 4); $cat->isAlive(); echo $cat->getName() . " is " . $cat->getAge() . " years old\n"; $cat->kill(); $cat->isAlive(); ?>
We have used several new concepts here. In the code example, we have three classes:
Being
,Animal
, and Cat
. The Animal
class inherits from the Being
class. The Cat
class inherits from theAnimal
class. Classes inherit methods and data members that are not declared as private.abstract class Being {
The
Being
class is declared to be abstract
. The abstract
keyword prohibits instantiation of classes. It does not make much sense to create an instance of the class Being
.protected $isAlive = true;
The
$isAlive
data member is declared to be protected
. Such members can be accessed only by the classes that define them and by their descendants.abstract class Animal extends Being {
The
Animal
class is also declared to be abstract. It inherits from class Being
. For this, we use theextends
keyword. The Animal
is a descendant. It inherits methods and variables of the base Being
class.class Cat extends Animal {
The
Cat
class inherits from the Animal
class. It inherits from the Animal
class and indirectly from the Being
class too. It is not declared abstract, which means that we can instantiate it.parent::__construct($age);
In the constructor of the
Cat
class, we call the parent constructor using the parent
keyword, followed by two colons and the __construct()
method. The constructors of the parent classes must be called explicitly.$cat = new Cat("Cici", 4); $cat->isAlive(); echo $cat->getName() . " is " . $cat->getAge() . " years old\n"; $cat->kill(); $cat->isAlive();
We create a new Cat: Cici, 4 years old. Then we call functions on the cici object. Note the usage of methods that were not created in the
Cat
class, but rather inherited from the parent classes.$ php inheritance.php Being is alive Cici is 4 years old Being is not alive
Output of the script.
Abstract classes and methods
PHP 5 introduces abstract classes and methods. Abstract classes cannot be instantiated. If a class contains at least one abstract method, it must be declared abstract too. Abstract methods cannot be implemented, they merely declare the methods' signatures. When we inherit from an abstract class, all abstract methods must be implemented by the derived class. Furthermore, these methods must be declared with the same of less restricted visibility.
Unlike interfaces, abstract classes may have methods with full implementation and may also have defined member fields. So abstract classes may provide a partial implementation. Programmers often put some common functionality into abstract classes. And these abstract classes are later subclassed to provide more specific implementation. For example, the Qt graphics library has a
QAbstractButton
, which is the abstract base class of button widgets, providing functionality common to buttons. Buttons Q3Button
, QCheckBox
, QPushButton
, QRadioButton
, and QToolButton
inherit from this base abstract class.
Formally put, abstract classes are used to enforce a protocol. A protocol is a set of operations, which all implementing objects must support.
<?php abstract class Drawing { protected $x = 0; protected $y = 0; public abstract function area(); public function getCoordinates() { echo "\$x is $this->x\n"; echo "\$y is $this->y\n"; } } class Circle extends Drawing { private $radius; public function __construct($x, $y, $r) { $this->radius = $r; $this->x = $x; $this->y = $y; } public function area() { return $this->radius * $this->radius * pi(); } public function __toString() { return "Circle, at x: $this->x, y: $this->y, radius: $this->radius"; } } $o = new Circle(12, 45, 22); echo "$o \n"; echo "Area of the circle: " . $o->area() . "\n"; echo $o->getCoordinates(); ?>
In our PHP script, we have an abstract base
Drawing
class. The class defines two member fields, defines one method and declares one method. One of the methods is abstract, the other one is fully implemented. The Drawing
class is abstract, because we cannot draw it. We can draw a circle, a dot or a square. The Drawing
class has some common functionality to the objects that we can draw.class Circle extends Drawing {
A
Circle
is a subclass of the Drawing
class. It must implement the abstract area method.$ php abstract.php Circle, at x: 12, y: 45, radius: 22 Area of the circle: 1520.53084434 $x is 12 $y is 45
Output of the script.
Interfaces
A remote control is an interface between the viewer and the TV. It is an interface to this electronic device. Diplomatic protocol guides all activities in the diplomatic field. Rules of the road are rules that motorists, cyclists and pedestrians must follow. Interfaces in programming are analoguos to the previous examples.
Interfaces are:
- APIs
- Contracts
Objects interact with the outside world with the methods, they expose. The actual implementation is not important to the programmer, or it also might be secret. A company might sell a library and it does not want to disclose the actual implementation. A programmer might call a
maximize()
method on a window of a GUI toolkit, but knows nothing about how this method is implemented. From this point of view, interfaces are methods, through which objects interact with the outside world, without exposing too much about their inner workings.
From the second point of view, interfaces are contracts. If agreed upon, they must be followed. They are used to design an architecture of an application. They help organize the code.
Interfaces are fully abstract types. They are declared using the
interface
keyword. Interfaces can only have method signatures and constants. All method signatures declared in an interface must be public. They cannot have fully implemented methods, nor member fields. A PHP class may implement any number of interfaces. An interface can also extend any number of interfaces. A class that implements an interface must implement all method signatures of an interface.
Interfaces are used to simulate multiple inheritance. A PHP class can extend only one class. A PHP class can implement multiple interfaces. Multiple inheritance using the interfaces is not about inheriting methods and variables. It is about inheriting ideas or contracts, which are described by the interfaces.
There is one important distinction between interfaces and abstract classes. Abstract classes provide partial implementation for classes that are related in the inheritance hierarchy. Interfaces on the other hand can be implemented by classes that are not related to each other. For example, we have two buttons. A classic button and a round button. Both inherit from an abstract button class that provides some common functionality to all buttons. Implementing classes are related, since all are buttons. Another example might have classes
Database
and SignIn
. They are not related to each other. We can apply an ILoggable
interface that would force them to create a method to do logging.<?php interface IInfo { public function do_inform(); } class Some implements IInfo { public function do_inform() { echo "This is a Some class\n"; } } $sm = new Some(); $sm->do_inform(); ?>
This is a simple PHP script demonstrating an interface.
interface IInfo { public function do_inform(); }
This is an interface
IInfo
. It has the do_inform()
method signature.class Some implements IInfo {
We use the
implements
to implement from an interface.public function do_inform() { echo "This is a Some class\n"; }
The class provides an implementation for the
do_inform()
method.
The next example shows, how a class can implement multiple interfaces.
<?php interface Device { public function switch_on(); public function switch_off(); } interface Volume { public function volume_up(); public function volume_down(); } interface Pluggable { public function plug_in(); public function plug_off(); } class CellPhone implements Device, Volume, Pluggable { public function switch_on() { echo "Switching on\n"; } public function switch_off() { echo "Switching off\n"; } public function volume_up() { echo "Volume up\n"; } public function volume_down() { echo "Volume down\n"; } public function plug_in() { echo "Plugging in\n"; } public function plug_off() { echo "Plugging off\n"; } } $o = new CellPhone(); $o->switch_on(); $o->volume_up(); $o->plug_in(); ?>
We have a CellPhone class that inherits from three interfaces.
class CellPhone implements Device, Volume, Pluggable {
The class implements all three interfaces, which are divided by a comma. The
CellPhone
class must implement all method signatures from all three interfaces.$ php interface.php Switching on Volume up Plugging in
Running the PHP script.
The next example shows how interfaces can extend from multiple other interfaces.
<?php interface IInfo { public function do_inform(); } interface IVersion { public function get_version(); } interface ILog extends IInfo, IVersion { public function do_log(); } class DBConnect implements ILog { public function do_inform() { echo "This is a DBConnect class\n"; } public function get_version() { echo "Version 1.02\n"; } public function do_log() { echo "Logging\n"; } public function connect() { echo "Connecting to the database\n"; } } $db = new DBConnect(); $db->do_inform(); $db->get_version(); $db->do_log(); $db->connect(); ?>
In this PHP script, we define three interfaces. Extending interfaces allows us to organize them.
interface ILog extends IInfo, IVersion { public function do_log(); }
The ILog interface extends other two interfaces.
public function do_inform() { echo "This is a DBConnect class\n"; }
The
DBConnect
class implements the do_inform()
method. This method was inherited by the ILog
interface, which the class implements.Polymorphism
The polymorphism is the process of using an operator or function in different ways for different data input. In practical terms, polymorphism means that if class B inherits from class A, it doesn’t have to inherit everything about class A; it can do some of the things that class A does differently. (wikipedia)
In general, polymorphism is the ability to appear in different forms. Technically, it is the ability to redefine methods for derived classes. Polymorphism is concerned with the application of specific implementations to an interface or a more generic base class.
<?php abstract class Shape { private $x = 0; private $y = 0; public abstract function area(); } class Rectangle extends Shape { function __construct($x, $y) { $this->x = $x; $this->y = $y; } function area() { return $this->x * $this->y; } } class Square extends Shape { function __construct($x) { $this->x = $x; } function area() { return $this->x * $this->x; } } $shapes = array( new Square(5), new Rectangle(12, 4), new Square(8) ); foreach ($shapes as $shape) { echo $shape->area() . "\n"; } ?>
In the above PHP script, we have an abstract
Shape
class. This class morphs into two descendant classes: Rectangle
and Square
. Both provide their own implementation of the area()
method. Polymorphism brings flexibility and scalability to the OOP systems.Object-oriented programming II in PHP
In this chapter of the PHP tutorial, we continue description of the OOP in the PHP language.
The static keyword
We can declare class properties and methods to be
static
. The static
properties and methods do not belong to the instance of the class. They belong to the class itself. They are accessible through the scope resolution operator ::
.<?php class Sys { public static function println($string) { echo "$string\n"; } } Sys::println("PHP"); Sys::println("PERL"); Sys::println("Python"); Sys::println("Pike"); ?>
In the above PHP script, we have a static
println()
method. It prints a string and starts a new line. This example is inspired by the Java language.Sys::println("PHP");
We do not need an object to call
println()
method. We call static
methods by specifying the class name, followed by double colon operator and the method name.$ php static1.php PHP PERL Python Pike
This is the output of the script.
<?php class Math { public static $PI = 3.14159265359; } echo Math::$PI . "\n"; ?>
And now we have an example with a
static
variable.echo Math::$PI . "\n";
We access the variable by specifying the class name, followed by the scope resolution operator and the variable name.
The final keyword
Final methods cannot be overridden and final classes cannot be extended. The
final
keyword is a matter of design of the application. Some classes should not be extended and some methods should not be overridden. This behaviour is enforced by the final
keyword.<?php class Base { final function say() { echo "Base class"; } } class Derived extends Base { function say() { echo "Derived class"; } } ?>
This PHP script won't compile. We get an error "Cannot override final method Base::say()".
<?php final class Math { static function getPI() { return 3.141592; } } class DerivedMath extends Math { function say() { echo "DerivedMath class"; } } ?>
In the previous PHP script, we have a prototype base
Math
class. The sole purpose of this class is to provide some helpful methods and constants to the programmer. (In our case we have only one method for simplicity reasons.) It is not created to be extended. To prevent uninformed other programmers to derive from this class, the creators made the class final
. If you try to run this PHP script, you get the following error: "Fatal error: Class DerivedMath may not inherit from final class (Math)".Deep copy vs shallow copy
Copying of data is an important task in programming. Object is a composite data type in OOP. Member field in an object may be stored by value or by reference. Copying may be performed in two ways.
The shallow copy copies all values and references into a new instance. The data to which a reference is pointing is not copied; only the pointer is copied. The new references are pointing to the original objects. Any changes to the reference members affect both objects.
The deep copy copies all values into a new instance. In case of members that are stored as references a deep copy performs a deep copy of data that is being referenced. A new copy of a referenced object is created. And the pointer to the newly created object is stored. Any changes to those referenced objects will not affect other copies of the object. Deep copies are fully replicated objects.
In PHP, we have a
copy
keyword, which performs a shallow copy by default. It calls the object's__clone()
method. We can implement the method to create our custom deep copy. In PHP 5, all objects are assigned by reference.
The next two examples will perform a shallow and a deep copy on objects.
<?php class Object { public $id; public $size; public $color; function __construct($id, $size, $color) { $this->id = $id; $this->size = $size; $this->color = $color; } } class Color { public $red; public $green; public $blue; function __construct($red, $green, $blue) { $this->red = $red; $this->green = $green; $this->blue = $blue; } } $color = new Color(23, 42, 223); $object1 = new Object(23, "small", $color); $object2 = clone $object1; $object2->id++; $object2->color->red = 255; $object2->size = "big"; print_r($object1); print_r($object2); ?>
In the above PHP script, we define two custom objects:
Object
and Color
. The Object
object will have a reference to the Color
object.$color = new Color(23, 42, 223);
We create an instance of the Color object.
$object1 = new Object(23, "small", $color);
An instance of the Object object is created. It passes the instance of the Color object to its constructor.
$object2 = clone $object1;
We perform a shallow copy of the
Object
object.$object2->id++; $object2->color->red = 255; $object2->size = "big";
Here we modify the member fields of the cloned object. We increment the id, change the red part of the color object and change the size to "big".
print_r($object1); print_r($object2);
We use the
print_r()
function to compare the results.$ php shallowcopy.php Object Object ( [id] => 23 [size] => small [color] => Color Object ( [red] => 255 [green] => 42 [blue] => 223 ) ) Object Object ( [id] => 24 [size] => big [color] => Color Object ( [red] => 255 [green] => 42 [blue] => 223 ) )
We can see that the ids are different: 23 vs 24. The size is different. "small" vs "big". But the red part of the color object is same for both instances. 255. Changing member values of the cloned object did not affect the original object. Changing members of the referenced object has affected the original object too. In other words, both objects refer to the same color object in memory.
To change this behaviour, we will do a deep copy next.
<?php class Object { public $id; public $size; public $color; function __construct($id, $size, $color) { $this->id = $id; $this->size = $size; $this->color = $color; } function __clone() { $red = $this->color->red; $green = $this->color->green; $blue = $this->color->blue; $this->color = new Color($red, $green, $blue); } } class Color { public $red; public $green; public $blue; function __construct($red, $green, $blue) { $this->red = $red; $this->green = $green; $this->blue = $blue; } } $color = new Color(23, 42, 223); $object1 = new Object(23, "small", $color); $object2 = clone $object1; $object2->id++; $object2->color->red = 255; $object2->size = "big"; print_r($object1); print_r($object2); ?>
In this PHP script, we have implemented the
__clone()
method.function __clone() { $red = $this->color->red; $green = $this->color->green; $blue = $this->color->blue; $this->color = new Color($red, $green, $blue); }
Inside the
__clone()
method, we copy the red, green and blue member fields and create a new Color object. Now, the $color field points to a different Color
object.$ php deepcopy.php Object Object ( [id] => 23 [size] => small [color] => Color Object ( [red] => 23 [green] => 42 [blue] => 223 ) ) Object Object ( [id] => 24 [size] => big [color] => Color Object ( [red] => 255 [green] => 42 [blue] => 223 ) )
Now the red part of the referenced
Color
object is not the same. The original object has retained its previous 23 value.Exceptions
Exceptions are designed to handle the occurrence of exceptions, special conditions that change the normal flow of program execution. Exceptions are raised or thrown, initiated.
During the execution of our application, many things might go wrong. A disk might get full and we cannot save our file. An Internet connection might go down and our application tries to connect to a site. All these might result in a crash of our application. To prevent happening this, we must cope with all possible errors that might occur. For this, we can use the exception handling.
Exceptions were only recently built into the PHP 5 language. Most PHP errors still use the old error reporting and not exceptions. With a
set_error_handler()
we can do a workaround for this.<?php set_error_handler("error_handler"); function error_handler($errno, $errstring, $errfile, $line, $trace) { throw new ErrorException($errstring, $errno, 0, $errfile, $line); } try { $a = 0; $b = 32; $c = $b / $a; } catch(ErrorException $e) { echo "Error occurred\n"; echo $e->getMessage(), "\n"; } ?>
In the above PHP script, we intentionally divide a number by zero. This leads to an error. The error is not an exception and is not caught by the
catch
keyword.set_error_handler("error_handler");
The
set_error_handler()
function sets a user defined error handler function.function error_handler($errno, $errstring, $errfile, $line, $trace) { throw new ErrorException($errstring, $errno, 0, $errfile, $line); }
Inside the
set_error_handler()
function, we throw an ErrorException
. This exception is later caught by the catch
keyword.try { $a = 0; $b = 32; $c = $b / $a; }
The code that we are checking against an error is put inside the block following the
try
keyword.} catch(Exception $e) { echo $e->getMessage(); }
The
catch
keyword is used to catch an exception, which occurred. To find out more info, we call the getMessage()
method on the exception object.$ php zerodiv.php Error occurred Division by zero
This is the output of our PHP script.
The
Exception
is a base class for all exceptions. We can create our own exceptions derived from this base class.<?php define("LIMIT", 333); class BigValueException extends Exception { public function __construct($message) { parent::__construct($message); } } $a = 34325; try { if ($a > LIMIT) { throw new BigValueException("Exceeded the maximum value allowed\n"); } } catch (BigValueException $e) { echo $e->getMessage(); } ?>
Let's say, we have a situation in which we cannot deal with big numbers.
define("LIMIT", 333);
Numbers bigger than this constant are considered to be "big" by our PHP script.
class BigValueException extends Exception {
We have a
BigValueException
class. This class derives from the Exception
class through the extends
keyword.public function __construct($message) { parent::__construct($message); }
Inside the constructor, we call the parent's constructor.
if ($a > LIMIT) { throw new BigValueException("Exceeded the maximum value allowed\n"); }
If the value is bigger than the limit, we throw our custom exception. We give the exception a message "Exceeded the maximum value allowed".
} catch (BigValueException $e) { echo $e->getMessage(); }
We catch the exception and print its message to the console.
Constructor overloading
At the time of writing this tutorial (February 2010), constructor overloading is not supported in PHP language. In other words, each class can only have one constructor defined. Many programmers that already know Java or C# languages are looking for a similar feature in PHP. There are two ways how we can handle this.
The first solution is based on the
func_get_args()
function. The second solution uses a factory pattern.<?php class Book { private $title = "not specified"; private $author = "not specified"; private $year = "not specified"; public function __construct() { $args = func_get_args(); foreach(array("title", "author", "year") as $item) { if(empty($args)) { break; } $this->$item = array_shift($args); } } public function __toString() { return "Author: $this->author\nTitle: $this->title\nPublished: $this->year\n\n"; } } $book1 = new Book("Stephen Prata", "C Primer Plus"); echo $book1; $book2 = new Book("Joshua Bloch", "Effective Java", 2008); echo $book2; ?>
In the above script, we have a
Book
class. We instantiate the class with 2 and 3 parameters.private $author = "not specified"; private $title = "not specified"; private $year = "not specified";
We have three member fields defined. Their initial value is "not specified".
$args = func_get_args();
The
func_get_args()
function returns an array comprising a function's argument list. So the idea is: the code inside the constructor is dynamic, it depends on the arguments passed to it.foreach(array("title", "author", "year") as $item)
We go through all member fields using the
foreach
keyword.$this->$item = array_shift($args);
One of the most basic tasks in constructors is to initialize the member fields of the class. This is done by the above code line. The
array_shift()
function removes the first item from the array and returns it.$book1 = new Book("Stephen Prata", "C Primer Plus"); ... $book2 = new Book("Joshua Bloch", "Effective Java", 2008);
We have two different constructors. The first takes 2 parameters, the second takes 3.
$ php constructors.php Author: C Primer Plus Title: Stephen Prata Published: not specified Author: Effective Java Title: Joshua Bloch Published: 2008
This is the outcome of the script.
The next code example simulates constructor overloading using the factory pattern. It is one of the creational patterns in OOP. The pattern creates objects without specifying the exact class of object that will be created. More generally, the term factory method is often used to refer to any method whose main purpose is creation of objects. (from Wikipedia)
<?php class Cat { private $name = "unspecified"; private $age = "unspecified"; public static function withName($name) { $cat = new Cat(); $cat->name = $name; return $cat; } public static function withAge($age) { $cat = new Cat(); $cat->age = $age; return $cat; } public static function fullCat($name, $age) { $cat = new Cat(); $cat->name = $name; $cat->age = $age; return $cat; } public function __toString() { return "Name: $this->name, Age: $this->age\n"; } } $cici = Cat::withName("Cici"); echo $cici; $missy = Cat::withAge(6); echo $missy; $lucky = Cat::fullCat("Lucky", 4); echo $lucky; ?>
We have a
Cat
factory class in the above PHP script. It has three different static functions. Each of them returns a specific cat object.private $name = "unspecified"; private $age = "unspecified";
We have two member fields. Their initial value is "unspecified".
public static function withName($name) { $cat = new Cat(); $cat->name = $name; return $cat; }
Here is a
withName()
static function. This function creates an instance of the Cat
class. Set the name member field and returns the object.$cici = Cat::withName("Cici"); echo $cici;
We create an instance of the cat with one of the factory methods. We echo the object. e.g. call the
__toString()
method of the class.$ php factory.php Name: Cici, Age: unspecified Name: unspecified, Age: 6 Name: Lucky, Age: 4
The output of the script.
0 comments:
Post a Comment