Multiple return values to indicate success/failure. - php

I'm kind of interested in getting some feedback about this technique I picked up from somewhere.
I use this when a function can either succeed or fail, but you'd like to get more information about why it failed. A standard way to do this same thing would be with exception handling, but I often find it a bit over the top for this sort of thing, plus PHP4 does not offer this.
Basically the technique involves returning true for success, and something which equates to false for failure. Here's an example to show what I mean:
define ('DUPLICATE_USERNAME', false);
define ('DATABASE_ERROR', 0);
define ('INSUFFICIENT_DETAILS', 0.0);
define ('OK', true);
function createUser($username) {
// create the user and return the appropriate constant from the above
}
The beauty of this is that in your calling code, if you don't care WHY the user creation failed, you can write simple and readable code:
if (createUser('fred')) {
// yay, it worked!
} else {
// aww, it didn't work.
}
If you particularly want to check why it didn't work (for logging, display to the user, or do whatever), use identity comparison with ===
$status = createUser('fred');
if ($status) {
// yay, it worked!
} else if ($status === DUPLICATE_USERNAME) {
// tell the user about it and get them to try again.
} else {
// aww, it didn't work. log it and show a generic error message? whatever.
}
The way I see it, the benefits of this are that it is a normal expectation that a successful execution of a function like that would return true, and failure return false.
The downside is that you can only have 7 "error" return values: false, 0, 0.0, "0", null, "", and (object) null. If you forget to use identity checking you could get your program flow all wrong. Someone else has told me that using constants like an enum where they all equate to false is "ick".
So, to restate the question: how acceptable is a practise like this? Would you recommend a different way to achieve the same thing?

I agree with the others who have stated that this is a little on the WTFy side. If it's clearly documented functionality, then it's less of an issue, but I think it'd be safer to take an alternate route of returning 0 for success and integers for error codes. If you don't like that idea or the idea of a global last error variable, consider redefining your function as:
function createUser($username, &$error)
Then you can use:
if (createUser('fred', $error)) {
echo 'success';
}
else {
echo $error;
}
Inside createUser, just populate $error with any error you encounter and it'll be accessible outside of the function scope due to the reference.

As long as it's documented and contracted, and not too WTFy, then there shouldn't be a problem.
Then again, I would recommend using exceptions for something like this. It makes more sense. If you can use PHP5, then that would be the way to go. Otherwise you don't have much choice.

A more common approach I have seen when exceptions aren't available is to store the error type in a 'last_error' variable somewhere and then when a failure happens (ie it returns false) look up the error.
Another approach is to use the venerable unix tool approach numbered error codes - return 0 for success and any integer (that maps to some error) for the various error conditions.
Most of these suffer in comparison to exceptions when I've seen them used however.
Just to respond to Andrew's comment -
I agree that the last_error should not be a global and perhaps the 'somewhere' in my answer was a little vague - other people have suggested better places already so I won't bother to repeat them

how acceptable is a practice like this?
I'd say it's unacceptable.
Requires the === operator, which is very dangerous. If the user used ==, it leads to a very hard to find bug.
Using "0" and "" to denote false may change in future PHP versions. Plus in a lot of other languages "0" and "" does not evaluate to false which leads to great confusion
Using getLastError() type of global function is probably the best practice in PHP because it ties in well with the language, since PHP is still mostly a procedural langauge. I think another problem with the approach you just gave is that very few other systems work like that. The programmer has to learn this way of error checking which is the source of errors. It's best to make things work like how most people expect.
if ( makeClient() )
{ // happy scenario goes here }
else
{
// error handling all goes inside this block
switch ( getMakeClientError() )
{ case: // .. }
}

Often you will return 0 to indicate success, and 1, 2, 3, etc. to indicate different failures. Your way of doing it is kind of hackish, because you can only have so many errors, and this kind of coding will bite you sooner or later.
I like defining a struct/object that includes a Boolean to indicate success, and an error message or other value indicate what kind of error occurred. You can also include other fields to indicate what kind of action was executed.
This makes logging very easy, since you can then just pass the status-struct into the logger, and it will then insert the appropriate log entry.

Reinventing the wheel here. Using squares.
OK, you don't have exceptions in PHP 4. Welcome in the year 1982, take a look at C.
You can have error codes. Consider negative values, they seem more intuitive, so you would just have to check if (createUser() > 0).
You can have an error log if you want, with error messages (or just arbitrary error codes) pushed onto an array, dealt with elegance afterwards.
But PHP is a loosely typed language for a reason, and throwing error codes that have different types but evaluate to the same "false" is something that shouldn't be done.
What happens when you run out of built-in types?
What happens when you get a new coder and have to explain how this thing works? Say, in 6 months, you won't remember.
Is PHP === operator fast enough to get through it? Is it faster than error codes? or any other method?
Just drop it.

When exceptions aren't available, I'd use the PEAR model and provide isError() functionality in all your classes.

Ick.
In Unix pre-exception this is done with errno. You return 0 for success or -1 for failure, then you have a value you can retrieve with an integer error code to get the actual error. This works in all cases, because you don't have a (realistic) limit to the number of error codes. INT_MAX is certainly more than 7, and you don't have to worry about the type (errno).
I vote against the solution proposed in the question.

It does make sense that a successful execution returns true. Handling generic errors will be much easier:
if (!createUser($username)) {
// the dingo ate my user.
// deal with it.
}
But it doesn't make sense at all to associate meaning with different types of false. False should mean one thing and one thing only, regardless of the type or how the programming language treats it. If you're going to define error status constants anyway, better stick with switch/case
define(DUPLICATE_USERNAME, 4)
define(USERNAME_NOT_ALPHANUM, 8)
switch ($status) {
case DUPLICATE_USERNAME:
// sorry hun, there's someone else
break;
case USERNAME_NOT_ALPHANUM:
break;
default:
// yay, it worked
}
Also with this technique, you'll be able to bitwise AND and OR status messages, so you can return status messages that carry more than one meaning like DUPLICATE_USERNAME & USERNAME_NOT_ALPHANUM and treat it appropriately. This isn't always a good idea, it depends on how you use it.

I like the way COM can handle both exception and non-exception capable callers. The example below show how a HRESULT is tested and an exception is thrown in case of failure. (usually autogenerated in tli files)
inline _bstr_t IMyClass::GetName ( ) {
BSTR _result;
HRESULT _hr = get_name(&_result);
if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
return _bstr_t(_result, false);
}
Using return values will affect readability by having error handling scattered and worst case, the return values are never checked by the code. That's why I prefer exception when a contract is breached.

If you really want to do this kind of thing, you should have different values for each error, and check for success. Something like
define ('OK', 0);
define ('DUPLICATE_USERNAME', 1);
define ('DATABASE_ERROR', 2);
define ('INSUFFICIENT_DETAILS', 3);
And check:
if (createUser('fred') == OK) {
//OK
}
else {
//Fail
}

Other ways include exceptions:
throw new Validation_Exception_SQLDuplicate("There's someone else, hun");),
returning structures,
return new Result($status, $stuff);
if ($result->status == 0) {
$stuff = $result->data;
}
else {
die('Oh hell');
}
I would hate to be the person who came after you for using the code pattern you suggested originally.
And I mean "Came after you" as in "followed you in employment and had to maintain the code" rather than "came after you" "with a wedgiematic", though both are options.

In my opinion, you should use this technique only if failure is a "normal part of operation" of your method / function. For example, it's as probable that a call suceeds as that it fails. If failure is a exceptional event, then you should use exception handling so your program can terminate as early and gracefully as possible.
As for your use of different "false" values, I'd better return an instance of a custom "Result"-class with an proper error code. Something like:
class Result
{
var $_result;
var $_errormsg;
function Result($res, $error)
{
$this->_result = $res;
$ths->_errorMsg = $error
}
function getResult()
{
return $this->_result;
}
function isError()
{
return ! ((boolean) $this->_result);
}
function getErrorMessage()
{
return $this->_errorMsg;
}

Look at COM HRESULT for a correct way to do it.
But exceptions are generally better.
Update: the correct way is: define as many error values as you want, not only "false" ones. Use function succeeded() to check if function succeeded.
if (succeeded(result = MyFunction()))
...
else
...

Related

Why use Exception if you can use If Else code instead Everywhere? What if we dont use Exception anywhere?

Why use Exception if you can use If Else code instead Everywhere?What if we dont use Exception anywhere?
Just see if I dont want to use exception and use If Else simple conditions everywhere , it gets my work done with ease and with No doubt , so why use exception anywhere in my code ??
For example : Code with exception
<?php
function division($num1, $num2){
//If num == 0 , throw exc
if(!$num1){
throw new Exception("Hell no !! This is Divide by 0 Exception");
}
return $num2/$num1;
}
try{
echo "Division result is : ".division(0,22);
}catch (Exception $e){
echo 'Exception msg : '.$e->getMessage();
}
But say if I dont want to follow exception handling way using try catch etc.
Without exception
function division($num1, $num2){
$arr = array('error' => 0,'msg'=>'');
//If num == 0 , throw exc
if(!$num1){
$arr['error'] = 1;
$arr['msg'] = 'div by 0';
}
return $arr['result'] = $num2/$num1;
}
$res = division(0,22);
if($res['error']) {
echo "errr div by 0";
}else{
echo $res['result'];
}
If we simply use If Else conditions and Exit(0) Or Return with some custom message and its code , it will still work as expected. So Why to use exceptions in code ? And what if I dont use exception and use conditional code with return custom message??
The use of exceptions (not only in PHP but in all languages with exceptions management) is not mandatory but is highly recommended.
Oracle guys have an excellent post with some of the main advantages of exceptions (it is for Java, but is exactly the same idea).
In summary:
Separating Error-Handling Code from "Regular" Code
Propagating Errors Up the Call Stack
Grouping and Differentiating Error Types
Personally, I use exceptions just for convention, especially in big projects when the code gets difficult to read.
Of course you can do the same without it (only with if-else statements) but I like it because it provides semantic to my codes, separating the "normal or expected behavior" from "exceptional cases".
In addition, when you have too many conditions to check, if you use if-else, you will have too many nesting levels as well, making it difficult to read and understand.
In your specific example, if I only wanted to check division by 0, and no other programmer is going to use my function, probably I would use if-else. But if I were in a company, surely I would use exceptions.
If you're asking about performance overhead,you really should not use try/catch. try/catch is for things that go wrong that are outside of your control and not in the normal program flow.If you can create logic to handle exception then always go for if/else as its less processing overhead.
In you codes above, correct approach is one with if statement and not a try/catch. Cited
In case of run-time exceptions,you need to handle it so that the program or application doesn't stop running if an error occurs.
If you don't handle it,the application/program will throw an error and stop running.You don't want this to happen if you are a developer who is creating and distributing a premium software.
If such a thing happens,people or ordinary users would consider it as a bug in the software and move ahead for a better software.
So it is always better to handle exceptions.If you are using php to write some important script or program,I would suggest that you handle the exceptions.
I see what you mean, you are essentially asking why should you bother with exception handling when you can easily do the same with conditional checks? In the above example you are right, I do not see any particular need to use exception handling but I will give you a scenario where it is required :
Objective: divide two numbers
It is very easy and efficient to use if else for zero based division but note that it is an anticipated error condition meaning you already know about it before it even occurs.
Objective : read numbers from a file and divide them
Now the situation has changed. You can still use your division function but how can you guarantee that the file you are trying to open is available? or the disk the file is on , is still connected? or a million other things. Thus you can see why using if/else in such condition would most likely break your application unless you anticipate and account for all these different possibilities.
As a rule of thumb:
Use exceptions only for exceptional conditions

Proper way to error handle

This is more of a architectural question. I like to know what people's opinion on how to properly handle errors in a fully Object Oriented PHP environment.
For example I have a class that retrieves all user data from the database. So I would use it in this way:
$userData = new UserDataConnection();
$userData->openDatabase();
$userData->retrieveData();
$userData->showData();
$userData->closeDatabase();
At each of these steps, an error could have potentially occurred. So would I return a boolean from each step stating whether the function has successfully executed (hence the error checking is within each function), or do I do a try-catch exception handling around the whole thing? or is there a better way?
Is it also better to always go to some kind of error page when some kind of error occur?
<?php
$ERROR = false;
try {
$userData = new UserDataConnection();
$userData->openDatabase();
$userData->retrieveData();
$DETAILS = $userData->showData();
$userData->closeDatabase();
} catch( Exception $e ) {
$ERROR = $e->getMessage(); // exit;
}
if( $ERROR ) {
$DETAILS = array();
}
?>
If you are using PDO you can throw a PDOException.
It typically boils down to a simple question:
May this function "legitimately" fail or is any failure a sign of something being really wrong?
If a function expects a certain kind of input and should never ever be called with a different kind of input, any failure to supply the correct input is an InvalidArgumentException. This is especially true for functions which do not directly handle user input. This works even better with type hinting.
If a function should always produce a certain result given the correct input, any failure of the function to produce that result is a RuntimeException or LogicException or some other kind of exception.
If a function may or may not produce some kind of result, false is a legitimate return value. Functions which deal with user input and therefore get very arbitrary input values are often fine with returning false.
Some examples:
getDatabaseConnection() is perfectly correct in throwing a RuntimeException or similar if it cannot establish a connection to the database. This is an exceptional circumstance in which case no work can continue.
transformFooIntoBar(Foo $foo) is correct in throwing some form of exception if it cannot return a Bar instance, for whatever reason that may be. The function has a clear purpose and a type checked input value. If it cannot do its job under such clear conditions, something is clearly wrong and must be fixed. Ergo, an exception.
checkIfUserExists($id) may well return false, because its job is to return a thumbs up or thumbs down. A user not existing is not an exceptional circumstance given this job description.
Where to catch a thrown exception depends on where you want to deal with it. Database connection errors should probably be caught at the very top of the call stack, since you probably cannot do anything if the database is down.
On the other hand, if some module is calling some submodule and half expects that submodule to fail and/or has a contingency plan in case it does fail, the module may catch the exception of the submodule and continue on doing its job. For instance getRSSUpdates() makes a call to a HTTP::fetchContents($url). If the URL returns a 404, the HTTP module may throw an exception, since it cannot return contents under these circumstances. The getRSSUpdates() function is prepared for this though and handles this case as "no updates at this time".

understanding exceptions

I have read articles upon articles trying to understand what exceptions are used for in php and I have gone through the answers already given in the forum. One of the answers which made atleast some sense to me is this one: Are exceptions in php really that useful?
Here is a simple function for finding the inverse of a integer with and without using exceptions (source):
With exception:
function inverse($x) {
if ($x==0) {
throw new Exception('Division by zero.');
} else {
return 1/$x;
}
}
try {
inverse();
}
catch (Exception $e) {
echo $e->getMessage();
}
Without exception:
function inverse($x) {
if ($x==0) {
echo "I'm zero. Don't let me be the denominator.";
} else {
return 1/$x;
}
}
So here is my question, why and when should I use one over the other?
why and when should i use one over the other?
Oh, this is easy: You should never use "without exception" :) Don't misuse return values as status flag. Thats a bad habit from earlier days and only makes things more complicated, because then you have to check the return values and even their types over and over again.
If you have a function like inverse() the only thing it should ever do is to "inverse". If it can't do it, it's an exceptional situation, thus (you may guess) an exception.
To sum it up: Throw an exception, when there is a situation, that prevent a function/method to work properly, and that the function/method is not able to handle itself.
There are various opinions on "when to use an exception". My personal opinion is:
If you are working with your own code, theres basically no need to throw exceptions, as you then need to write your own handler for it - which also can be done without throwing an exception.
If you are developing APIs that other programmers are using, it can be usefull to throw exceptions, so the developer using your code knows, that he has to take care of handling errors, AND gets an idea of what was the error-reason. (instead of just getting null he might catch NumberToSmallException, NotANumberException, ....)
In other words: When you already know how to handle an exception if it would appear - dont throw it. If the handling should be up to another developer, using your code - throw it.
Exceptions should not be used to control the flow of your application logic. Therefore use if / else statements.
But these are just my ten cents.
Especially when you use object oriented programming, exceptions are quite handy. For example, in an application where you use a DB library that throws exceptions for when it cannot make a connection. In that case, you can catch that exception somewhere, and you show a special page that tells the user that the database is not working.
Maybe the best usage of Exceptions happens when there a method calls more than one level.
Think like I call method A, then it calls method B, and it calls method C. When this happens, if you do not use exceptions, method A must know all different types of error messages of method B. And method B must know about C's in the same way. But by using exception, method C's error can be caught easily without the help of method A and B.
Exceptions should be used when your script encounters an error, in the example you can't divide by zero and so you have a logical error, so an exception would be appropriate.
Using exceptions allows you to see better errors messages and will help when it comes to debugging, rather than simply printing out a string which could be anything. Furthermore you can catch exceptions so you can detect when something goes wrong, whereas simply outputting a string isn't of much help.
Check out the PHP docs on this for more info.
Exceptions are an invaluable tool when writing complex and/or extensible pieces of software, but saying that return values aren't good for reporting anomalies is IMHO an oversimplified and even dogmatic approach.
In this specific case it's perfectly reasonable to return a null value, as in "the inverse of the argument does not exist". For me the most important point is that inverse does not actually do anything; it merely provides some information (i.e. it is "read-only", it has absolutely no side effects).
Note that "absolutely no side effects" is also a good rule of thumb which means that you should definitely not echo from within the function unless echoing is why it exists in the first place.
If there were an expectation that after calling inverse successfully the state of the program would have changed and inverse cannot perform this change for whatever reason (perhaps it got passed bad arguments; perhaps a resource it needs is not available; etc) then you should absolutely throw an exception and let the caller decide how to handle the error.
Think of it like this:
Sometimes, the value for $x comes from a user (eg. from an HTML form or something), in this case you would like to display an error (maybe something like "The frobing level needs to be different than zero").
Other times, you are getting the value from a database, in that case it's kind of useless to tell the user about frobing levels or stuff like that, you will want to show an error page and log detailed information somewhere on the server (or even send an email to an admin).
With the second example it's not really possible to control what happens in case of an error (the same message is printed each time).
The right thing to do is to let the code that is calling your function decide what happens in case of an error. Exceptions are one way of doing this.
There are 2 major benefits of using exceptions:
They go through the execution stack (meaning that if you have a few nested functions, you don't have to re-pass the error value, this is done automatically.
The first piece of code after a throw() statement is the code in a catch() statement. This means that you don't have to make hundreds of checks in every nested function/method you have.
Considering this functionality, using a return value is useful in simple cases (for example your case). In complex cases, where you have 10-20-30 different error messages that can appear in different levels in the execution stack, using exceptions is a must, or other developers(/even you in a few months) will have major problems when debugging.
That's my 2 cents on the issue, hope it helps.
PS: It's useful to log the exceptions in an exceptions.log.

Handling errors in a class, what method should I use?

I have been dealing with PHP since 2000, but not very actively, and my knowledge of PHP5 is quite horrible. Recently I got my interest for webdevelopment back after a 5 year long break, and I started working on a project. I wrote a class for that project that became fairly large, but so far without any specific error handling.
The purpose of the class is to parse HTML files with a specific layout and handle its data, and it was more or less a training exercise for me to get back into the game. I started to rewrite the class now, and I decided it was time to be a little more professional about error handling than simply using die(), which I have been using a lot so far. I think we can all agree that is a terrible direction to take. I want the class to be quite project independent, so that I can distribute it to whoever wants to use it.
The HTML files I want to parse contain tables with specific bordercolors, trs with specific bgcolors, but the number of elements are dynamic. To validate that the HTML files actually have this specific pattern, I have the following sample (pseudo)code
public function Validate() {
$tables = getall('table', $this->data);
foreach ($tables as $table) {
if ($table->bordercolor != 'navy' && $table->cellspacing != 0) {
// Error
}
foreach ($tables->tr as $tr) {
if ($tr->bgcolor != '#fff') {
// Error
}
}
}
return true;
}
Where it says // Error, the HTML layout doesn't check out and my class should not attempt to parse it. Traditionally I would do this:
if ($table->bgcolor != '#fff') {
$this->error = 'Invalid HTML layout';
return false;
}
And from where I call the method I would use
if ($class->Validate() === false) {
exit_with_error($class->GetError()); // Simple return of $this->error
}
I've always thought it's a decent approach because of it's simplicity. However that's also a disadvantage as it doesn't really provide any in-depth information of the error apart from the text itself. I can't see where the error was triggered, what methods were called, etc.
Since I resumed my PHP hobby I have discovered exceptions. My problem with them is simply that I don't understand how to properly use them. And if I should use them at all. I would like my class to be portable; can I use exceptions to handle errors at all? And if yes, how can I communicate the errors (i.e. translate my traditional error handling)? Or perhaps there is an even better approach to take, which I don't know about.
Any help appreciated :)
You are certainly thinking along the right path. Typically, I like to separatte class design from error handling logic. In other words I don't want to have a bunch of $this->error = 'something' logic in the class, as why would you want to add extra code to every class to store/handle/report errors.
Now you get into exceptions vs. errors and when to use each. This is likely a subject for debate, but my personal preference has largely been to throw Exceptions in cases where you get to a point in your code that you cannot recover from or do not have the logic to handle. A great example of this, that I typically use, is throwing Exceptions right at the beginning of any class method that requires parameters of a certain sort or value. Like this example:
public method set_attribute($value) {
if (empty($value)) {
throw new Exception('You must send me something');
} else if (!is_string($value)) {
throw new Exception("You sent me something but it wasn't the string I was expecting.");
}
// method logic here
}
Here if the caller didn't give us a non-empty string, we throw an Exception, as we were not expecting this and we can't guarantee successful completion of the method without a proper value. There is not reason to continue with the method at all. We send the caller the exception with a message about the problem. Hopefully they invoked this method in a try-catch block and can gracefully handle the exception and pass it along up the call stack. If not, your code just stopped execution with fatal error from an uncaught exception (something really easy to catch in testing).
Triggering errors, I honestly use a lot less, and typically use them more for debug, warning purposes. An example of this might be a deprecated method that you still want to work, but you want to give the user an error on
public function old_method() {
trigger_error('This method had been deprecated. You should consider not using it anymore.'. E_USER_WARNING);
// method logic
}
Of course you can trigger whatever level of E_USER class warning here yourself.
Like I said, I tend to work a lot more with Exceptions, as they are also easily extensible for easy use with logging and such. I almost always would have a custom Exception class extending from PHP's base Exception class that also provides logging, etc.
The other thing to consider is global error handling and Exceptoin handling. I strongly recommend using these and having these be some of the very first lines of code in any new project. Again, it will give you much better control over how you log errors/exceptions than what you can get by default.
I don't see a problem with how you are doing it, but If you want to dive into Exceptions, learn how to use try/catch statements. Usually it would be something like this:
try {
//some code that may cause an error here
} catch (Exception e) {
//if a error is found, or an exception is thrown in the try statement, whatever here will execute
//you can get the error message by using e->getMessage()
}
you can read more about it here: http://php.net/manual/en/language.exceptions.php

Is it best practice to try - catch my entire PHP code, or be as specific as possible?

I do not have many kinds of Exceptions in my project.
Right now,(we use MVC) I have the try catch encompassing my entire code:
try{
fronController::dispatch($somthing...);
}catch(Exception $E){
//handle errors
}
I wonder if there is a good reason to use the try-catch block in as specific as possible way as I can or just keep it general as it is now?
The idea of an exception is so that a function can report failure without having to return special values. In ye old PHP, the only way a function could say it had a problem was by returning some special value like false or -1. This is not pleasant. For example, suppose I am writing a variant of file_get_contents().
The typical return value is a handle - represented by a positive integer. However, there are two basic problems I can encounter: the file you specified was not found, or the file you specified was not readable. To indicate an error I might return a negative number - because handles are positive - that associates to the particular cause of error. Let's say that -1 means the file wasn't there and -2 means the file wasn't readable.
Now we have a problem that -1 and -2 do not inherently mean anything to someone reading the code. To rectify this we introduce the global constants FILE_NOT_FOUND and FILE_NOT_READABLE. Let's see some resultant code.
<?php
define('FILE_NOT_FOUND', -1);
define('FILE_NOT_READABLE', -2);
function my_file_get_contents($file) {
// blah blah blah
}
$friendListFile = getDefaultFriendListFile();
$result = my_file_get_contents($friendListFile);
if ($result == FILE_NOT_FOUND) {
deleteFriendListFromMenu();
} elseif ($result == FILE_NOT_READABLE) {
alertUserAboutPermissionProblem();
} else {
useFriendList($result);
}
By having different error codes we can act accordingly to what the problem really is. That functionality is well and fine. The issue is purely in how the code reads.
$result is a horrible variable name. Variable names should be descriptive and obvious, like $friendListFile. The real name for $result is $fileContentsOrErrorCode which is not only too long, it examplifies how we are overloading a single variable with two meanings. You never, ever, want to have the same data mean two things. We want a separate $errorCode and $fileContents!
So how do we get around this problem? One not-really-a-solution some PHP libraries have used is to have their my_file_get_contents()-like functions return false if they encounter a problem. To disambiguate what the problem actually was we instead call my_file_get_contents_getError(). This almost works.
define('FILE_OKAY', 0);
define('FILE_NOT_FOUND', -1);
define('FILE_NOT_READABLE', -2);
$my_file_get_contents_error = FILE_OKAY;
function my_file_get_contents_getError() {
// blah blah blah
}
function my_file_get_contents($file) {
global $my_file_get_contents_error;
// blah blah blah
// whoa, an error? return false and store the error code in
// $my_file_get_contents_error
// no error? set $my_file_get_contents_error to FILE_OKAY
}
$friendListFile = getDefaultFriendListFile();
$result = my_file_get_contents($friendListFile);
if (my_file_get_contents_getError() == FILE_NOT_FOUND) {
deleteFriendListFromMenu();
} elseif (my_file_get_contents_getError() == FILE_NOT_READABLE) {
alertUserAboutPermissionProblem();
} elseif (my_file_get_contents_getError() == FILE_OKAY) {
useFriendList($result);
} else {
die('I have no idea what happened. my_file_get_contents_getError() returns '
. my_file_get_contents_getError()
);
}
As a note, yes, we can do a much better job by avoiding a global variable and other such little bits. Consider this the nuts-and-bolts demonstration.
We still cannot call $result anything better than $fileContentsOrFalseIfError. That problem has not been fixed.
I have now rectified one problem that you may have noticed in the earlier example. What if we do not cover all of the error codes? If a programmer decides that there needs to be a -3 code we weren't originally detecting it! We could have checked if $result was a string to make sure it wasn't an error code, but we aren't supposed to really care about types in PHP, right? Now that we can utilize a second return value from my_file_get_contents_getError() it is no problem to include a success code.
There is now a brand new problem that has emerged. Fix one and find three more eh? The new problem is that only the most-recent error code can be kept. This is terribly fragile! If anything else calls my_file_get_contents() before you deal with your error code, their code will overwrite yours!
Gah, now we need to keep a list of functions that are unsafe to call before you deal with the return value from my_file_get_contents_getError(). If you don't do that, you have to keep as an ad-hoc coding convention that you always call my_file_get_contents_getError() immediately after my_file_get_contents() in order to save the error code that belongs to you before it is mysteriously overwritten.
Wait! Why don't we just hand out identifiers to our callers? In order to use my_file_get_contents() you now have to ask create_my_file_get_contents_handle() for some number that will disambiguate you with all other callers. Now you can call my_file_get_contents($myHandle, $myFile) and the error code can be stored in a special location just for you. Now when you call my_file_get_contents_getError($myHandle) you can access that special place, get your error code, and no one has stepped on your toes.
Er, but if there are many callers we don't want to have zillions of useless error codes laying around. We had better ask users to call destroy_my_file_get_contents_handle($myHandle) when they are done so we can free some memory.
I hope this is all feeling very familiar to ye old PHP mantras.
This is all so crazy, just make it simple, please!
What would it mean if the language supported a better mechanism to react to errors? Clearly, trying to create some solution with the existing tools is confusing, obnoxious, and error-prone.
Enter exceptions!
<?php
class FileNotFoundException extends Exception {}
class FileNotReadableException extends Exception {}
function my_file_get_contents($file) {
if (!is_file($file)) {
throw new FileNotFoundException($file);
} elseif (!is_readable($file)) {
throw new FileNotReadableException($file);
} else {
// blah blah blah
}
}
$friendListFile = getDefaultFriendListFile();
try {
$fileContents = my_file_get_contents($friendListFile);
useFriendList($fileContents);
} catch (FileNotFoundException $e) {
deleteFriendListFromMenu();
} catch (FileNotReadableException $e) {
alertUserAboutPermissionProblem();
}
All of a sudden our old headaches of special return values and handles and coding conventions have been cured!
We can now truly rename $result to $fileContents. If my_file_get_contents() has a problem, the assignment is aborted altogether and we jump right down to the appropriate catch block. Only if there is no error do we even think about giving $fileContents a value or calling useFriendList().
No longer are we plagued by multiple callers stepping on each other's error codes! Every call to my_file_get_contents() will instantiate its own exceptions, if the error arises.
No memory problems! The garbage collector will happily clean up no-longer-used exception objects without you thinking about it. Using ye old handle system we had to remember to manually destroy the handle, lest have it lurk around forever in memory.
There are many other benefits and traits to exceptions. I strongly recommend looking to other sources to learn about these. Particularly interesting are how they bubble up the execution stack until some caller can catch them. Also interesting is how you can catch an exception, try to fix the problem, and then rethrow the exception if you can not. Do not forget that exceptions are objects! There is loads of flexibility to be gained by that. For exceptions that no one can catch, look into the exception handler.
My intent to answer the question was to demonstrate why we need exceptions. By doing this, I hope it is easy to infer what problems we can solve with them.
generally throw locally, catch globally unless an exception handler is specific to a function in which case handle locally.
class fooException extends Exception{}
// DB CLASS
public function Open(){
// open DB connection
...
if ($this->Conn->connect_errno)
throw new fooException("Could not connect: " . $this->Conn->connect_error);
}
// MAIN CLASS
public final function Main(){
try{
// do stuff
}
catch(fooException $ex){
//handle fooExceptions
}
}
Remember that exceptions are for exceptional cases. As I understand that, that happens when the error is out of your control. For example, invalid parameters are passed to a public API function, division by zero, situations like 'network connection lost', 'file not found'... this kind of things.
As a general rule, you should catch the exceptions that you know how to handle, like recovering from the error, log the error and propagate it, etc. If you don't know how to handle it, it's better to let it fail. Otherwise your application could be in an error state that you may not want.
So answering your question, it's better to be as specific as possible since every exception should be handled only if you know what to do with it (silently swallowing is a bad idea). If not just let the exception notify the user that something went wrong. Or if you want to, catch the exception to log the error and rethrow it.
There's good discussion here for C++, but the general concepts apply. I found the java tutorials on exceptions also very good.
You should be as specific as possible with catching errors in your code. Catching specific errors appropriately increases code maintainability, makes your code structured and organized.
It is also good practice as a convention, especially if you later work on team-based projects and you're not the only one looking at the code.
Personally throwing everything into one try catch block seems to be a code smell.
If you are using a try block for all your code, you might as well define a default exception handler (see the docs).
Other than that, the size of the try block is up to you, it depends on how fine you want your error handling to be. If you can't recover from any of the exceptions, there's really no reason to be specific, unless you want to log error messages that are specific (but the message of the exception and the stack trace will probably be enough).
If your handling all of your errors with one general catch you will get minimal feedback and options when an error does occour, it may be fine during development but when its on the front line it could cause you no end of problems.
Be specific and cover all of your bases where feedback is needed and recoverablity is possible.
Different errors may require different responses.
You wouldn't jump out of an airplane in response to every possible problem that could arise. Would you?
Well, that's what your app is doing.
There are situations where an exception can be caught and the application could continue to run. More likely, the app may need to respond to the same class of exception differently in different situations. Perhaps in one function an I/O exception isn't detrimental but in another it is.

Categories