Building a Modular Exception Class in PHP 5

Welcome to the final part of the four-part series “Subclassing exceptions in PHP 5.” By means of a hands-on approach, this series walks you through using inheritance to build finely-tuned exception subclasses. These subclasses can be used to handle, via multiple “try-catch” blocks, several failures that might occur during the execution of a given web application.

Introduction

Working with generic exceptions in PHP 5 is a fairly easy process that can be tackled with minor hassles. This is true even if you’re starting to get your hands dirty developing web applications that utilize them to handle a certain number of critical conditions.

Things can be a bit more complicated, however, when it’s necessary to work with customized exceptions. This specific situation requires that you derive some exception subclasses from the built-in one provided by PHP 5, and that you use multiple “try-catch” blocks within the same application.

Of course, if you’re an experienced PHP 5 developer with a solid background in working with exceptions, then you’ve probably mastered them already. However, if you’re just getting started with employing them in your own applications, and want to know how to build a customized exception mechanism in a few simple steps, then this article series might be what you need.

Now, getting straight to the point, you’ll possibly recall that in the last article I demonstrated how to build a customized exception system in PHP 5, which had the capacity to catch not only generic exceptions, but a couple of additional ones, triggered when working specifically with MySQL.

In short, the system in question was smart enough to handle two well-differentiated kinds of exceptions: first, the ones triggered when connecting to the MySQL server, selecting a particular database, and running SQL queries; and second, exceptions that could be thrown when manipulating result sets.

Nonetheless, handling the aforementioned types of exceptions demanded the use of two different subclasses, which in a case like this is really unnecessary. Therefore, in this concluding article of the series, I’m going to implement a similar MySQL exception mechanism, but this time by using only one subclass.

So, are you ready to tackle the final chapter of this educational journey? Let’s begin now!

{mospagebreak title=Review: Handling MySQL-related exceptions with two subclasses}

Since my intention here is to demonstrate how to utilize only one subclass to handle all of the exceptions triggered when working with MySQL, first I’d like to list the complete source code corresponding to the practical example developed in the preceding tutorial of the series. As you’ll surely recall, that example used two different subclasses to handle all of these MySQL-related exceptions.

Having said that, here’s how the example in question looked originally:


// 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 ResultException(‘Error counting rows’);

}

return $rows;

}

// count affected rows

public function countAffectedRows(){

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

throw new ResultException(‘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 ResultException(‘Error getting ID’);

}

return $id;

}

// seek row

public function seekRow($row=0){

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

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

}

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

throw new ResultException(‘Error seeking data’);

}

}

}


// extend the built-in exception class to throw MySQL-related exceptions

class MySQLException extends Exception{

public function __construct($message,$code=0){

// call parent of Exception class

parent::__construct($message,$code);

}

public function showExceptionInfo(){

return ‘Catching MySQL exceptions…<br />Exception message: ‘.$this->getMessage().'<br />Source filename of exception: ‘.$this->getFile().'<br />Source line of exception: ‘.$this->getLine();

}

}


// extend the built-in exception class to throw MySQL data sets-related exceptions

class ResultException extends Exception{

public function __construct($message,$code=0){

// call parent of Exception class

parent::__construct($message,$code);

}

public function showResultExceptionInfo(){

return ‘Catching Result exceptions…<br />Exception message: ‘.$this->getMessage().'<br />Source filename of exception: ‘.$this->getFile().'<br />Source line of exception: ‘.$this->getLine();

}

}


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 />';

}

// throw a Result Exception

echo $result->getInsertID();

 

/* displays the following

Catching Result exceptions…

Exception message: Error getting ID

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

Source line of exception: 93

*/

}

// catch MySQL exceptions here

catch(MySQLException $e){

echo $e->showMySQLExceptionInfo();

exit();

}

// catch Result exceptions here

catch(ResultException $e){

echo $e->showResultExceptionInfo();

exit();

}

// catch default exceptions here

catch(Exception $e){

echo ‘Catching default exceptions…<br />';

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 from the above example, I first defined a couple of MySQL handling classes for connecting to the server and selecting specific databases, running several SQL queries and processing results sets.

However, apart from understanding how these classes do their respective things, undoubtedly the most important point to stress here is the way that the different MySQL-related exceptions are handled at runtime.

In this case, I utilized two specific subclasses, called “MySQLException” and “ResultException” respectively. These subclasses separately handle the errors that might be triggered when connecting to the server and running SQL statements, and the failures that might happen when processing result sets. Pretty easy to understand, right?

Now that you hopefully recalled how to use the two distinct subclasses to process all of the exceptions triggered by a couple of MySQL handling classes, it’s time to see how to perform this same task by means of only one exception class.

Keeping this idea in mind, in the next section I’m going show you how to merge the two exception subclasses that you saw before into a single one, which will implement the logic necessary to handle all of the MySQL-related exceptions in a more efficient manner.

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

{mospagebreak title=Centralizing the processing of MySQL-related exceptions}

The next thing I’m going to teach you in this tutorial will be simply how to merge the pair of exception subclasses that I listed earlier into a single subclass that will be responsible for processing all of the exceptions triggered when working with MySQL.

Please take a look at the signature of this brand new exception subclass, included below:


// define MySQLException subclass


class MySQLException extends Exception{

private $exceptionType;

public function __construct($message,$code=0){

// call parent of Exception class

parent::__construct($message,$code);

if($code==1){

$this->exceptionType=’MySQLException';

}

elseif($code==2){

$this->exceptionType=’ResultException';

}

else{

$this->exceptionType=’Unknown Exception';

}

}

public function showMySQLExceptionInfo(){

return ‘Catching ‘.$this->exceptionType.’…<br />Exception message: ‘.$this->getMessage().'<br />Source filename of exception: ‘.$this->getFile().'<br />Source line of exception: ‘.$this->getLine();

}

}

 

As you can see, the above “MySQLException” subclass now implements the required logic to handle two different types of MySQL-related exceptions. Obviously, this process is performed according to the value assigned to its $code input argument and is easy to follow — if this parameter has a value of 1, it means that an exception of type “MySQLException” has been triggered by a certain class, while a value of 2 assigned to the parameter in question implies that an exception of type “ResultException” has been launched.

In both cases, the exceptions are processed in a fairly trivial manner, but I did this purposely, so you can grasp more quickly how to process all of the MySQL-related exceptions via a unique subclass.

Well, at this point, you hopefully understand how the previous “MySQLException” subclass does its business. Therefore, it’s time to create a complete hands-on example where the subclass can be utilized in a handy fashion. The last section of this tutorial will be focused entirely on developing the aforementioned example, in this way concluding this quick overview on subclassing exception with PHP 5.

Go ahead and read the next few lines. We’re almost finished!

{mospagebreak title=Handling MySQL-related exceptions through one single subclass}

As I anticipated in the previous section, below I developed an illustrative example to show you how the exception subclass coded previously can be utilized in conjunction with the pair of MySQL processing classes that I listed in the beginning of this tutorial.

This being said, please pay close attention to the example in question, which looks like this:


// define MySQLException subclass


class MySQLException extends Exception{

private $exceptionType;

public function __construct($message,$code=0){

// call parent of Exception class

parent::__construct($message,$code);

if($code==1){

$this->exceptionType=’MySQLException';

}

elseif($code==2){

$this->exceptionType=’ResultException';

}

else{

$this->exceptionType=’Unknown Exception';

}

}

public function showMySQLExceptionInfo(){

return ‘Catching ‘.$this->exceptionType.’…<br />Exception message: ‘.$this->getMessage().'<br />Source filename of exception: ‘.$this->getFile().'<br />Source line of exception: ‘.$this->getLine();

}

}


// define ‘MySQL’ class

class MySQL{

public $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’,1);

}

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

if(!$value){

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

}

$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’,1);

}

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

throw new MySQLException(‘Error selecting database’,1);

}

}

// run query

public function query($query){

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

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

}

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 MySQLException(‘Error counting rows’,2);

}

return $rows;

}

// count affected rows

public function countAffectedRows(){

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

throw new MySQLException(‘Error counting affected rows’,2);

}

return $rows;

}

// get ID of last-inserted row

public function getInsertID(){

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

throw new MySQLException(‘Error getting ID’,2);

}

return $id;

}

// seek row

public function seekRow($row=0){

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

throw new MySQLException(‘Invalid result set offset’,2);

}

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

throw new MySQLException(‘Error seeking data’,2);

}

}

}


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 and throw a MySQL exception

 

/* displays the following

Catching MySQLException…

Exception message: Error connecting to the server

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

Source line of exception: 47

*/

 

// now throw a Result Exception

echo $result->getInsertID();

 

/* displays the following

Catching ResultException…

Exception message: Error getting ID

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

Source line of exception: 93

*/

}

// catch MySQL exceptions here

catch(MySQLException $e){

echo $e->showMySQLExceptionInfo();

exit();

}

// catch default exceptions here

catch(Exception $e){

echo ‘Catching default exceptions…<br />';

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 previous example shows in a nutshell how to use the customized “MySQLException” subclass to handle all of the exceptions triggered by the respective “MySQL” and “Result” classes.

Speaking more specifically, I’ve recreated a couple of situations where the two different types of MySQL-related exceptions are intercepted by the same “catch” block and processed later by the corresponding “MySQLException” subclass. That was pretty easy to grasp, right?

And with this final hand-on example, I conclude this introduction to using exception subclasses with PHP 5. Hopefully, all of the code samples developed in the different tutorials of this series will help you learn the basic concepts of this subject with minor effort.

Final thoughts

It’s hard to believe, but we’ve come to the end of this series. As you saw previously, creating a few exception subclasses to handle more specifically the errors that may arise during the execution of a PHP 5 application is a no-brainer process that only requires patience and practice. So, if you’re armed with these two elements, success is already guaranteed!

See you in the next PHP development tutorial!

[gp-comments width="770" linklove="off" ]

antalya escort bayan antalya escort bayan Antalya escort diyarbakir escort