Showing posts with label PHP5. Show all posts
Showing posts with label PHP5. Show all posts

Friday, March 13, 2015

Using Exception Objects to Handle Errors

Using Exception Objects to Handle Errors

Although functions like trigger_error() and set_error_handler() give us a lot of flexibility with raising and handling errors, they do have limitations. For example, if a piece of code calls a class method and an error occurs in that method, it would be nice if the method could simply tell the calling code about the error, rather than having to raise an error with trigger_error() and go through a central error handler. That way the calling code could take action to correct the problem, making the application more robust.

One simple, common way to achieve this is to get a function or method to return a special error value, such as -1 or false. The calling code can then inspect the return value and, if it equals the error value, it knows there was a problem. However, this can get unwieldy when you start working with deeply nested function or method calls, as the following code shows:

class WarpDrive {
   public function setWarpFactor( $factor ) {
     if ( $factor >=1 && $factor <= 9 ) {
       echo "Warp factor $factor<br />";
       return true;
     } else {
       return false;
     }
   }
 }
 class ChiefEngineer {
   public function doWarp( $factor ) {
     $wd = new WarpDrive;
     return $wd->setWarpFactor( $factor );
   }
 }
 class Captain {
   public function newWarpOrder( $factor ) {
     $ce = new ChiefEngineer;
     return $ce->doWarp( $factor );
   }
 }
 $c = new Captain;
 if ( !$c->newWarpOrder( 10 ) ) echo "She cannot go any faster!<br />";

The WarpDrive::setWarpFactor() function returns true if the function succeeded, and false otherwise (if the warp factor was less than 1 or greater than 9). This return value then needs to be passed through both the ChiefEngineer::doWarp() method and the Captain::newWarpOrder() method to reach the calling code, which can then identify and report on the error. It's not uncommon to find at least this level of nested method calls in complex applications.

Another problem is that simply returning false doesn't tell the calling code much about what went wrong. What's more, when a method has to return an error value, it can't then easily return anything else (because methods and functions can return only one thing at a time).

Fortunately, PHP gives us exceptions, which are a much more elegant way of triggering and handling error conditions. Rather than returning a single error value, our method or function can create a rich Exception object that includes detailed information about the problem, then throw the object up to the calling code to handle, or catch.

Another nice feature of exceptions is that the calling code doesn't have to catch an exception if it doesn't want to; if it ignores it, the exception is re-thrown up the calling chain until it is caught. If no code catches the exception, the script halts with a fatal error and the exception is logged or displayed to the user (depending on our log_errors and display_errors settings). So by using exceptions, any problem can either be handled automatically by another part of the application or, if all else fails, reported to the developer or user. This allows applications to be much more flexible and robust in their handling of error scenarios.

Note: If we don't want uncaught exceptions to raise fatal errors, we can create our own exception handler to deal with the exceptions (much like creating our own error handler).
See http://www.php.net/manual/en/function.set-exception-handler.php for details.

Throwing Exceptions

Here's how to create and throw an exception when an error occurs in our code:

throw new Exception;

We can also pass an optional error message to the Exception object when it's created (this is generally a good idea):

throw new Exception( "Oops, something went wrong" );

If we have a lot of different error messages in our application, it can help to give each exception a numeric error code to distinguish it. To add an error code to our thrown exception, pass it as the second argument when creating the Exception object:

If we don't catch our thrown exception at some other point in our code, eventually it bubbles up to the top level of our script, displaying an error message similar to the following:

PHP Fatal error:  Uncaught exception 'Exception' with message 'Oops, something went wrong' in script.php:4
 Stack trace:
 #0 {main}
   thrown in script.php on line 4

This tells us that an exception occurred that wasn't handled by the script itself, gives us the error message, and informs us that the exception was thrown in the main (top-level) part of the script.

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?"