Building a Modular Exception Class in PHP 5 - Review: Handling MySQL-related exceptions with two subclasses (
Page 2 of 4 )
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.