Showing posts with label Magic Methods in Php. Show all posts
Showing posts with label Magic Methods in Php. Show all posts

Magic Methods in Php – __clone() method

Lets understand what does object cloning mean.
To clone an object means to create a duplicate of an object. With regular variables $a = $b means that a new variable $a gets created that contains the value of $b. This means that 2 variables get created.

With objects $object2 = $object1 does not mean that a new object i.e. $object2 gets created. When we execute $object2 = $object1, the reference of $object1 is assigned to $object2.
This means that $object1 and $object2 point to the same memory space.

Look at the diagram below.

Lets see at an example where only references are assigned to another object:
class Customers {
 private $name;
 
 public function setName($name) {
  $this->name = $name;
 }
 
 public function getName() {
  return $this->name;
 }
}
 
$c1 = new Customers();
$c1->setName("Hiren");
 
$c2 = $c1; //only reference or memory assigned to $c2
 
$c2->setName("Bhavin");
 
echo $c1->getName()."\n";
echo $c2->getName()."\n";
Output:
Bhavin
Bhavin
In the above example, $c2 has the reference of $c1; therefore, when you set a new name in the $c2 object – $c1 object changes as well. Therefore, when an object is assigned as a reference; changes made to one object are also reflected in the other.
Therefore, to create a new $object2 object we must clone an object to create a new one. To clone an PHP5 Object a special keyword i.e. clone is used. Example below:

        $object2 = clone $object1;

After the above line is executed $object2 with a new memory space is created with the data members having the same value as that of $object1. This is also referred to as shallow copy.
The above technique works with a class having data members that are of intrinsic type i.e. int, boolean, string, float, etc.. However, this technique will not work with a class that has a data member which is an object of another class. In such a scenario, the cloned object continues to share the reference of the data member object of the class that was cloned.
So, how do we resolve this issue? Doing a regular shallow copy won’t help us. To allow aggregated objects (i.e. data members that are objects of another class) to also get cloned properly we need to use the concept of ‘deep copy‘ as opposed to ‘shallow copy‘. To implement a ‘deep copy‘ you should implement the magic method __clone().
You could also provide the implementation of __clone() magic method even when you don’t have an aggregated object. You would want to do this for providing necessary clean up operations, conversions or validations.

Lets explore a simple example of cloning intrinsic data types:

class Customers {
 private $name;
 
 public function setName($name) {
  $this->name = $name;
 }
 
 public function getName() {
  return $this->name;
 }
 
 public function __clone() {
  $c = new Customers();
  $c->setName($this->name);
  return $c;
 }
 
}
 
$c1 = new Customers();
$c1->setName("Hiren");
 
$c2 = clone $c1; //new object $c2 created
 
$c2->setName("Bhavin");
 
echo $c1->getName()."\n";
echo $c2->getName()."\n";
Output:
Hiren
Bhavin
In the above example, observe the line where the statement $c2 = clone $c1 is executed. This is internally represented as $c2 = $c1.__clone(). However, you cannot explicitly call the __clone() method on an object as the __clone() is automatically called. Now that $c1 and $c2 are two individual objects, changes made to one object is not reflected in the other.
Cloning aggregate objects (i.e. data members that are objects of another class)
To clone a class having aggregated objects, you should perform ‘deep copy‘. Please refer to the example below:
class Order {
 private $order_id;
 private $customer;
 
 public function setOrderId($order_id) {
  $this->order_id = $order_id;
 }
 
 public function getOrderId() {
  return $this->order_id;
 }
 
 public function setCustomer(Customer $customer) {
  $this->customer = clone $customer;
 }
 
 public function getCustomer() {
  return $this->customer;
 }
 
 public function __clone() {
 
  $order = new Order();
  $order->setOrderId($this->order_id);
 
 
  //force a copy of the same object to itself, otherwise
  //it takes the same instance. Seems like a bug to me
  $this->customer = clone $this->customer;
  $order->setCustomer($this->customer);
  return $order;
 }
 
}
 
class Customer {
 private $name;
 
 public function setName($name) {
  $this->name = $name;
 }
 
 public function getName() {
  return $this->name;
 }
 
 public function __clone() {
  $c = new Customer();
  $c->setName($this->name);
  return $c;
 }
 
}
 
$c = new Customer();
$c->setName("Hiren");
 
$order1 = new Order();
$order1->setOrderId("OD0001");
$order1->setCustomer($c);
 
$order2 = clone $order1;
 
$order2->getCustomer()->setName("Bhavin");
 
var_dump($c);
var_dump($order1);
var_dump($order2);
Output:
object(Customer)#1 (1) {
["name:private"]=>
string(5) "Hiren"
}
object(Order)#2 (2) {
["order_id:private"]=>
string(6) "OD0001"
["customer:private"]=>
object(Customer)#3 (1) {
["name:private"]=>
string(5) "Hiren"
}
}
object(Order)#4 (2) {
["order_id:private"]=>
string(6) "OD0001"
["customer:private"]=>
object(Customer)#6 (1) {
["name:private"]=>
string(6) "Bhavin"
}
}
In the example above both $order1 and $order2 have their own set of customer objects, therefore changes made to one object is not reflected in another. This example implements the concepts of ‘deep copy‘.
A special note on $this->customer = clone $this->customer; For some reason it is necessary to do this for proper working of aggregated cloning.

Magic Methods in Php – __sleep() and __wakeup()

The magic method __sleep() and __wakeup() is called when an object is serialized.
The magic method __sleep() and __wakeup() provides a method to clean up and restore objects before being serialized.

Working with the magic method __sleep().

__sleep() magic method is called when the object of a class is about to be serialized. This magic method __sleep() does not accept any parameter and returns an array. The array should contain a list of class members that should be serialized. This means that if you don’t wish to serialize a particular class member, you should not include it in the array.

Example :


class Customers {
 private $name;
 private $credit_card_number;
 
 public function setName($name) {
  $this->name = $name;
 }
 
 public function getName() {
  return $this->name;
 }
 
 public function setCC($cc) {
  $this->credit_card_number = $cc;
 }
 
 public function getCC() {
  return $this->credit_card_number;
 }
 
 public function __sleep() {
  return array("name"); //because of this, only name is serialized
 }
 
}
 
$c = new Customers();
$c->setName("Hiren");
$c->setCC("1234567890123456");
 
$data = serialize($c)."\n";
echo $data."\n";
Output is :
O:8:”Customers”:1:{s:14:” Customers name”;s:5:”Hiren”;}
In the above example, you can see that the serialized string data only contains the name of the Customers Object. This is because the __sleep() magic method returned an array containing only the ‘name’ data member.

Working with the magic method __wakeup().


__wakeup() magic method is the opposite of the __sleep() method. It is called when the object of a class is about to be unserialized. This magic method __wakeup() does not accept any parameter nor returns anything. The __wakeup() method is responsible for setup operations after an object has been unserialized.

Example :

class Customers {
 private $name;
 private $credit_card_number;
 
 public function setName($name) {
  $this->name = $name;
 }
 
 public function getName() {
  return $this->name;
 }
 
 public function setCC($cc) {
  $this->credit_card_number = $cc;
 }
 
 public function getCC() {
  return $this->credit_card_number;
 }
 
 public function __sleep() {
  return array("name");
 }
 
 public function __wakeup() {
  if($this->name == "Hiren") {
   //you would ideally fetch CC data from Database
   $this->credit_card_number = "1234523490123456";
  }
 }
}
 
$c = new Customers();
$c->setName("Hiren");
$c->setCC("1234523490123456");
 
$data = serialize($c)."\n";
var_dump(unserialize($data));

Output:

object(Customers)#2 (2) {
[name:private]=>
string(5) "Hiren"
[credit_card_number:private]=>
string(16) "1234523490123456"
}
In the above example, you can see that after the $c object has been serialized and the output stored in $data variable, we use the $data variable and pass it to the unserialize(). Before the object is unserizlied and object created, the __wakeup() method is called. In the __wakeup() method you should ideally make a database call to fetch data of the missing member variable.


Magic Methods in Php – __call() method

When to use the magic method __call(). ?
The magic method __call() is to undeclared methods what __get() and __set() are to undeclared data member.
These methods are automatically called internally when the program tires to execute a method that has not been defined within the class at the time of development.

The magic method __call() takes two arguments. The first argument is the name of the undeclared method invoked by the program and the second is an array that contains a list of parameters passed to the undeclared array.

Look at the example below:
class Customer {
 
 public function __call($name, $args) {
  var_dump($name);
  echo "\n";
  var_dump($args);
  echo "\n";
 }
}
 
$c = new Customer();
$c->setName("Hiren","Prajapati");
Output:
string(7) "setName"
array(2) {
[0]=>
string(5) "Hiren"
[1]=>
string(6) "Prajapati"
}
In the example above, an object of the Customer class is created and an undeclared method viz. $c->setName is called. The magic method __call() is internally executed which accepts two parameters. The first parameter $name contains the name of the method i.e. setName and the second parameter $args contains the arguments passed to the setName method i.e Hiren &Prajapati.
Using this method, you can provide code to handle calls to undeclared method. To disallow programs to call an undeclared method, you should raise an exception from within __call() magic method.
Look at the example below:
class Customer {
 
 public function __call($name, $args) {
  throw new Exception("Undeclared method execution not allowed",10);
 }
}
 
$c = new Customer();
$c->setName("Hiren","Prajapati");
Output:

Fatal error: Uncaught exception Exception with message "Undeclared method execution not allowed in .....":6
Stack trace:
#0 [internal function]: Customer->__call("setName", Array)
#1 ......(11): Customer->setName("Hiren","Prajapati")
#2 {main}
thrown in ..... on line 6

In the above program, when the script calls an undeclared variable $c->setName(), the magic method __call() is executed. On executing the magic method __call(), an exception is raised and the execution of the program stops there (unless we use the try..catch statements)

Magic Methods – __autoload() method

Learn magic method __autoload().
The magic method __autoload() function is a convenience that allows you to use classes without having to explicitly write code to include them.
The magic method __autoload() is not included in your class definition as this is to be called once in a script. The best place to put the autoload() file is in your configuration file which is loaded in all your other scripts.

Many people debate that the magic method __autoload() causes a performance overhead. Well, that is not the case. There is no performance penalty to pay. In fact, there may be performance improvements if not all classes are used all the time. This is explained below.
Using the magic method __autoload has the beneficial side effect of requiring strict naming conventions for files that hold class definitions.
Look at the example below:
include “customer.php”;
include “orders.php”;
 
$c = new Customer();
In the example displayed above, an instance of class Customer is created. Therefore, we only need the customers.php file. The file orders.php is not needed. This means that we should only have included the customer.php file. But, what if during execution on the basis of a condition, an instance of class Orders would have to be created. Therefore you need to include both the files i.e. customer.php and orders.php
But this causes performance issues. Every time the above script is executed, orders.php is included. To avoid this performance hit, we would have to do additional programming to ensure that the file orders.php is loaded only when needed.
This is the reason why magic method __autoload() should be used. Look at the example below:
function __autoload($class) {
   require $class . '.php'; //is substituted as require Customer.php (with capital 'C')
}
 
$c = new Customer();
In the above program, we don’t explicitly include customer.php and orders.php file. When an instance of the customer class is to be created, the PHP engine checks to see if the file Customer.php is loaded. It does not raise an warning on finding that Customer.php has not been loaded, it in turn calls the magic method __autoload(). The __autoload() magic method accepts a parameter which is the name of the class that needs to be loaded.
Therefore, on the line when an instance of the customer class is created i.e. object $c, magic method __autoload() is called with the parameter $class containing value ‘Customer’. Within the __autoload() method we call the ‘require’ method. The require method tries to load $class.’php’ file i.e. Customer.php. Therefore, as stated earlier, the __autoload() method has its beneficial side effect of requiring strict file naming convention.
The __autoload() method is called only once for each new class that needs to be loaded. Subsequent instantiation of the Customer class object will not call the __autoload() method again. Therefore, this offers performance improvements in your scripts because, unless the class is needed – files are not loaded. Therefore, the PHP engine does not have to parse and compile an unnecessary file.

Php Magic Methods – __isset() and __unset()

In previous PHP5 OOPS tutorial on PHP5 Tutorial – Magic Methods – __get() and __set(), we learnt how and when to use these magic methods.
This PHP5 OOPS tutorial will teach you how and when to use the magic methods __isset() and __unset().

These methods are automatically called internally when isset() and unset() is called on undeclared data members. The magic method __isset() method receives an argument – the value of which is the name of the variable that the program wants to test if the variable is set or not.

The magic method __unset() method receives an argument – the value of which is the name of the variable that the program wants to unset.

Look at the example below:

class Customer {
 private $data = array();
 
 public function __set($dt, $vl) {
  $this->data[$dt] = $vl;
 }
 
 public function __get($dt) {
  return $this->data[$dt];
 }
 
 public function __isset($dt) {
  return isset($this->data[$dt]);
 }
 
 public function __unset($dt) {
  return unset($this->data[$dt]);
 }
}
 
$c = new Customer();
$c->name = "Hiren Prajapati";
 
echo isset($c->name)."\n";
echo unset($c->name);
In the above example the script creates a new Customer Object. The program assigns a string value to an undeclared variable i.e. $c->name. The undeclared variable is handled by the magic method __set(). Read this post on PHP5 Magic Methods __set() if you need more understanding how the magic method __set() works.
The program ties to check if the undeclared variable i.e., $c->name has been set or not using the PHP method isset(). Since $c->name is an undeclared variable the PHP5 magic method __isset() is invoked that takes the name of the undeclared variable i.e. “name” and checks if the internal array $data["name"] is set or not.
Similarly, when the program calls unset() on the undeclared variable i.e. $c->name, the PHP5 magic method __unset() is invoked that takes the name of the undeclared variable i.e. “name” and unsets the internal array $data["name"].

__get() and __set() in Php

This article talks about the use of __get() (double underscore – get()) and __set() (double underscore – set()) PHP5 magic methods.
By default PHP is a Loosely typed language and so it is not necessary to declare variables before using them. This also holds true for using class members. Look at an example below.

class Customer {
public $name;
}
$c = new Customer();
$c->name = “Hiren”; // $name is set because its public
$c->email = “email@domain.com”; //assigning email@domain.com to the $email variable.
?>
Ideally in a strict language this would have been an error. But, with PHP this works perfectly well as you can assign values to an undefined variable.
Because of the above limitation, PHP engine provides two magic methods __get() and __set(). __get() is used when value from an undefined variable is to be read and __set() is used when a value is to be assigned to a undefined variable of a class.
__set() allows you to provide functionality to validate data being stored. See example below:
class Customer {
public $name;
private $data = array();
public function __set($dt, $vl) {
$this->data[$dt] = $vl;
}
public function __get($dt) {
return $this->data[$dt];
}
}
$c = new Customer();
$c->name = “Hiren”; // $name is set because its public
$c->email = “email@domain.com”; //assigning email@domain.com to the $email variable.
echo $c->email;
?>
In the above example when email@domain.com is assigned to the undefined variable $email, the magic method __set() is called. To this __set() method the name of the variable is passed into $dt variable of __set() method and the value i.e. email@domain.com is passed to $vl variable of the __set() method.
The next step is to store these values into the $data array so that you could retrieve it later.
The __get() method works in the similar fashion. When you echo $c->email, __get() method is called and the name email is passed in the $dt of the __get() method.
Tip:
It is possible to stop this behavior of PHP to assign values to undefined issues. The solution is that you raise an exception from within __set() method. Look at the code below:
class Customer {
private $name;
public function __set($dt, $vl) {
throw new Exception(“Cannot assign values to undefined variables”,1);
}
}
$c = new Customer();
$c->email = “email@domain.com”; //this will cause an exception to be raised
?>
Related Post:
PHP5 OOPS – Magic Methods __toString()

Magic Methods – __toString() method


PHP5 provides a magic method by the name of __toString() (double underscore followed by toString()) which is useful for debugging purposes.
The __toString() method is automatically called when an object in PHP5 is converted into a string for the purpose of display or concatenation.

Following is the example of the __toString() method:
<?php
class Customer {
private $firstName, $lastName, $email;
public function __construct($firstName, $lastName, $email) {
$this->firstName = $firstName;
$this->lastName = $lastName;
$this->email = $email;
}
public function __toString() {
return “Debug message from Customer Class : First Name = ” . $this->firstName . “, Last Name = ” . $this->lastName . “, Email = ” . $this->email;
}
}
$c = new Customer(“Hiren”,”Prajapati”,”email@domain.com”);
echo “Customer Object is >>” . $c;
?>
Output:
Customer Object is >> Debug message from Customer Class : First Name = Hiren, Last Name = Prajapati, Email = email@domain.com
See how in this example $c Customer Object got converted into a string type when used with the dot (.) concatenation operator. In the background the magic method __toString() is automatically called when such a conversion happens.
Security Tip:
Be careful not to include sensitive data as part of the output as you could compromise security by leaking secure information. Many applications are written to write object states in a log file, therefore you should ensure that sensitive information like Credit Card information, etc is not made available through the magic method __toString()
Subscribe to my newsletter and be informed as a new PHP5 tutorial is posted online: