Sub Classing Exceptions in PHP 5

If you do any serious programming, whether it’s in PHP 5 or some other language, you’ve needed to know how to handle run time errors and other "exceptional" conditions. You can do this by making your program throw generic exceptions. Or you can unlock the potential of PHP 5 and learn how to create custom exceptions, which is the subject of this four-part series.

Introduction

As you may know, many mature object-oriented programming languages, like Java and C++, let developers work with exceptions in a fairly simple fashion. Of course, in these specific cases, this feature has been available for a long time, and it’s been a generous source of inspiration for younger languages, including PHP 5.

While PHP 5′s exception mechanism is pretty solid and can be mastered by inexperienced programmers in a short time, its full potential is rarely exploited when developing web applications. What’s more, if you’re anything like me, then it’s probable that in the past few years you’ve built some scripts that handled all run time errors and “exceptional” conditions by means of abrupt “die()” statements.

Yes, I have to admit that I’m guilty of that mistake too. I’m trying hard not to do it anymore, since I learned how to work with exceptions. Now, speaking more seriously, I’m sure that you already know how to wrap your PHP 5 scripts into neat “try-catch()” blocks, and of course, all of your classes handle certain types of errors by means of clean exceptions. That sounds fine.

However, let me ask you the following question: how far did you go when using PHP 5’s exception mechanism? If your answer is simply: "hey, generic exceptions are all that I need for my web applications," then this series of articles might be useful to you. In it, you’ll learn how to create subclasses from the built-in “Exception” class bundled with PHP 5, in this way providing your programs with the ability to handle customized exceptions.

Now that you’re aware of the goal of this series, it’s time show some action, so jump ahead and start learning how to use inheritance to throw and handle specific exceptions in PHP 5. It’s going to be a fun experience!

{mospagebreak title=Getting started with exceptions with PHP 5}

Before I start teaching you a simple way to create customized exceptions with PHP 5, let me show you an introductory example. In this case, the built-in “Exception” class is used to handle certain errors triggered by a pair of MySQL processing classes. The respective signatures of these PHP 5 classes are coded as follows:


// define ‘MySQL’ class


class MySQL{

private $conId;

private $host;

private $user;

private $password;

private $database;

private $result;

const OPTIONS=4;

public function __construct($options=array()){

if(count($options)!=self::OPTIONS){

throw new Exception(‘Invalid number of connection parameters’);

}

foreach($options as $parameter=>$value){

if(!$value){

throw new Exception(‘Invalid parameter ‘.$parameter);

}

$this->{$parameter}=$value;

}

$this->connectDB();

}

// connect to MySQL

private function connectDB(){

if(!$this->conId=mysql_connect($this->host,$this->user,$this->password)){

throw new Exception(‘Error connecting to the server’);

}

if(!mysql_select_db($this->database,$this->conId)){

throw new Exception(‘Error selecting database’);

}

}

// run query

public function query($query){

if(!$this->result=mysql_query($query,$this->conId)){

throw new Exception(‘Error performing query ‘.$query);

}

return new Result($this,$this->result);

}

}


// define ‘Result’ class

class Result {

private $mysql;

private $result;

public function __construct($mysql,$result){

$this->mysql=$mysql;

$this->result=$result;

}

// fetch row

public function fetchRow(){

if(!$row=mysql_fetch_assoc($this->result)){

return false;

}

return $row;

}

// count rows

public function countRows(){

if(!$rows=mysql_num_rows($this->result)){

throw new Exception(‘Error counting rows’);

}

return $rows;

}

// count affected rows

public function countAffectedRows(){

if(!$rows=mysql_affected_rows($this->mysql->conId)){

throw new Exception(‘Error counting affected rows’);

}

return $rows;

}

// get ID of last-inserted row

public function getInsertID(){

if(!$id=mysql_insert_id($this->mysql->conId)){

throw new Exception(‘Error getting ID’);

}

return $id;

}

// seek row

public function seekRow($row=0){

if(!is_int($row)||$row<0){

throw new Exception(‘Invalid result set offset’);

}

if(!mysql_data_seek($this->result,$row)){

throw new Exception(‘Error seeking data’);

}

}

}


If you’ve ever had the chance to read some of my previous PHP-related articles, published here on the prestigious Developer Shed Network, then it’s highly probable that the two previous MySQL handling classes are pretty familiar to you, since I’ve used them before.

As you can see, the classes are very easy to grasp; they perform some common tasks related to MySQL, such as connecting to the server and selecting a database, performing queries and handling result sets, and so forth.

However, apart from my explanation of how these classes work, I’d like you to pay attention to the way the some of their methods handle certain error conditions. In this specific case, if (for whatever reason) it’s not possible to establish a connection to MySQL, or a specified database can’t be selected, or a query fails to be executed, then a generic exception will be thrown, by using the built-in “Exception” class.

So far, there’s nothing unexpected, right? At this point, I coded a couple of MySQL-related classes that are capable of handling certain application errors by means of a few generic exceptions. Nonetheless, this initial hands-on example would be rather incomplete if I don’t show you how these exceptions can be properly caught within a “try-catch” block.

Therefore, in the section to come I’m going to demonstrate how the native exception mechanism of PHP 5 can be utilized to handle all of the potential errors triggered by the previous MySQL-processing classes.

To see how this process will be achieved, please jump ahead and read the next few lines.

{mospagebreak title=Catching MySQL-related exceptions with the PHP 5 Exception class}

In the previous section I showed you how to utilize the native exception mechanism provided by PHP 5 to handle some errors that might occur when connecting to MySQL, running queries against a selected database, etc. Of course, the simplest way to achieve this was by using a couple of SQL processing classes, whose respective signatures were shown previously.

Nonetheless, since these concepts are pretty simple to grasp, let me go one step further and show you how the aforementioned classes are capable of triggering generic exceptions that can be caught within a typical “try-catch” block.

Having said that, please take a look at the following code sample. It attempts to pull out some records from a sample “USERS” MySQL database table, while this server is turned off:


try{

// connect to MySQL

$db=new MySQL(array(‘host’=>’host’,'user’=>’user’,'password’=>’password’,'database’=>’database’));

// fetch data on some users

$result=$db->query(‘SELECT * FROM users’);

// display data on some users

while($row=$result->fetchRow()){

echo ‘First Name: ‘.$row['firstname'].’ Last Name: ‘.$row['lastname'].’ Email: ‘.$row['email'].’<br />’;

}

 

// turn off MySQL (throws an exception)

 

/* displays the following


Exception message: Error connecting to the server

Source filename of exception: path/to/file/exception_test.php

Source line of exception: 26

*/

}

// catch all generic exceptions here

catch(Exception $e){

echo ‘Exception message: ‘.$e->getMessage().’<br />’;

echo ‘Source filename of exception: ‘.$e->getFile().’<br />’;

echo ‘Source line of exception: ‘.$e->getLine();

exit();

}


As you can see, the above hands-on example demonstrates in a nutshell how to use a simple “try-catch” block to intercept any generic exceptions triggered by the pertinent “MySQL” and “Result” classes defined earlier. The situation is in this case particularly illustrative, since the database server has been purposely turned off, which obviously triggers an exception that’s trapped by the corresponding “cacth()” block.

So far, you have seen how to handle generic exceptions by using the base “Exception” class included with PHP 5. As I mentioned in the beginning of this article, however, it’s also possible to extend the functionality of this native class by creating some subclasses from it, and triggering customized exceptions that can be processed by multiple “try-catch()” blocks.

Sounds interesting, doesn’t it? Therefore, in the following section I’m going to modify the respective signatures of the MySQL handling classes that you learned before to provide them with the capacity for triggering not only generic exceptions, but a couple of customized ones.

To learn how this will be done, please click on the link below and keep reading.

{mospagebreak title=Modifying the MySQL class’s signature to trigger customized exceptions}

As I mentioned earlier, it’s perfectly possible to extend the native “Exception” class provided by PHP 5 to create some customized exceptions, which can be used to handle different error conditions in a separate way.

This exception sub classing process is composed basically of two steps. The first one requires us to provide certain classes with the capacity for triggering customized exceptions, while the second one sees us creating one or more subclasses from the parent “Exception.”

Particularly, in this final section of this tutorial I’m going to show you how to modify the signature of the “MySQL” class that you saw before, so it can launch some custom exceptions to client code. The remaining task, that is, extending the pertinent base “Exception” class, will be discussed in the next tutorial.

All right, now that I have clarified that point, please take a look at the modified definition of the aforementioned “MySQL” class below. It is now capable of triggering a customized “MySQLException.”


// define ‘MySQL’ class

class MySQL{

private $conId;

private $host;

private $user;

private $password;

private $database;

private $result;

const OPTIONS=4;

public function __construct($options=array()){

if(count($options)!=self::OPTIONS){

throw new MySQLException(‘Invalid number of connection parameters’);

}

foreach($options as $parameter=>$value){

if(!$value){

throw new MySQLException(‘Invalid parameter ‘.$parameter);

}

$this->{$parameter}=$value;

}

$this->connectDB();

}

// connect to MySQL

private function connectDB(){

if(!$this->conId=mysql_connect($this->host,$this->user,$this->password)){

throw new MySQLException(‘Error connecting to the server’);

}

if(!mysql_select_db($this->database,$this->conId)){

throw new MySQLException(‘Error selecting database’);

}

}

// run query

public function query($query){

if(!$this->result=mysql_query($query,$this->conId)){

throw new MySQLException(‘Error performing query ‘.$query);

}

return new Result($this,$this->result);

}

}


// define ‘Result’ class

class Result {

private $mysql;

private $result;

public function __construct($mysql,$result){

$this->mysql=$mysql;

$this->result=$result;

}

// fetch row

public function fetchRow(){

if(!$row=mysql_fetch_assoc($this->result)){

return false;

}

return $row;

}

// count rows

public function countRows(){

if(!$rows=mysql_num_rows($this->result)){

throw new Exception(‘Error counting rows’);

}

return $rows;

}

// count affected rows

public function countAffectedRows(){

if(!$rows=mysql_affected_rows($this->mysql->conId)){

throw new Exception(‘Error counting affected rows’);

}

return $rows;

}

// get ID of last-inserted row

public function getInsertID(){

if(!$id=mysql_insert_id($this->mysql->conId)){

throw new Exception(‘Error getting ID’);

}

return $id;

 }

// seek row

public function seekRow($row=0){

if(!is_int($row)||$row<0){

throw new Exception(‘Invalid result set offset’);

}

if(!mysql_data_seek($this->result,$row)){

throw new Exception(‘Error seeking data’);

}

}

}


That wasn’t hard to grasp, right? As you can see, the previous “MySQL” class now has the ability to throw a specific “MySQLException,” which certainly helps to improve the way that certain errors conditions are handled. In this case, one “try-catch” block could be used for processing only generic exceptions, and a different one would handle specifically those of type “MySQLException.”

But I’m getting ahead of myself, since this process will be discussed in depth in the next part of the series. In the meantime, feel free to use all of the sample classes listed earlier, and try to develop your own customized exceptions.

Final thoughts

In this first installment of the series, you hopefully learned the basic concepts that surround the implementation of generic exceptions with PHP 5. Besides, you saw how to build, in a few simple steps, a sample class that can throw customized exceptions.

Logically, it’s necessary to demonstrate not only how to create these specific exceptions, but how they can be intercepted within a particular “try-catch” block. However, these useful topics will be discussed in detail in the next article, so you don’t have any excuses to miss it!

Google+ Comments

Google+ Comments