I searched for this for a long time (here too), have read many php codes, but still couldn't find a satisfying answer. It may seem a too wide topic, but it really sticks together - at last for me. Could you please help?
In a php website I have PDO as DAL, what is used by BLL objects, and they are called from the UI. Now if something happens, PDO throws a PDOException. Of course the UI layer doesn't have to know anything about PDOExceptions, so the BLL object catches it. But now what?
I have read that
exceptions are for truly exceptional situations and
one re-throws exceptions from lower layers in order not to get low-level exceptions in upper layers.
Let me illustrate my problem (please don't pay attention to the function args):
class User
{
function signUp()
{
try
{
//executes a PDO query
//returns a code/flag/string hinting the status of the sign up:
//success, username taken, etc.
}
catch (PDOException $e)
{
//take the appropriate measure, e.g. a rollback
//DataAccessException gets all the information (e.g. message, stack
//trace) of PDOException, and maybe adds some other information too
//if not, it is like a "conversion" from PDOException to DAE
throw new DataAccessException();
}
}
}
//and in an upper layer
$user = new User();
try
{
$status = $user->signUp();
//display a message regarding the outcome of the operation
//if it was technically successful
}
catch (DataAccessException $e)
{
//a technical problem occurred
//log it, and display a friendly error message
//no other exception is thrown
}
Is this a right solution?
When re-throwing the PDOException I don't think it would be appropriate to use exception chaining (as this would only make debugging information redundant; DataAccessException gets everything, including the full stack trace from PDOException).
Thanks in advance.
As I understood your post a good resource is this:
Api design
I think that you've done your homework (if I can use the phrase) but you forgot the reason why this thing is being done. In your example I'd create something like
SignUpException
which would inform the upper layers that something has gone wrong about signup. What you do here is essentially masking a database exception with a different named one, which is essentially the same thing, something that although programmatically correct, misses the point of the why-s of doing it in the first place.
Related
During the process of my PHP learning I have been trying to read up on the best practices for error reporting and handling, but statements vary person to person and I have struggled to come up with a clear concise way of handling errors in my applications. I use exceptions on things that could go wrong, but for the most part it is hard for me to understand whether an exception should kill the application and display an error page or just be caught and silently dealt with.
Something that seems to elude me is, is there such thing as too much reporting? Every single time you call a function something could go horribly wrong meaning that if you were to confirm every single function call you would have to fill pages with if statements and work out what effect one failure may have on the rest. Is there a concise document or idea for error reporting that could clear this up for me? Are there best practices? What are the best examples of good error handling?
Currently I do the following:
Add important event results to an array to be logged and emailed to me if a fatal error was to occur
Display abstract/generic errors for fatal errors.
Use exceptions for cases that are likely to fail
Turn on error reporting in a development environment and off for live environment
Validate all user input data
Sanitizing invalid user input
Display concise, informative error messages to users without providing a platform for exploitation.
Exceptions are the only thing that you haven't understood IMHO: exceptions are meant to be out of your control, are meant to be caught be dealt with from outside the scope they are thrown in. The try block has a specific limit: it should contain related actions. For example take a database try catch block:
$array = array();
try {
// connect throws exception on fail
// query throws exception on fail
// fetch results into $array
} catch (...) {
$array[0]['default'] = 'me';
$array[0]['default2'] = ...;
...
}
as you can see I put every database related function inside the try block. If the connection fails the query and the fetching is not performed because they would have no sense without a connection. If the querying fails the fetching is skipped because there would be no sense in fetching no results. And if anything goes wrong, I have an empty $array to deal with: so I can, for example, populate it with default data.
Using exceptions like:
$array = array();
try {
if (!file_exists('file.php')) throw new Exception('file does not exists');
include('file.php');
} catch (Exception $e) {
trigger_error($e->getMessage());
}
makes no sense. It just a longer version of:
if (!file_exists('file.php')) trigger_error('file does not exists');
include('file.php');
I have been wondering since I stepped into the murky waters of OOP and have written a couple or so of distributed libraries when it is necessary to write my own extension of the exception class.
So far I simply use the built in exception class and it seems to serve me well. Is it necessary, and if so when is it ok, for me to write an exception subclass.
You should extend the Exception class with your own Exception types when you need to differentiate between different types of errors. Throwing an Exception just means something went wrong. You have no idea what went wrong though. Should you abort everything? Is this an expected error? Throwing a UserIsNotAllowedToDoThisException instead means something much more specific. The importance is to differentiate what code can handle what kind of error:
try {
new Foo($bar);
} catch (UserIsNotAllowedToDoThisException $e) {
echo "Sorry, you're not allowed to do this.";
}
This code handles the simple case when something is not allowed. If Foo would throw some other exception, like TheDatabaseJustCrashedAndIsBurningException, you don't want to know about this here, you want some global error handler to handle it. By differentiating what went wrong, it allows you to handle problems appropriately.
OK, here a little more complete example:
First, if you use proper OOP, you need Exceptions to fail object constructions. Without being able to fail object constructions, you're ignoring a large part of OOP: type safety and therefore data integrity. See for example:
class User {
private $name = null;
private $db = null;
public function __construct($name, PDO $db) {
if (strlen($name) < 3) {
throw new InvalidArgumentException('Username too short');
}
$this->name = $name;
$this->db = $db;
$this->db->save($this->name); // very fictional DB call, mind you
}
}
In this example, we see a lot of things:
My User objects have to have a name. Failing to pass a $name argument to the constructor will make PHP fail the whole program.
The username needs to be at least 3 characters long. If it is not, the object cannot be constructed (because an Exception is thrown).
My User objects have to have a valid and working database connection.
Failing to pass the $db argument will make PHP fail the whole program.
Failing to pass a valid PDO instance will make PHP fail the whole program.
I can't pass just anything as the second argument, it needs to be a valid PDO object.
This means if the construction of a PDO instance succeeded, I have a valid database connection. I do not need to worry about or check the validity of my database connection henceforth. That's the same reason I'm constructing a User object; if the construction succeeds, I have a valid user (valid meaning his name is at least 3 characters long). I do not need to check this again. Ever. I only need to type hint for User objects, PHP takes care of the rest.
So, you see the power that OOP + Exceptions gives you. If you have an instance of an object of a certain type, you can be 100% assured that its data is valid. That's a huge step up from passing data arrays around in any halfway complex application.
Now, the above __construct may fail due to two problems: The username being too short, or the database is for whatever reason not working. The PDO object is valid, so the connection was working at the time the object was constructed, but maybe it's gone down in the meantime. In that case, the call to $db->save will throw its own PDOException or a subtype thereof.
try {
$user = new User($_POST['username'], $db);
} catch (InvalidArgumentException $e) {
echo $e->getMessage();
}
So I'd use the above code to construct a User object. I do not check beforehand whether the username is at least 3 characters long, because this would violate the DRY principle. Instead, I'll just let the constructor worry about it. If the construction fails with an InvalidArgumentException, I know the username was incorrect, so I'll let the user know about that.
What if the database is down though? Then I cannot continue to do anything in my current app. In that case I want to halt my application completely, displaying an HTTP 500 Internal Server Error page. Here's one way to do it:
try {
$user = new User($_POST['username'], $db);
} catch (InvalidArgumentException $e) {
echo $e->getMessage();
} catch (PDOException $e) {
abortEverythingAndShowError500();
}
But this is a bad way. The database may fail at any time anywhere in the application. I do not want to do this check at every point I'm passing a database connection to anything. What I'll do instead is I let the exception bubble up. In fact, it has already bubbled up. The exception was not thrown by new User, it was thrown in a nested function call to $db->save. The Exception has already traveled up at least two layers. So I'll just let it travel up even further, because I have set up my global error handler to deal with PDOExceptions (it's logging the error and displays a nice error page). I do not want to worry about this particular error here. So, here it comes:
Using different types of Exceptions allows me to ignore certain types of errors at certain points in my code and let other parts of my code handle them. If I have an object of a certain type, I do not ever have to question or check or worry about its validity. If it wasn't valid, I wouldn't have an instance of it in the first place. And, if it ever fails to be valid (like a suddenly failing database connection), the object can signal by itself that an error occurred. All I need to do is catch the Exception at the right point (which can be very high up), I do not need to check whether something succeeded or not at every single point in my program. The upshot is less, more robust, better structured code. In this very simple example, I'm only using a generic InvalidArgumentException. In somewhat more complex code with objects that accept many arguments, you'd probably want to differentiate between different types of invalid arguments. Hence you'd make your own Exception subclasses.
Try to replicate this by using only one type of Exception. Try to replicate this using only function calls and return false. You need a lot more code to do so every time you need to make that check. Writing custom exceptions and custom objects is a little more code and apparent complexity upfront, but it saves you a ton of code later and makes things much simpler in the long run. Because anything that shouldn't be (like a user with a too short username) is guaranteed to cause an error of some sort. You don't need to check every time. On the contrary, you only need to worry about at which layer you want to contain the error, not whether you'll find it at all.
And it's really no effort to "write your own Exceptions":
class UserStoppedLovingUsException extends Exception { }
There, you have created your own Exception subclass. You can now throw and catch it at the appropriate points in your code. You don't need to do any more than that. In fact, you have now a formal declaration of the types of things that may go wrong in your app. Doesn't that beat a lot of informal documentation and ifs and elses and return falses?
The built in exception is good enough for almost every case. The only scenario that I could think of where you need another one is when there are more than one exception that can be thrown in a try and you want to do different things depending of which one is thrown. You have to distinguish two exceptions. therefore you need another one.
Only write it when you need it, e.g. to signal an own exception, for example if you code a library and no exception of the existing ones speaks the same.
See
Core PHP Exceptions
Core PHP SPL Exceptions
There are a lot of exceptions already build-in in PHP so for the normal program flow things should be covered exception-wise.
If you're developing a more complex system, like a parser, lexer plus compiler and everything is accessed via one routine/faced for it's API, the parser might want to throw a parser exception, the lexer a lexer exception and the compiler a compiler exception.
It can be useful to be able to differentiate. But I normally stick with the build in exceptions, it's easy to refactor later anyway in case it needs more differentiation.
And since there are namespaces nowadays, this is really freaking easy if you stick to the firsthand Exception class from the beginning ;)
IMHO:
Simply if you enter your own domain.
That is usually indicated when you need additional data to provide in your exception.
So actually you then also have to do you own class to add the additional class members.
I've been programming PHP for a long time, but not so much PHP 5... I've known about exception handling in PHP 5 for some time, but never really looked into it. After a quick Google it seems fairly pointless to use exception handling - I can't see the advantages of using it over just using some if() {} statements, and perhaps my own error handling class or whatever.
There's got to be a bunch of good reasons for using it (I guess?!) otherwise it wouldn't have been put into the language (probably). Can anyone tell me of some good benefits it has over just using a bunch of if statements or a switch statement or something?
Exceptions allow you to distinguish between different types of errors, and is also great for routing. For example...
class Application
{
public function run()
{
try {
// Start her up!!
} catch (Exception $e) {
// If Ajax request, send back status and message
if ($this->getRequest()->isAjax()) {
return Application_Json::encode(array(
'status' => 'error',
'msg' => $e->getMessage());
}
// ...otherwise, just throw error
throw $e;
}
}
}
The thrown exception can then be handled by a custom error handler.
Since PHP is a loosely typed language, you might need to ensure that only strings are passed as arguments to a class method. For example...
class StringsOnly
{
public function onlyPassStringToThisMethod($string)
{
if (!is_string($string)) {
throw new InvalidArgumentException('$string is definitely not a string');
}
// Cool string manipulation...
return $this;
}
}
...or if you need to handle different types of exceptions in different ways.
class DifferentExceptionsForDifferentFolks
{
public function catchMeIfYouCan()
{
try {
$this->flyForFree();
} catch (CantFlyForFreeException $e) {
$this->alertAuthorities();
return 'Sorry, you can\'t fly for free dude. It just don\'t work that way!';
} catch (DbException $e) {
// Get DB debug info
$this->logDbDebugInfo();
return 'Could not access database. What did you mess up this time?';
} catch (Exception $e) {
$this->logMiscException($e);
return 'I catch all exceptions for which you did not account!';
}
}
}
If using transactions in something like Zend Framework:
class CreditCardController extends Zend_Controller_Action
{
public function buyforgirlfriendAction()
{
try {
$this->getDb()->beginTransaction();
$this->insertGift($giftName, $giftPrice, $giftWowFactor);
$this->getDb()->commit();
} catch (Exception $e) {
// Error encountered, rollback changes
$this->getDb()->rollBack();
// Re-throw exception, allow ErrorController forward
throw $e;
}
}
}
Exception handling: If condition versus Exception isn't specific to PHP, but gives a good perspective. Personally, Exception(s) & try/catch are implemented in languages to enforce good behaviour amongst developers that normally wouldn't be as attentive to error checking / handling.
If you are confident that your if/else if/else is catching all scenarios, than cool.
Here is an overview of the Advantages of Exceptions - Java -- At one point, there is a snippet of code that has many if/else statements and the following excerpt:
There's so much error detection, reporting, and returning here that the original seven lines of code are lost in the clutter. Worse yet, the logical flow of the code has also been lost, thus making it difficult to tell whether the code is doing the right thing: Is the file really being closed if the function fails to allocate enough memory? It's even more difficult to ensure that the code continues to do the right thing when you modify the method three months after writing it. Many programmers solve this problem by simply ignoring it — errors are reported when their programs crash.
So really, it comes down to personal preference in the end. If you want code that is readable and can be consumed by other people, it's generally a better approach and enforces best-behaviour
If you are following the object-oriented methodology then exceptions comes handy for the error handling. It is convenient to communicate the errors through exception across the objects.
Exceptions are really helpful if you go with layered design approach.
If you are not coding in object-oriented way, then exceptions are not required.
We use exception handling if we are not sure about the code results. We put that snippet of code in try block and catch that error in catch block. Please check this link for more information.
In general there are two good reasons to use exception handling:
You might now always know where an exception will occur - something unexpected could arise. If you use a global exception handler you can make sure that no matter what goes wrong, your program has a chance to recover. Similarly a particularly sensitive piece of code (like something that does I/O) could have all sorts of different errors that can only be detected at runtime and you want to catch any possible contingency. Some things might not occur during testing; like what if a server outside of your control fails? This may never be tested before it really happens (although good testing would include this). This is the more important reason really.
Performance. Typically exceptions are implemented so that everything is fast so long as nothing goes wrong. Exceptions are caught after they occur. This means that no 'if' statement has to be evaluated in advance if something goes wrong, and the overhead is very low in that case. If you don't use exceptions you will be forced to add a lot of 'if' statements to your code. While usually this isn't much of a problem, this can kill a performance-critical application. This is especially true because a branch mis-prediction in the CPU can cause a pipeline flush.
I that reason is that Exception is called after trigger_error(); function and you can send also some additional information to that exception = better debugging?
I'm not sure but I think that's it
example:
class db
{
function connect()
{
mysql_Connect("lolcalhost", "root", "pass:)") or trigger_error("Test");
}
}
try
{
}
catch (db
One of the primary reasons for having an exceptions framework is so that if the code ever gets to the point where it cannot proceed, it has the ability to tell the surrounding context that something has gone wrong. It means that if I have a class Foo which needs to have $fooInstance->setBarHandler($barHandler) called before $fooInstance->doFoo(); can succeed, the class can provide a message to the greater context instead of failing silently and returning FALSE. Further, it allows the context to say, "Huh. That broke. Well, I can now tell the user/logs/something else that something bad happened, and I can decide whether I need to keep on chugging."
Exceptions can provide much more data than simple -1 or false.
Exceptions can do advanced error handling. Keep in mind that try .. catch blocks can be nested and there could be more than one catch block in try .. catch block.
Exceptions force you to handle errors. When you're not using them you do something like:
function doSomething($a, $b, $c) {
...
if ($a < $b && $b > $c) {
return -1; // error
}
...
}
$d = doSomething($x, $y, $z);
if ($d === -1) {
die('Fatal error!');
}
And everything is fine as long as you remember to check whether function returned error. But what happen if you forgot to check that? It's actually a quite common problem.
Exceptions make the flow of a program much more natural.
Exceptions are hard to use in the correct context,especially in php. Personally i use exceptions when these 3 things happen:
Resource failure exception - You can throw an exception maybe when your program runs out of memory. for example in php when you run a script that exceeds 30 seconds executing. Though you can chanage that in .ini
Client code errors exceptions - For example when trying to connect to a database with the wrong credentials or unlinking a file not on server. Or when the database server is down and unresponsive, you can throw an exception.
Programmer error exception - These are errors generated due to your own coding problems.This can also be used when you are not sure of the results your code will give you. for example when dividing by 0.
this is more of a conceptual question.
Is it aceptable/good programming mixing do/while loops with try/catch?
ex:
<?php
function main() {
$failure = 0;
do {
$failure += foo();
$failure += bar();
...
if ($failure) { return $failure; }
}
while ($failure == 0);
}
function foo() {
try {
echo 'DO FOO STUFF<br />';
return 0;
}
catch (Exception $e) {
return 1;
}
}
function bar() {
try {
echo 'DO BAR STUFF<br />';
return 0;
}
catch (Exception $e) {
return 1;
}
}
?>
The reason I'm asking is that I read somewhere that mixing both is bad practice. You should create your own exceptions and "throw" them instead. But isn't that a bit of an overkill?
EDIT:
To explain the scenario where this question emerged, imagine a robot that has to follow a line. The robot calculates its position (X position, Y poistion, Z position and "postural" position) everytime he takes a step and sends the information to a server. Server doublechecks this information. If any anomaly is found, servers sends a "stop" signal to the robot. The robot stops, recalculates its position, resends information and waits for a "go" signal.
This loop example was based on the data feed received from the robot. If something went wrong, (like a broken wireless link, or an obstacle, or a misstep) the robot must stop to prevent going astray, or falling, or anything. We aren't interested in knowing what went wrong, or why it went wrong, just that it went wrong (or not). (that is actually taken care in another part of the code, not PHP based, the Debug Module).
EDIT 2:
Like everyone pointed out, seems the right approach is to properly raise/handle the exceptions.
Since everyone seems to agree in this, I don't know who to award the "right answer" tick.
I will wait a few days and give it to the one with more votes if that's ok!
There is no reason to avoid mixing try/catch with do/while.
But the example you give has less problems with do/while and more with swallowing exceptions in the try/catch block.
Catching the base exception type and swallowing it is a bad practice in any language, PHP or otherwise. It is best to only catch specific exception types, and let the rest fall through the catch block.
For example, if you were accessing a file, and caught the FileNotFound exception (if there is such a thing in PHP), that would be OK.
But if you catch all exception types, then you're going to catch things like OutOfMemoryException. This would be fine, if you properly handled it, and made sure it wasn't going to throw again. But in your case, you're probably not "doing the right thing" for that case, so you should just let it fall through.
The reason you don't want to eat exceptions (without logging them) is that you won't be able to debug your code if it ever goes to a locked down production environment. If the exception is caught, then the user will just see buggy behavior, and won't be able to file a bug report that gives you enough information to debug it. They might be able to give you repro steps, but that is pretty unreliable. This is also pretty much worthless for an intermittant problem.
If you eat the exception, you're basically throwing away the most useful data there is for debugging.
Things that make this less true:
Sometimes in top-level web code, you will want to catch the exception, log it, then swallow it so the user doesn't see it (but return from the function, so you don't execute more code while in a buggy state). This makes it so the user doesn't see revealing information about your code (the stack trace).
If the language you are doing exception handling in has poor support for exceptions, and doesn't have derived exception types. I don't know a lot about PHP, so this may or may not be true in this language.
If you want to swallow the exceptions and just report the failure then this is okay. You'll have to do something like
$failure &= bar();
though, or you'll lose the failure condition of foo().
If you want to be able to act on the exception further up the call stack then you're better off leaving it alone, or at least rethrowing it, so you can catch it where you want to handle it.
There is no general reason to avoid using try/catch within functions called inside a do/while loop.
Depending upon what you are actually doing inside the loop you have the option of catching exceptions inside the loop, and exiting on an error condition returned, or wrapping the whole while loop in a try/catch block.
You should decide how to do it in any specific case according to what the exceptions mean for your code:
Can the exception be recovered from?
What action needs to be taken in response to an exception being thrown?
What state should your program be left in after a particular exception has been thrown and handled?
In your example, bar() will be executed even if foo() generates an error. This may or may not be what you want. Catching exceptions outside the loop would not allow this option.
Also, the loop won't terminate when bar() returns false if foo() subsequently returns true - this is probably a bug. Catching any exceptions outside the loop would mean the loop would be terminated at the point of error. Again, this may or may not be desirable depending on your application.
Also, it is likely that different exceptions need to be handled in different ways. Importantly, if you catch the base exception type, rather than a more specific derived type you are losing the ability to do this. Catching more specific exceptions (including custom exceptions) will help you:
Make sure you are not
accidentally swallowing unexpected
exceptions you didn't mean to catch.
Have multiple different error
scenarios handled by the same catch
block (by throwing the same custom
exception for each of those error
scenarios).
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.