Friday, February 27, 2015

Storing Objects as Strings

Objects that we create in PHP are stored as binary data in memory. Although we can pass objects around using PHP variables, functions, and methods, sometimes its useful to be able to pass objects to other applications, or via fields in Web forms, for example.

PHP provides two functions to help us with this:

    serialize() converts an object — properties, methods, and all — into a string of text
    unserialize() takes a string created by serialize() and turns it back into a usable object

The following example shows these two functions in action:

 class Person
 {
   public $age;
 }
 
 $harry = new Person();
 $harry->age = 28;
 $harryString = serialize( $harry );
 echo "Harry is now serialized in the following string: '$harryString'<br />";
 echo "Converting '$harryString' back to an object...<br />";
 $obj = unserialize( $harryString );
 echo "Harry's age is: $obj->age<br />";


This code creates a simple Person class with one property, $age. It then creates a new Person object, $harry, and sets its $age property to 28. It calls serialize() to convert the object to a string, which it displays. Finally, it converts the string back into a new object, $obj, then displays its $obj->age property (28). Here's the result of running the script:

 Harry is now serialized in the following string: 'O:6:"Person":1:{s:3:"age";i:28;}'
 Converting 'O:6:"Person":1:{s:3:"age";i:28;}' back to an object...Harry's age is: 28

We can actually use serialize() and unserialize() on any PHP value, not just objects. However, it's especially useful with objects and arrays, because these structures can be quite complex and it's not easy to convert them to strings in any other way.

What's more, when we serialize an object, PHP attempts to call a method with the name __sleep() inside the object. We can use this method to do anything that's required before the object is serialized. Similarly, We can create a __wakeup() method that is called when the object is unserialized.

__sleep() is useful for cleaning up an object prior to serializing it, in the same way that we might clean up in a destructor method. For example, we might need to close database handles, files, and so on. In addition, __sleep() has another trick up its sleeve. PHP expects our __sleep() method to return an array of names of properties to preserve in the serialized string. We can use this fact to limit the number of properties stored in the string — very useful if our object contains a lot of properties that we don't need to store.

Here's an example:

class User {
   public $username;
   public $password;
   public $loginsToday;
 
   public function __sleep() {
     // (Clean up; close database handles, etc)
     return array( "username", "password" );
   }
 }
 
 $user = new User;
 $user->username = "harry";
 $user->password = "monkey";
 $user->loginsToday = 3;
 echo "The original user object:<br />";
 print_r( $user );
 echo "<br /><br />";
 echo "Serializing the object...<br /><br />";
 $userString = serialize( $user );
 echo "The user is now serialized in the following string:<br />";
 echo "$userString<br /><br />";
 echo "Converting the string back to an object...<br /><br />";
 $obj = unserialize( $userString );
 echo "The unserialized object:<br />";
 print_r( $obj );
 echo "<br />";


This code outputs the following:

 The original user object:
 User Object ( [username] => harry [password] => monkey [loginsToday] => 3 )
 
 Serializing the object...
 
 The user is now serialized in the following string:
 O:4:"User":2:{s:8:"username";s:5:"harry";s:8:"password";s:6:"monkey";}
 
 Converting the string back to an object...
 
 The unserialized object:
 User Object ( [username] => harry [password] => monkey [loginsToday] => )


In this example, we don't care about preserving the number of times the user has logged in today, so the __sleep() method only returns the "username" and "password" property names. Notice that the serialized string doesn't contain the $loginsToday property. Furthermore, when the object is restored from the string, the $loginsToday property is empty.

In a real-world situation, We make sure that we don't transmit sensitive information such as usernames as passwords as plain text strings if there's a chance that the data might be intercepted or read by untrusted third parties.

If we do need to preserve all our object's properties, we can use the built-in get_object_vars() function to get an associative array of all the properties in the object, then we use the array_keys() function to get just the property names as an array, which we can then return from our __sleep() method:

class User {
   public $username;
   public $password;
   public $loginsToday;
 
   public function __sleep() {
     // (Clean up; close database handles, etc)

     return array_keys( get_object_vars( $this ) );

   }
 }


Finally, here's an example that shows the __wakeup() method in action:

 class User
 {
   public function __wakeup()
 {
     echo "Yawn... what's for breakfast?<br />";
   }
 }
 
 $user = new User;
 $userString = serialize( $user );
 $obj = unserialize( $userString );  // Displays "Yawn... what's for breakfast?"

No comments:

Post a Comment