NotionCommotion Posted January 7, 2015 Share Posted January 7, 2015 Will an uncaught exception in any of the child methods percolate up? Same thing for functions? Thank you. <?php class bla { function bla() { try { if($bad) {throw new Exception('bla bla.');} $this->something_else_that_might_throw_an_uncaught_exception(); } catch (Exception $e) {echo('do something to deal with the exception');} } function something_else_that_might_throw_an_uncaught_exception(){ $this->even_something_else_that_might_throw_an_uncaught_exception(); } } ?> Quote Link to comment Share on other sites More sharing options...
Jacques1 Posted January 7, 2015 Share Posted January 7, 2015 Yes, exceptions travel through the call stack. If you don't catch an exception in the function/method, it's passed on to the caller. If you don't catch it there, it's again passed to the caller etc. On a side note: Never catch the top-level Exception class, only specific subclasses. Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted January 7, 2015 Author Share Posted January 7, 2015 Thank you Jacques, Could you elaborate on "Never catch the top-level Exception class, only specific subclasses"? If you mean let the default error handler deal with them, I understand (but didn't 30 days ago), but if something else, please advise. Thanks Quote Link to comment Share on other sites More sharing options...
Jacques1 Posted January 7, 2015 Share Posted January 7, 2015 The Exception class covers every possible exception, it could be anything from SecurityBreachException to CPUHasCaughtFireException. If you simply catch any exception (aka Pokémon Exception Handling) and keep the application running, this can have serious consequences. Exceptions exist for a reason, so you shouldn't just stop them at will. If you want to handle a specific problem, then only catch this specific subclass of Exception. Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted January 7, 2015 Author Share Posted January 7, 2015 Gotcha, So what if I wanted to try something, and catch any for example PDOexception plus any exception I throw in my script, and deal with them in the same manner (i.e. delete any inserted records, etc)? Could I have multiple catch statements, and if so are they executed in sequence and only perform the first match? On a side note (sorry for asking more), are local variables in my try statement available in my catch? try { $bla=123; db::db()->exec('some bad SQL'); if($bad) {throw new Exception('bla bla.');} } catch (PDOException $e) {echo('deal with SQL exceptions and do the same thing '.$bla);} catch (Exception $e) {echo('deal with script thrown exceptions and do the same thing. '$bla);} Quote Link to comment Share on other sites More sharing options...
Jacques1 Posted January 7, 2015 Share Posted January 7, 2015 There's an easy way to find out: Try it. Yes, you can have multiple catch blocks, and the first match is executed. Yes, you can use the variables anywhere in the function or method, because try statements don't create a new scope. No, you still shouldn't catch the generic Exception. In this case, it doesn't even make sense, because there might be a fatal error which is not an exception. If you need a cleanup procedure, use something like register_shutdown_function() which will be called when the script ends (regardless of the reason) and allows you to check if there was a fatal error of any kind. However, there are usually more elegant solutions. For example, wrap the sequence of INSERT queries in a transaction. If one of them fails, the entire transaction is rolled back automatically, and there won't be any garbage data left. Should you actually encounter a situation where you need to catch all exceptions (I can't think of any), then rethrow the exception when you're done: <?php try { } catch (Exception $error) // for some special reason, we need to catch every exception here { // do the error handling // rethrow the exception throw $error; } Quote Link to comment Share on other sites More sharing options...
requinix Posted January 7, 2015 Share Posted January 7, 2015 (edited) If there was a single entry point into the application, such as a "router" or "front controller", then I would catch Exception there for one primary reason: to avoid a white screen of death. Without a single entry point you can use a global exception handler (ie, set_exception_handler) to the same effect. Meanwhile shutdown functions are a bit limited in what they can work with, lest you trigger undefined behavior for instance, so at that point it may be too late to take particular types of actions. I would also catch Exception if there was a place in code that had to guarantee that it does not throw exceptions of any kind. The only place I can think of where that might be the case is inside a __toString(): if you throw an exception from there then PHP will fatal out saying "Method class::__toString() must not throw an exception". Most other cases can be dealt with using a try/finally: try { // code } finally { // clean up after previous code } Edited January 7, 2015 by requinix Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted January 8, 2015 Author Share Posted January 8, 2015 There's an easy way to find out: Try it. ... For example, wrap the sequence of INSERT queries in a transaction. If one of them fails, the entire transaction is rolled back automatically, and there won't be any garbage data left Try it: Agree. Nice pun Transactions: Most of my reasons for these questions were driven by trying to implement transactions. One tries three sequential queries, and one fails, so you catch the (PDO) exception and roll it back. Do I have this right? But what if there was some business logic in the mix? Could I throw an exception so it is rolled back just as if it was a PDO exception? Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted January 8, 2015 Author Share Posted January 8, 2015 Most other cases can be dealt with using a try/finally:} I have never used finally. Is it an often used pattern that I should probably be using more often? Do you have any thoughts about inserting a business exception in between PDO exceptions, and dealing with them similarly, yet letting other exceptions remain uncaught? Quote Link to comment Share on other sites More sharing options...
requinix Posted January 8, 2015 Share Posted January 8, 2015 (edited) I have never used finally. Is it an often used pattern that I should probably be using more often?Don't know if I'd call it a "pattern"... I suppose it is, kinda. Consider code like this: function processCsvFile($file) { $handle = fopen($file, "rb"); if (!$handle) { trigger_error("Cannot open CSV file: {$file}", E_USER_WARNING); return false; } $l = 1; $header = fgetcsv($handle); if (!$header) { trigger_error("First line of CSV file {$file} is not a valid header", E_USER_WARNING); fclose($handle); return false; } while (!feof($handle)) { $l++; $line = fgetcsv($handle); if (!processCsvLine($header, $line)) { trigger_error("Cannot process line {$l} of file {$file}", E_USER_WARNING); fclose($handle); return false; } } fclose($handle); return true; }There are three places where errors can occur: when opening the file, when reading the first line, and when reading subsequent lines. For the latter two you should fclose() the file before returning or else the file handle will stay open for the rest of your script. Exceptions make it a bit less obvious that your function can quit early because they can happen at any time: in your code, in the code you call, in anything that code calls, and so on. However you should still make sure to close that file handle. A try/finally lets you keep an eye out for exceptions without forcing you to handle them; processCsvLine() could throw an exception and processCsvFile() may not want to catch it. function processCsvFile($file) { $handle = fopen($file, "rb"); if (!$handle) { throw new FileOpenException($file, "rb"); // cannot open file $file for mode rb } try { $l = 1; $header = fgetcsv($handle); if (!$header) { throw new InvalidFileDataException($file, "First line of CSV file %s is not a valid header"); } while (!feof($handle)) { $l++; $line = fgetcsv($handle); processCsvLine($header, $line); } } finally { fclose($handle); } }Notice that neither of the two functions need to return true/false anymore because "success" is returning at all and "failure" is receiving an exception. Do you have any thoughts about inserting a business exception in between PDO exceptions, and dealing with them similarly, yet letting other exceptions remain uncaught?A bit too vague for me to make a decision either way but in general I would support that.At least make sure you're using the $previous argument to Exception's constructor catch (PDOException $pdoe) { throw new BusinessPdoException("Message", other arguments, $pdoe); // class BusinessPdoException extends Exception { // public function __construct($message, $other_arguments, Exception $previous = null) { // parent::__construct($message, 0, $previous);so that logging can (hopefully) get the full trace. Edited January 8, 2015 by requinix Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted January 8, 2015 Author Share Posted January 8, 2015 Don't know if I'd call it a "pattern"... I suppose it is, kinda. Just spent the last hour teaching algebra to my 11 year old daughter. I'm going to call it a pattern throw new FileOpenException($file, "rb"); Whoah, I looked in the manual, and this the "FileOpenException" definitely doesn't exist! So, could one could just willynilly make up exceptions for what ever one wishes? I hope so, and guess this is the crux of my question. Quote Link to comment Share on other sites More sharing options...
requinix Posted January 8, 2015 Share Posted January 8, 2015 Whoah, I looked in the manual, and this the "FileOpenException" definitely doesn't exist! So, could one could just willynilly make up exceptions for what ever one wishes? I hope so, and guess this is the crux of my question.No. It was hypothetical. You'd have to make them yourself. Quote Link to comment Share on other sites More sharing options...
kicken Posted January 8, 2015 Share Posted January 8, 2015 So, could one could just willynilly make up exceptions for what ever one wishes? I hope so, and guess this is the crux of my question. You have to create the exception, but a lot of the time this can be as simple as just extending the exception class, or some sub-class of it. eg: class FileOpenException extends RuntimeException { public function __construct($file, $mode){ $message = 'Failed to open '.$file.' in mode '.$mode; parent::__construct($message); } } Once defined then you can throw new FileOpenException($file, $mode) just like you would any other exception. In this example the constructor was overridden to accept more specific parameters rather than the default of a generic message. Generally when creating an exception you will either only override the constructor, or not override anything and just extend the class. 1 Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted January 8, 2015 Author Share Posted January 8, 2015 Okay, I understand that this is not correct since I am catching any exceptions, and not a specific type. What exception should I throw if I have a problem with the file upload part? try { db::db()->beginTransaction(); //Do a bunch of queries, and throw PDO exception upon error if(!$this->save_XHR_File($file_path)) {throw new Exception('File not uploaded.');} if(!$this->verifyFileType($file_path)) {throw new Exception('Invalid file upload type.');} db::db()->commit(); } catch (Exception $e) { db::db()->rollBack(); if(file_exists($file_path)) {unlink($file_path);} } Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.