As you probably know, the new OOP features in PHP 5 bring a new perspective to PHP applications. In this article, you will see the new PHP 5 features regarding constructors and destructors, which are one of the most important aspects in any type of object-orientated class. As a reminder, a constructor is a special function of a class that is automatically executed whenever an object of a class gets instantiated. A destructor is a special function of a class that is automatically executed whenever an object should be destroyed.
The article starts with a short overview of PHP 5 constructors, compared with the old fashion PHP 4. It continues with an explanation of writing constructors with or without parameters, restrictive constructors, overloading and PHP 5 destructors. At the end, you will be able to write almost any kind of constructor for your PHP 5 classes.
PHP 5 vs. PHP 4 Constructors
As a developer, each time you try to learn something new (a technology, a design pattern, a paradigm, etc.) you start by looking at what you used in the past and trying to identify the advantages of the new concept. This approach can tell you if using PHP 5 constructors is worth your time. To that end, consider the following comparison that explores why PHP 5 constructors are superior to PHP 4:
- PHP 5 introduces new unified constructor/destructor names. In PHP 4, a constructor was just a method that had the same name as the class itself; this is a common approach in many programming languages. The main issue here is that each time you rename the class you must rename the constructor also.
- In PHP 5, all constructors are named
__construct() (that is, the word construct prefixed by two underscores).
- PHP 5 introduces
newly __destruct(), which is destruct prefixed by two underscores. This allows you to write code that will be executed when the object is destroyed and it is automatically called; parent destructors will not be called implicitly by the engine and it must explicitly call parent::__destruct() in the destructor body.
- For backwards compatibility, if PHP 5 cannot find a
__construct() function for a given class, it will search for the old-style constructor function by the name of the class, starting with PHP 5.3.3. Methods with the same name as the last element of a namespaced class name will no longer be treated as constructors.
Here are some important notes regarding constructors and destructors from the
PHP documentation:
- Parent constructors are not called implicitly if the child class defines a constructor. In order to run a parent constructor, a call to
parent::__construct() within the child constructor is required.
- Destructors called during the script shutdown have HTTP headers already sent. The working directory in the script shutdown phase can be different with some SAPIs (e.g. Apache).
- Attempting to throw an exception from a destructor (called in the time of script termination) causes a fatal error.
- A destructor cannot take any arguments.
Your First PHP 5 Constructor
Let's start with a basic example of a PHP 5 constructor with no arguments. Its job will be to create an instance of its class and to initialize a set of class properties. Look through the code to get a first impression of what is happening.
<?php
class Player {
private $name;
private $surname;
private $country;
private $atp;
//this is a simple PHP 5 constructor
public function __construct() {
$this->name = "empty";
$this->surname = "empty";
$this->country = "empty";
$this->atp = 0;
}
public function setDetails($name, $surname, $country, $atp) {
$this->name = $name;
$this->surname = $surname;
$this->country = $country;
$this->atp = $atp;
}
public function displayDetails() {
echo "Name : " . $this->name . " Surname: " .
$this->surname . " Country: " . $this->country .
" Atp Ranking: " . $this->atp . "<br />";
}
}
$player_1 = new Player();
$player_1->displayDetails();
$player_1->setDetails("Rafael", "Nadal", "ESP", 1);
$player_1->displayDetails();
?>
The bolded code represents the PHP 5 unified constructor. The constructor's body initializes the four class properties with default values, while the desired values are passed to the setDetails function. Notice the use of the $this keyword to indicate that you will initialize the class properties, not local properties.
The output of this class is shown in Figure 1 below.
Click here for larger image
Figure 1. Output of Player Class (Constructor with No Arguments)
Writing a PHP 5 Constructor with Parameters
Usually, the desired values are set inside the constructor, which is much closer to the real cases. When you create a class instance, you also set the object's properties. The code below is similar to the one above; the only difference being that you eliminate the setDetails method and pass its behavior to the unified constructor:
<?php
class Player {
private $name;
private $surname;
private $country;
private $atp;
//this is a constructor with parameters
public function __construct($name, $surname, $country, $atp) {
$this->name = $name;
$this->surname = $surname;
$this->country = $country;
$this->atp = $atp;
}
public function displayDetails() {
echo "Name : " . $this->name . " Surname: " .
$this->surname . " Country: " . $this->country .
" Atp Ranking: " . $this->atp . "<br />";
}
}
$player_1 = new Player("Rafael", "Nadal", "ESP", 1);
$player_1->displayDetails();
$player_2 = new Player("Roger", "Federer", "SUI", 2);
$player_2->displayDetails();
?>
This kind of constructor is the most common in real applications. It receives a set of parameters -- which have the same names as the class properties -- and uses the $this keyword to associate each class property to the proper parameter.
The output of this class is shown in Figure 2 below.
Click here for larger image
Figure 2. Output of Player Class (Constructor with Parameters)
Your First PHP 5 Destructor
If the constructor is responsible for creating objects, the destructor is responsible for destroying objects. The destructor provides an elegant way for accomplishing necessary cleanup operations such as unsetting internal class objects, closing database connections or socket connections, etc. The destructor is automatically called when an object should be destroyed. An object of a class is destroyed when:
- it goes out of scope
- you specifically set it to null
- you unset it or the program execution is over
The destructor below calls the unset method with the class property passed as an argument:
<?php
class Player {
private $name;
private $surname;
private $country;
private $atp;
public function __construct($name, $surname, $country, $atp) {
$this->name = $name;
$this->surname = $surname;
$this->country = $country;
$this->atp = $atp;
}
public function displayDetails() {
echo "Name : " . $this->name . " Surname: " . $this->surname . "
Country: " . $this->country . " Atp Ranking: " . $this->atp . "<br />";
}
public function __destruct() {
unset($this->name);
unset($this->surname);
unset($this->country);
unset($this->atp);
echo("
Object destroyed ...");
}
}
$player_1 = new Player("Rafael", "Nadal", "ESP", 1);
$player_1->displayDetails();
$player_2 = new Player("Roger", "Federer", "SUI", 2);
$player_2->displayDetails();
?>
The output of this class is shown in Figure 3 below.
Click here for larger image
Figure 3. Output of Player Class (Destructor)
Here are some important notes regarding destructors from the
PHP documentation:
- Attempting to throw an exception from a destructor (called in the time of script termination) causes a fatal error.
- A destructor cannot take any arguments.
Introducing PHP 5 Restrictive Constructors
Restrictive constructors sound complex, but they actually are simple. A restrictive constructor is nothing but a regular constructor method whose level of visibility is protected or private. This function can be necessary in several cases, but two of the best known are:
- The implementation of certain creation design patterns, such a Singleton and Factory
- Building classes that are intended to be used out of the object context
Per example, a constructor for a Singleton class should be defined as below (as private, in this case):
//Locked down the constructor, therefore the class cannot be externally instantiated
private function __construct() { }
While this constructor is locked, the class supports a single instance through a method like this:
//A static member variable representing the class instance
private static $_instance = null;
public static function getInstance()
{
//or if(is_null(self::$_instance)) or if(self::$_instance == null)
if(!is_object(self::$_instance))
self::$_instance = new self;
//or, in PHP 5.3.0 or newer
//if (empty(static::$_instance)) {
// $class = get_called_class();
// static::$_instance = new $class;
//}
return self::$_instance;
}
If you are not familiar with the Singleton design pattern, read the article
Implementing the Singleton Pattern in PHP 5 for instructions.
PHP 5 Constructor Overloading
Overloading constructors is a common task usually implemented as multiple constructors with the same name as the class but with different numbers/types of arguments. This is not possible with PHP 5 because it does not support constructor overloading. However, you can improvise using the __call method.
For those who are not familiar with the PHP 5 _call method, suppose that in a case of method overloading, the code defines a method that acts as a wildcard for calls to undefined methods of the corresponding class. This wildcard method will be called only when the class does not contain the method you are trying to access. Using the magic of __call() you can develop the following code:
<?php
class Player {
private $name;
private $surname;
private $country;
private $atp;
public function __construct() {
$num = func_num_args();
$args = func_get_args();
switch($num){
case 0:
$this->__call('__construct_0', null);
break;
case 1:
$this->__call('__construct_1', $args);
break;
case 2:
$this->__call('__construct_2', $args);
break;
case 3:
$this->__call('__construct_3', $args);
break;
case 4:
$this->__call('__construct_4', $args);
break;
default:
throw new Exception();
}
}
public function __construct_0(){
echo "constructor 0" . "<br />";
}
public function __construct_1($name){
$this->name = $name;
echo "constructor 1: " . $this->name . "<br />";
}
public function __construct_2($name, $surname){
$this->name = $name;
$this->surname = $surname;
echo "constructor 2: " . $this->name . "|" . $this->surname . "<br />";
}
public function __construct_3($name, $surname, $country){
$this->name = $name;
$this->surname = $surname;
$this->country = $country;
echo "constructor 3: " . $this->name . "|" . $this->surname .
"|" . $this->country . "<br />";
}
public function __construct_4($name, $surname, $country, $atp){
$this->name = $name;
$this->surname = $surname;
$this->country = $country;
$this->atp = $atp;
echo "constructor 4: " . $this->name . "|" . $this->surname . "|" .
$this->country . "|" . $this->atp . "<br />";
}
private function __call($con, $arg){
return call_user_func_array(array($this, $con), $arg);
}
}
$player_1 = new Player("Rafael");
$player_2 = new Player("Rafael", "Nadal");
$player_3 = new Player("Rafael", "Nadal", "ESP");
$player_4 = new Player("Rafael", "Nadal", "ESP", "1");
?>
Well, you have several interesting things here. The application starts when an instance of the Player class is created (this code flow is the same for each of the four instances) and ends when the instance properties are set to the passed values. When the instance is created, the __construct() constructor is called. This constructor is called no matter how many arguments you pass to it as the start point for creating a new instance. This constructor acts as a generic one, and is responsible for dispatching the initialization phase of the new object to four methods, named __construct_number_of_arguments().
To accomplish dispatching, the constructor needs the magic __call() method, which gets two arguments. The first argument is the name of the original method (in some cases this can be an unfound method but in this case). The second one is a one-dimensional, numerically indexed array containing all of the arguments for the original method.
Calling a defined/undefined method with two arguments, Rafael and Nadal, will result in the following array:
Array
(
[0] => Rafael
[1] => Nadal
)
Next, the __call() method will call the original method and pass the set of arguments.
At this point, the flow of execution is in the fake overloaded constructor, depending on the number of arguments. From here, trivial code initializes the new object properties and prints out a checking message. This message should be deleted and getter and setter methods should be used instead.
The output of this class is shown in Figure 4 below.
Click here for larger image
Figure 4. Output of Player Class (Constructor Overloading)
Conclusion
In this article, you have explored the most important aspects of the new constructor/destructor mechanism available with PHP 5's new OOP features. You saw how to write simple constructors/destructors and restrictive constructors, and how to simulate the overloading mechanism, which is not available in PHP 5 -- yet.
About the Author
Octavia Andreea Anghel is a senior PHP developer currently working as a primary trainer for programming teams that participate at national and international software-development contests. She consults on developing educational projects at a national level. She is a coauthor of the book "XML Technologies: XML in Java" (Albastra, ISBN 978-973-650-210-1), for which she wrote the XML portions. In addition to PHP and XML, she's interested in software architecture, web services, UML, and high-performance unit tests. to e-mail her.