Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.
matthijs wrote:But there's the difference between normal logic and real exceptional errors. The first would be: a form field filled in with an incorrect value. That's an "error" you catch with normal programming logic, if else and then show an error message in the view. The real exceptions would be things like database errors, unavailability of a webservice, etc
I disagree with this because it does not take into account the structure of the code. A subsystem error could be handled more cleanly in some circumstances with a return value, in others more cleanly with an exception. It is the structure, not the error that determines this.
Jenk wrote:My personal take on Exceptions is they are to be used when a scenario reaches an exceptional outcome. I don't like using exceptions for logic, as some people do, as they are "expensive" - especially when there aren't many scenarios that require you to use it for logic, because there will be a more logical path to take (e.g. a boolean to check if a stream is connected, rather than attempting to write, then throw/catch/handle an exception).
Again, I disagree that there is really something that is an "exceptional outcome". Either you check for conditions or your don't. Using return values or exceptions are about how you check for conditions, not about the condition itself. The idea that there are "exceptional" things is what confuses people -- because it is subjective. That is why I think the structure of the code surrounding the problem is a much better determiner of whether to use return values or exceptions. The more distant and modular the code, the more reason for using an exception
marcth wrote:My logic is that errors caused by the end-user (i.e. they entered a negative duration in a text-box) should be handled gracefully, but errors caused by developers or calling-code should result in nasty hard-errors--that way the calling code can be fixed sooner rather than later. If, instead of throwing an exception, my function merely returned FALSE, the calling-code would either propagate the error or the caller would have to look at my code to figure out what was going wrong (both these options, in my opinion wastes peoples time). To me:
is easier for the caller to use than:
Again, I don't think there is any proof that one is easier than the other until you can see the surrounding code that calls this function. That is my main point. And it is pretty clear that the return value function is less code. And wrapping it in an if() would probably be less code than wrapping it in a try/catch. So again, I would need to see the circumstances before I would go for the solution with more and slower code.
Interesting point you make arborint. Not sure if I agree or disagree. Could you give some examples about different structure situations which you'd handle with "normal" error routines or exceptions?
For example, a method in a model class, making a few queries to a db. Normal error handling (return false? return an error message?) or throw exception?
And wouldn't it be the case that the choice depends on the whole design of the code base. Meaning, that once you go the exception route, it makes more sense to use them all over in your code. For example if you look at ZF or EzComponents, there's tons of exceptions throwing there.
A Model is a good example, because the error could occur anywhere inside and the calling code really does not know anything about the internals. Database classes usually separate the call to check for an error from the call to get the error message for this reason. So you can check the return value and then get the error message, or you can try/catch the code and the error message is communicated via an exception. The net result is the same: an error occurred, you checked for it, and you got an error message. But with the exception, the error checking code needed to know less about the Model class because it did not need to call a method to get the error message. So there is looser coupling and simpler polymorphism in this case. Those could be wins if needed, or they could not matter to the calling code at all. It really depends on the circumstances.
Whoa! arborint! It appears you've taken this to heart--as would any passionate person do! And to be sure, I don't think I disagree with you generally--despite you disagreeing with me. Or maybe not.
I say exception-based programming has no place in PHP (although it is great that PHP has made this possible for those exceptional cases). And I throw exceptions left, right and center with the expectation that they shan't be caught.
However, I don't agree that a function/method can express itself properly by its return value all the time. To me, a function/method should return one precise piece of data all the time. From what I understand, you'd have a function, say one that's responsible for inserting some sort of record, return the primary key (if successful), or a negative value (on failure)--here your return value can have two different meanings--WTF is that? Or how do you handle such scenarios?
marcth wrote:However, I don't agree that a function/method can express itself properly by its return value all the time. To me, a function/method should return one precise piece of data all the time. From what I understand, you'd have a function, say one that's responsible for inserting some sort of record, return the primary key (if successful), or a negative value (on failure)--here your return value can have two different meanings--WTF is that? Or how do you handle such scenarios?
I agree. As I said above, you may need several methods. A method that fetches records should return records. If you want to check if an error occurred you might have and isError() method. And a third method to get any error message. That is a perfectly fine interface and can produce very clean logic, when fetching records in a loop for example. I also showed how you could do it with exceptions and what some of the advantages and disadvantages might be.
matthijs wrote:But there's the difference between normal logic and real exceptional errors. The first would be: a form field filled in with an incorrect value. That's an "error" you catch with normal programming logic, if else and then show an error message in the view. The real exceptions would be things like database errors, unavailability of a webservice, etc
This is the part I really disagreed with. And exception is what is generated from a throw() call. An error that is rare or severe or whatever may be thought of as 'exceptional' -- but that does not mean that using throw/catch is the appropriate control structure for it.
marcth wrote:However, I don't agree that a function/method can express itself properly by its return value all the time. To me, a function/method should return one precise piece of data all the time. From what I understand, you'd have a function, say one that's responsible for inserting some sort of record, return the primary key (if successful), or a negative value (on failure)--here your return value can have two different meanings--WTF is that? Or how do you handle such scenarios?
I agree. As I said above, you may need several methods. A method that fetches records should return records. If you want to check if an error occurred you might have and isError() method. And a third method to get any error message. That is a perfectly fine interface and can produce very clean logic, when fetching records in a loop for example. I also showed how you could do it with exceptions and what some of the advantages and disadvantages might be.
That's an interesting view. I often build functions to return either the results or false on error.
class someController {
function doSomething(){
// do stuff ..
$model->save();
if($model->isError()){
$error = $model->getError();
}
// further handle the error..
}
}
class someModel {
function save(){
$this->db->startTransaction();
try {
if ( ) {
// select query here
} else {
// another query
}
// another insert query
// and yet another one
$this->db->commit();
} catch (Exception $e) {
$this->db->rollBack();
// handle the error
}
}
}
It seems like a pretty clean way to use exceptions here. But I haven't got enough experience with bigger projects to know for sure what to use when. So this is an interesting discussion for me.
class someController {
function doSomething(){
try {
$model->save();
$model->commit();
} catch (Exception $e) {
$model->rollBack();
$error = $e->getMessage();
// further handle the error..
}
}
}
You will notice that the main difference is how the error message is communicated. There is honestly very little difference in this handling code one way or the other. So the design question here might be how easily the Model class can internally manage the error message. If things are fairly simple internally then holding and passing back the error would be very simple. But what if this Model can composite any number of sub-Models? The the communication of error messages and different possible interfaces may complicate things internally. Allowing any sub-Model to simply throw an exception may greatly simplify internal code.
These are the kind of design problems that lead to a decision one way or the other. That's why I say it has less to do with the kind of error. Checking return values can do just as good a job as catching exceptions (and vice-versa) to prevent your rocket from exploding.
arborint wrote:You will notice that the main difference is how the error message is communicated. There is honestly very little difference in this handling code one way or the other. So the design question here might be how easily the Model class can internally manage the error message. If things are fairly simple internally then holding and passing back the error would be very simple. But what if this Model can composite any number of sub-Models? The the communication of error messages and different possible interfaces may complicate things internally. Allowing any sub-Model to simply throw an exception may greatly simplify internal code.
These are the kind of design problems that lead to a decision one way or the other. That's why I say it has less to do with the kind of error. Checking return values can do just as good a job as catching exceptions (and vice-versa) to prevent your rocket from exploding.
Indeed. I think I agree with what you say here.
That is also why I showed the second example using exceptions to be more complicated, with a whole bunch of queries within the method. It was my idea that in that case it would be cleaner to use one big try-catch block, instead of having to write a sub-error handling routine for each and every query within the method.
Jenk wrote:My personal take on Exceptions is they are to be used when a scenario reaches an exceptional outcome. I don't like using exceptions for logic, as some people do, as they are "expensive" - especially when there aren't many scenarios that require you to use it for logic, because there will be a more logical path to take (e.g. a boolean to check if a stream is connected, rather than attempting to write, then throw/catch/handle an exception).
Again, I disagree that there is really something that is an "exceptional outcome". Either you check for conditions or your don't. Using return values or exceptions are about how you check for conditions, not about the condition itself. The idea that there are "exceptional" things is what confuses people -- because it is subjective. That is why I think the structure of the code surrounding the problem is a much better determiner of whether to use return values or exceptions. The more distant and modular the code, the more reason for using an exception
Not always that easy. We have a system that utilises FTP over WAN, and it's not the most stable of lines (i.e. public internet) so we have a stream, and sometimes these transfers take hours - we can't check every byte that the stream is up, so we check once before we start, then if it falls over during buffering, we fallback onto the exception logic.
There are plenty causes for using exceptions. Return values is a flawed alternative. If I am expecting a value - any type - from a method, how am I supposed to signal there is a problem? Answer: Exception.
It's also a lot quicker, and tidier, to have my error code in a catch{} block, than to nest my functionality in an if().
class someModel {
function save(){
$this->db->startTransaction();
try {
if ( ) {
// select query here
} else {
// another query
}
// another insert query
// and yet another one
$this->db->commit();
} catch (Exception $e) {
$this->db->rollBack();
// handle the error
}
}
}
It seems like a pretty clean way to use exceptions here. But I haven't got enough experience with bigger projects to know for sure what to use when. So this is an interesting discussion for me.
I have worked on very large projects and putting try/catch blocks around queries isn't that uncommon. For instance, a few columns in the database (Oracle) schema have triggers that get invoked when data is inserted or updated, which either accept the change or return a custom DB error code. Because there is no good way to test to see if the data will pass the trigger's validation routine, we just try to add the data and catch any DB error codes that may come back. It would completely defeat the purpose to recode the trigger's functionality in PHP.
Typically, however, my queries will look the way arborint does them:
arborint wrote:Your examples are not exactly equivalent. I modified them a little to show two implementations with the same functionality.
Reading this and many other threads on Exceptions vs. return values, I think I'm ready to blurt out my opinion on using them. First off, I'd like to state that exceptions, like return values, are a means to a goal: client code has to know that something went wrong and that's a goal that can be reached by using either. There is another commonality: the client has to be aware that something can go wrong, and has to check for this, whether it is by catching an exception or checking if a method returned false (and optionally get the specification of the error).
Then, onto the differences between the two approaches. Exceptions have the rare ability to "bubble" through layers, i.e. they can be catched in multiple layers of application code. Though the same effect can be achieved through return values, achieving it with the latter approach creates the need for a lot of checking and passing, while exceptions do not inflict such potentially redundant code on your application.
Adding layering to your application usually results in more maintainable software, but then it also adds abstraction over abstraction, which could become a pittfall if not designed correctly. I'll take the example of the well-respected member arborint, because we seem to like the phrase "apples-to-apples" comparison on this forum: a controller saving an object which seems to live in the model layer of a layered application. To further discuss the theory, I'll have to make some assumptions of what happens in the save method of the model object. I assume that the actual querying of the database is abstracted to another layer, possibly PDO or something similar.
Also, I'll assume that the model is aware of this abstraction, considering it also contains the methods "commit" and "rollback". I'll leave the critique on the example at this side of the keyboard, to not clutter this reply. As arborint made very clear, there is little difference between using a return value or throwing an exception in this example. The reason for this is that we're talking two layers here. If something goes wrong, the model will ask for (or catch) any errors that have occured in the database abstraction layer. It will then store these errors in one of it's properties.
If you were to take a slightly more complicated example, which uses more than one layer to save the model object, it is (for me, at least) easy to see that the return value approach may lead to redundant code. You'll have to check whatever the previous layer returned, store the errors, and return to the next layer. Throwing an exception in this situation is more beneficial, because only the responsible layer (the layer that can actually fix the problem) has to catch the exception and the rest of the layers don't have to be aware of the exception.
There is another difference. Exceptions not only bubble up multiple layers, they also do it immediately after being thrown. Only one exception (i.e. only one error-message) can be thrown in one single method, because it stops execution of said method. Errors that are retrieved later (the situation where the client checks the returned value) usually come in a stack, for example in the form of an array. Although one can stack exceptions in another exception, this usually isn't done. And although one can return a value before completing the method, this also isn't a very usual thing to do.
I don't have an answer to the question on where to use exceptions and where to use return values. No one will win the fight. I guess it all boils down to: whatever floats your boat. They both have advantages and both have disadvantages, but isn't this almost always the case in programming? I'm quite certain this debate will never stop, much like the discussions on OOP vs procedural. The reason for this is that there will always be people that neglect the disadvantages of their preffered method, and will defend it no matter what.
In my humble opinion a reason for the commotion on exceptions is their name. The name "exception" would reasonably trigger the emotion that some "exceptional" has to have happened in your code, but I don't think that has much to do with it at all. I just think the word exception is ill-defined when it comes to programming. Exception is something that has occured, but it wasn't unexpected, but out of the ordinary, yes? Then what is "out of the ordinary"? Is that every single thing that can happen when the programs execution isn't successful, like it should? It's a tough nut to crack.
Conclusion: seriously, there is no use on discussing the filosify on the name exception. It is a tool to achieve a goal, I'd recommend using it as such.
PS: Excuse any spelling and grammatical errors, English is not my native language.
Excellent (and long) post. Think you make some good points.
You are right of course that it doesn't make sense to discuss the question "which is better" or "to use exceptions or not". It would be an endless thread. But discussing examples of when to (possibly) use what and why is what I find most interesting. That's were I learn a lot from. Because I might have used error handling method X before in this or that situation, but maybe there are much better ways.
So I think the best way to get the most out of a discussion like this is to come up with concrete examples, give a bit of context and discuss those.
p.s. English is fine. At least from another dutchies point of view..
matthijs wrote:Excellent (and long) post. Think you make some good points.
Thanks.
matthijs wrote:You are right of course that it doesn't make sense to discuss the question "which is better" or "to use exceptions or not". It would be an endless thread.
Yes, it would I am just trying to write down my sentiment on this (but mostly other) threads I've seen on the topic, where the discussion on Exceptions vs return values is done to death, so to speak.
matthijs wrote:But discussing examples of when to (possibly) use what and why is what I find most interesting. That's were I learn a lot from. Because I might have used error handling method X before in this or that situation, but maybe there are much better ways. So I think the best way to get the most out of a discussion like this is to come up with concrete examples, give a bit of context and discuss those.
I would agree. Asking if using an exception instead of return values is pointless, unless you shed some light on the context it is used in.
matthijs wrote:p.s. English is fine. At least from another dutchies point of view..