Displaying Meaningful Error Messages when Auto Loading Classes in PHP 5

If you’re a PHP developer who wants to learn how to load automatically into client code all of the source classes required by your object-oriented applications, keep reading. In this series of articles, you’ll learn how to implement the magic “__autoload()” function that comes integrated with PHP 5. With this function, you’ll be able to build scripts that can include automatically all of the classes needed by a given application, without using multiple “require()/require_once()” PHP statements.

Introduction

All right, now that I introduced you to the primary goal of this series of educational tutorials, I think it’s time to recapitulate the themes covered in the preceding article. We’ll refresh some key concepts concerning the correct use of the  “__autoload()” magic function in the context of a determined object-based application.

As you’ll possibly remember, in that particular article I showed you how to improve the signature of the helpful “__autoload()” PHP 5 function to provide it with the capacity to throw an exception when a specific source class can’t be included into client code for whatever reason.

In addition to adding this important feature to the function, I demonstrated how to introduce some “tweaks” into the signature of this function, with the objective of triggering exceptions that could be correctly intercepted within a conventional “try-catch()” block. In this way we would implement a powerful mechanism for loading automatically all of the classes required by a certain PHP 5-driven application, which also takes advantage of the built-in exception system offered natively by PHP 5. As you can see, in this case the best of both worlds is integrated seamlessly in the same magic function.

Of course, if you reread the previous sentence, you might think that it simply isn’t possible to introduce more modifications to the “__autoload()” function anymore, which would mean that the topic wouldn’t bear any further analysis. Not so fast! Actually, it’s perfectly feasible to improve the way that the “__autoload()” function works even more. In its current incarnation it only is capable of displaying exception messages that aren’t very descriptive, such as “Class not found” or something similar.

Indeed, it’s necessary to modify the logic of this function so it can throw exceptions that can be much more explicit and indicative of what’s going wrong when a particular source class can’t be included properly into client code. Thus, in this last article of the series I’m going to introduce some additional modifications to the current signature of the “__autoload()” function to make it throw exceptions that display more useful error messages.

As you may guess, to learn how these improvements will be achieved, you’ll have to start reading this tutorial, so don’t waste more time; begin now!

{mospagebreak title=Establishing a Reference Point}

Establishing a reference point: listing the complete source files of the previous database-driven application

Before I show you how to improve the current signature of the “__autoload()” magic function, I want to list the definitions corresponding to all the source files that make up the sample database-driven application built in the previous article of the series. This will help you recall how it behaves when one of its classes can’t be included into client code.

As you’ll probably remember, the primary goal of this basic application was fetching some rows from a “Users” MySQL database table, which were displayed on the browser later on. Naturally, the reason for coding this small program was simply to demonstrate the different behaviors shown by the “__autoload()” function when, for any reason, one of the pertinent source classes can’t be loaded as expected.

That being said, here are the complete definitions of the files that comprise this primitive database-driven application, so take some time and have a close look at them, please:

(definition of mysql.php file)

class MySQL{

private $host;

private $user;

private $password;

private $database;

private $connId;

// constructor

function __construct($options=array()){

if(!is_array($options)){

throw new Exception(‘Connection options must be an array’);

}

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

if(empty($option)){

throw new Exception(‘Connection parameter cannot be empty’);

}

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

}

$this->connectDb();

}

// private ‘connectDb()’ method

private function connectDb(){

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

throw new Exception(‘Error connecting to MySQL’);

}

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

throw new Exception(‘Error selecting database’);

}

}

// public ‘query()’ method

public function query($sql){

if(!$result=mysql_query($sql)){

throw new Exception(‘Error running query ‘.$sql.’ ‘.mysql_error());

}

return new Result($this,$result);

}

}



(definition of result.php file)


class Result{

private $mysql;

private $result;

// constructor

public function __construct($mysql,$result){

$this->mysql=$mysql;

$this->result=$result;

}

// public ‘fetch()’ method

public function fetch(){

return mysql_fetch_array($this->result,MYSQL_ASSOC);

}

// public ‘count()’ method

public function count(){

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

throw new Exception(‘Error counting rows’);

}

return $rows;

}

// public ‘get_insertId()’ method

public function getInsertId(){

if(!$insId=mysql_insert_id($this->mysql->connId)){

throw new Exception(‘Error getting insert ID’);

}

return $insId;

}

// public ‘seek()’ method

public function seek($row){

if(!int($row)&&$row<0){

throw new Exception(‘Invalid row parameter’);

}

if(!$row=mysql_data_seek($this->mysql->connId,$row)){

throw new Exception(‘Error seeking row’);

}

return $row;

}

// public ‘getAffectedRows()’ method

public function getAffectedRows(){

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

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

}

return $rows;

}

}



(definition of autoload_example.php file)


try{

function __autoload($className){

if(!file_exists($className.’.php’)){

return eval("class {$className}{public function __construct(){throw new
Exception(‘Class not found!’);}}");

}

require_once $className.’.php';

}

// connect to MySQL

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

// fetch users from database table

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

// display user data

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

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

}

}

catch (Exception $e){

echo $e->getMessage();

exit();

}


As illustrated previously, this basic database-driven application is composed of three source files. The first two are responsible for containing the “MySQL” and “Result” classes required to work correctly. The last one, which I called “autoload_example.php,” is tasked with retrieving user data from the pertinent database table. In addition, this file implements the “__autoload()” magic function to automate the process for loading the corresponding source classes.

But undoubtedly, the most important thing to stress here is how the function works. When one of the source classes can’t be included into client code, it throws a “Class not found” exception, which is happily intercepted by the pertinent “try-catch()” block.

So far, so good. At this stage the “__autoload()” function does a decent job of loading all the source classes required by this sample application. Nonetheless, I have to admit that all the exceptions thrown by this function aren’t very descriptive. Indeed, a laconic “Class not found” error message wouldn’t give you a clear clue of which class failed to load, right?

Therefore, since this limitation exposed by the “__autoload()” function can be addressed with minor efforts, in the next section I’m going to show you how to incorporate some modifications to the function in question. These changes will enable the function to trigger indicative error messages when something goes wrong.

To see how these changes will be incorporated into the current signature of this handy function, please jump forward and read the next few lines. I’ll be there, waiting for you.

{mospagebreak title=Boosting the Existing Capacity}

Boosting up the existing capacity of the “__autoload()” magic function: displaying descriptive error messages

In accordance with the concepts I explained in the previous section, it’s perfectly possible (and desirable, actually) to modify the current signature of the “__autoload()” magic function so that, whenever a given source class can’t be included into client code, it throws the corresponding exception accompanied by a meaningful error message.

Given that, to implement this functionality, I’m going to make a slight change to the signature of the pertinent function, so now it looks like this:


function __autoload($className){

if(!file_exists($className.’.php’)){

return eval("class {$className}{public function __construct(){throw new
Exception(‘Class {$className} not found!’);}}");

}

require_once $className.’.php';

}


Not too difficult to grasp, right? As you can see, now all of the exceptions triggered by the above “__autoload()” function will indicate which source class wasn’t loaded at runtime, since the name of the troubling class is simply passed to the constructor of the built-in Exception class.

To illustrate even more clearly how the “__autoload()” function works after introducing the previous modification, below I included an example that displays a “MySQL class not found” error message, when this class can’t be automatically loaded.

The corresponding code sample is as follows:


try{

function __autoload($className){

if(!file_exists($className.’.php’)){

return eval("class {$className}{public function __construct(){throw new
Exception(‘Class {$className} not found!’);}}");

}

require_once $className.’.php';

}

// connect to MySQL

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

// fetch users from database table

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

// display user data

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

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

}

}

catch(Exception $e) {

echo $e->getMessage();

}


As I said before, if you test the above example on your own computer system and purposely remove the file that contains the pertinent “MySQL” class, then the “__autoload()” function will trigger an exception with the following error message: “Class MySQL not found!”

Indeed, this message is much more meaningful than a raw “Class not found” text, because it gives you a clear idea of which source class couldn’t be correctly loaded by the “__autoload()” function during the execution of a given application.

Okay, at this stage you hopefully learned how to tweak the signature of this handy function to display more descriptive error messages when something goes wrong. However, there are many other approaches that can be implemented to get the same results that you saw earlier.

Nevertheless, in this article I’m going to show you only one more of these additional approaches, which as you’ll see in a moment, will be based on working with the so-called exceptions sub classing.

Do you want to see how this technique will be developed? Click on the link below and keep reading.

{mospagebreak title=Developing a Final Example}

Developing a final example: displaying meaningful error messages with exception subclasses

As you possibly know, there are other techniques that can be implemented in conjunction with the “__autoload()” function, to display indicative error messages when a particular source class can’t be loaded by the function in question.

In this case, I’m going to show you how to create custom “autoloading” exceptions, which obviously will be thrown by the “__autoload()” function. Basically, this procedure is based upon deriving a subclass from the base Exception, which will be used when applicable by the mentioned function.

If this concept sounds rather confusing to you, the following example should dissipate any possible doubts, so have a close look at it, please:


try{

// define custom ‘AutoloadException’ class (extends built-in Exception class)


class AutoloadException extends Exception{}


function __autoload($className){

if(!file_exists($className.’.php’)){

return eval("class {$className}{public function __construct(){throw new
AutoloadException(‘Class {$className} not found!’);}}");

}

require_once $className.’.php';

}


// connect to MySQL

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

// fetch users from database table

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

// display user data

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

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

}

}

// catch Autoload Exceptions

catch(AutoloadException $e) {

echo $e->getMessage();

}

// catch generic exceptions

catch(Exception $e) {

echo $e->getMessage();

}


As you can see, the above example first creates a new “AutoloadException” class from the “Exception” parent, and then implements the pertinent “__autoload()” function in such a way that it will trigger this specific type of exception when a source class fails to be included into client code.

In addition, you should notice the existence of two different “catch()” statements. The first one is responsible for intercepting only “auto load” exceptions, and the second one is tasked with catching only regulars ones.

And with this final example, we have finished exploring the capacities of the “__autoload()” magic function. Logically, from this point onward, it’s up to you improve your existing skills for using this handy function in conjunction with your own object-based PHP 5 applications.

Final thoughts

It’s hard to believe, but we’ve come to the end of this series. As you saw in the different parts of this tutorial, the “__autoload()” magic function that comes bundled with PHP 5 can be really useful for including automatically all of the source classes required by a certain object-oriented application, without our having to be concerned as to how this task is performed behind scenes by the PHP 5 interpreter.

Also, I provided you with several examples of how to incorporate into this function the capacity for throwing exceptions that can be easily caught by a conventional “try-catch()” block. So with all this distilled material in your hands, you can start right now using this handy function within your own PHP 5-driven application.

See you in the next PHP development tutorial!

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

antalya escort bayan antalya escort bayan Antalya escort diyarbakir escort