Article

PHP5: Coming Soon to a Webserver Near You

Page: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Next

One final example of exception handling shows you how try/catch blocks can be nested and errors can be re-thrown, allowing them to be handled in the correct context:

             
<?php              
class IOError extends Exception {              
   function __construct($message) {              
       parent::Exception($message);              
   }              
}              
             
class SQLError extends Exception {              
   function __construct($message) {              
       parent::Exception($message);              
   }              
}              
             
// Class for SQLite connections              
class SQLite {              
   private $db;              
   function __construct($dbPath) {              
       if ( !$this->db = @sqlite_open($dbPath, 0666, $error) )              
           throw new IOError($error);              
   }              
   function query($sql) {              
       if ( !$result = @sqlite_query($sql, $this->db) )              
           throw new SQLError(sqlite_last_error($this->db).              
               ': '.sqlite_error_string(sqlite_last_error($this->db)));              
       return new SQLiteResult($this,$result);              
   }              
   function __destruct() {              
       sqlite_close($this->db);              
   }              
}              
             
// Result class returned from the query method above              
class SQLiteResult {              
   private $db;              
   private $result;              
   function __construct($db,$result) {              
       $this->db = $db;              
       $this->result = $result;              
   }              
   function fetch() {              
       return sqlite_fetch_array($this->result, SQLITE_ASSOC);              
   }              
}              
             
try {              
   $db = new SQLite('/www/sitepoint/php5/sitepoint.sqlite');              
   try {              
       # Simulate bad connection              
       // throw new IOError('Lost connection to database');              
       $result = $db->query('SELECT * FROM users');              
       while ( $row = $result->fetch() ) {              
           echo ( '<pre>' );              
           print_r($row);              
           echo ( '</pre>' );              
       }              
   } catch ( SQLError $e ) {              
       echo ( 'There is something wrong with the query: '.$e->getMessage() );              
   } catch ( Exception $e ) {              
       // Re-throw exception              
       throw ($e);              
   }              
} catch ( IOError $e ) {              
   echo ( 'Major problems!' );              
   error_log ( date('YmdHis').": ".$e->getMessage()."\n",3,'errors.txt' );              
} catch ( Exception $e ) {              
   echo ( 'Hmmm - no idea how to deal with this: '.$e->getMessage() );              
}              
?>              

Script: exception6.php

I've created my own Exception subclasses, IOError and SQLError above then created two classes for connecting to SQLite and fetching the results of a query.

Notice that I've got a try/catch block nested within the first try{} block. The nested block only "knows" about SQLError exceptions - any other errors it encounters it re-throws with;

             
   } catch ( Exception $e ) {              
       // Re-throw exception              
       throw ($e);              
   }              

...this passes the error to the "parent" try/catch block to see if it knows what to do.

A possible scenario that might arise with an application using a database is this. Imagine that at the point at which the connection to the database was established, everything was OK, but if in the brief interval between connecting and executing a query, somehow the connection was lost. In this situation, an error may occur that the code performing the query knows nothing about. You can simulate this by un-commenting this line:

             
       // throw new IOError('Lost connection to database');              

The IOError is not understood within the current context, so it’s re-thrown.

Note that in the above example it's not strictly necessary to re-throw the exception, as PHP will automatically pass the un-caught exceptions up to the parent block. However, there may be situations where you want the same exception to be caught more than once, perhaps first to display a message to the user, then later to update a "globally handled" error log.

Note for Java developers expecting a finally{} block, PHP5 (at least for the foreseeable future) won't provide one. Arguably, you can get by most of the time without it, as I’ve done in this example:

             
<?php              
echo ( 'Start execution<br />' );              
try {              
   echo ( 'In try block<br />' );              
   throw new Exception('Some error');              
} catch ( Exception $e ) {              
   echo ( 'In catch block<br />' );              
}              
echo ( 'Finally execution continues...<br />' );              
?>              

Script: exception7.php

That said, if you have nested blocks and you're re-throwing errors, or have un-caught errors, you'll experience problems.

PHP5's new exception mechanism is an important step forward and will be a big relief to many, as it introduces a standard approach to handling errors that does much to help third party class libraries fit together nicely. It will also (hopefully) encourage PHP developers to write more reliable PHP applications, addressing the argument that open source programmers stink at error handling

If you liked this article, share the love:
Print-Friendly Version Suggest an Article

Sponsored Links