Is possible to tell if we are inside an external try..catch block or not?
Example code (please take is just as an example):
<?php
class Foo{
public function load($id)
{
try{
// Model throw NodeNotFoundException only in rare cases
$node = $this->getModel()->loadById($id);
}
catch(NodeNotFoundException $nle)
{
// #here I need to tell if im in the First case or in the Second one,
// detecting the external try..catch block
if(externalTryCatchBlock() === true)
{
throw $nle;
}
else
{
watchdog('Unable to find node', $nle->details);
}
return false;
}
catch(Exception $e)
{
watchdog('Something gone wrong.');
return null;
}
return $node;
}
}
$foo = new Foo();
// First case, no external try..catch
$node = $foo->load(2);
// Second case: we need to do here something different if the node load
// throw an exception
try{
$another_node = $foo->load(3);
}
catch(NodeNotFoundException $nle)
{
watchdog('Unable to find node, using default.');
$another_node = Bar::defaultNode(); // This is JUST for example
}
// Do something with $another_node
?>
Basically, I need to re-thrown the exception (NodeNotFoundException) only if there is another catch block waiting for it, to avoid the Fatal error: Uncaught exception.
Of course I could, for the examble above, use 2 load methods (one with and another w/o try..catch) but I would like to avoid this.. and I'm curious to know if is possible to detect try..catch blocks in PHP
It is not the job of a piece of code to worry about how it is called. The code throws an exception, always, end of story. Thereby the code has the "attribute" may throw exception x:
/**
* Does something.
*
* #return mixed Some value.
* #throws SomeException if something went wrong.
*/
function foo() {
...
}
Now, whoever calls this code needs to be prepared for it to throw an exception at any time. It is not up to the code to worry about whether somebody can catch the exception. If there's an exceptional situation, it throws an exception because it cannot continue to do its job. The caller needs to be prepared for this, not the other way around.
Related
I don't understand how to properly create and return useful error messages with PHP to the web.
I have a class
class Foo {
const OK_IT_WORKED = 0;
const ERR_IT_FAILED = 1;
const ERR_IT_TIMED_OUT = 3;
public function fooItUp(){
if(itFooed)
return OK_IT_WORKED;
elseif(itFooedUp)
return ERR_IT_FAILED;
elseif(itFooedOut)
return ERR_IT_TIMED_OUT;
}
}
And another class that uses this class to do something useful, then return the result to the user. I am just wondering where I put the string value for all my error messages.
class Bar {
public function doFooeyThings(stuff){
$res = $myFoo->fooItUp();
// now i need to tell the user what happened, but they don't understand error codes
if($res === Foo::OK_IT_WORKED)
return 'string result here? seems wrong';
elseif ($res === Foo::ERR_IT_FAILED)
return Foo::ERR_IT_FAILED_STRING; // seems redundant?
elseif($res === Foo:ERR_IT_TIMED_OUT)
return $res; // return number and have an "enum" in the client (js) ?
}
}
You should avoid returning error states whenever possible. Use exceptions instead. If you've never used exceptions before you can read about them here
There multiple ways you can utilize exceptions in your example. You could create custom exceptions for every error or for every category of error. More on custom exceptions here or you could create an instance of the default Exception class supplying it the error messages as strings.
The code below follows the second approach:
class Foo {
const OK_IT_WORKED = 0;
const ERR_IT_FAILED = 1;
const ERR_IT_TIMED_OUT = 3;
public function fooItUp(){
if(itFooed)
return OK_IT_WORKED;
else if(itFooedUp)
throw new Exception("It failed")
else if(itFooedOut)
throw new Exception("Request timed out");
}
}
I'm sure you can think of some more elegant messages than the ones I used. Anyway, you can then go ahead and handle those exceptions on the caller method using try/catch blocks:
class Bar {
public function doFooeyThings(stuff){
try
{
$res = myFoo->fooItUp();
}
catch(Exception $e)
{
//do something with the error message
}
}
}
Whatever exception is thrown from fooItUp will be "caught" by the catch block and handled by your code.
Two things you should also consider are:
It's best not to show your users detailed information about errors because those information could be used by users with malicious intent
Ideally you should have some kind of global exception handling
One solution is to use exceptions in conjunction with set_exception_handler().
<?php
set_exception_handler(function($e) {
echo "Error encountered: {$e->getMessage()}";
});
class ErrorMessageTest
{
public function isOk()
{
echo "This works okay. ";
}
public function isNotOkay()
{
echo "This will not work. ";
throw new RuntimeException("Violets are red, roses are blue!! Wha!?!?");
}
}
$test = new ErrorMessageTest();
$test->isOk();
$test->isNotOkay();
The set_exception_handler() method takes a callable that will accept an exception as its parameter. This let's you provide your own logic for a thrown exception in the event it isn't caught in a try/catch.
Live Demo
See also: set_exception_handler() documentation
I perfectly understand the nuances of the (try/throw/catch) block.
What I don't understand is:
If we gonna use an IF (or any control structure) inside our try block anyway in order to test if a condition is met, only then, 'throw' an exception if the results of that test is false, then... in my opinion: throw/generate an exception is useles; because if a condition is not met, we can simply print an error message, call a function, instantiate a class, redirect to another location, etc.
Another story would be, if for instance, a variable was not initialized, we enclose that variable inside a try{} block, echo the variable, and from that point onward, everything will be handle by the catch() block because the try block raises an error; and since the try/catch blocks talk each other, the catch block will catch every error that was originated from his corresponding try block. However, you can set a custom error message inside yout try block (optional).
What I've read so far:
every results from searching: if vs. try
I do see the difference.
But I can not understand why some people choose try/throw/catch over if...else...switch...while... etc.
As far I can see, try/throw/catch can be used for debugging, though.
One benefit of exceptions over if/then is that you can wrap try/catch around a large block of code. It will be triggered if an error happens anywhere in the block.
try {
$db = db_open();
$statement = $db->prepare($sql);
$result = $statement->execute($params);
} catch (Exception $e) {
die($e->getMessage());
}
With if/then, you would have to perform a test at each step.
$db = db_open();
if (!$db) {
die(db_connect_error());
}
$statement = $db->prepare($sql);
if (!$statement) {
die(db_error($db));
}
$result = $statement->execute($params);
if (!$result) {
die(db_error($db));
}
As you said, it's a lot of overhead to throw an exception inside a try/catch block and catch it immediately.
try {
if (...) {
// good, do no throw
} else {
throw new Exception();
}
} catch ($e) {
// handle exception
}
This should be replaced by:
if (...) {
// good
} else {
// handle error, no exception
}
Exceptions are useful because they bubble up. So imagine if you have this code instead:
function bla() {
try {
tryToDoSomething();
} catch ($e) {
// handle error
}
}
function tryToDoSomething() {
if ($somethingNotAvailable) {
throw new Exception();
}
doSomething();
}
In this case, the function that defines the try/catch is NOT the one throwing the exception. tryToDoSomething() does not know how to handle the errors so it will let parent methods to take care of it. The exception can bubble up the call stack until someone catches it and handles the error. That's how exceptions can actually be useful :)
I can't get my head around WHEN to throw and catch exceptions for when I call functions from classes.
Please imagine that my QuizMaker class looks like this:
// Define exceptions for use in class
private class CreateQuizException extends Exception {}
private class InsertQuizException extends Exception {}
class QuizMaker()
{
// Define the items in my quiz object
$quiz_id = null;
$quiz_name = null;
// Function to create a quiz record in the database
function CreateQuizInDatabase()
{
try
{
$create_quiz = // Code to insert quiz
if (!$create_quiz)
{
// There was an error creating record in the database
throw new CreateQuizException("Failed inserting record");
}
else
{
// Return true, the quiz was created successfully
return true;
}
}
catch (CreateQuizException $create_quiz_exception)
{
// There was an error creating the quiz in the database
return false;
}
}
function InsertQuestions()
{
try
{
$insert_quiz = // Code to insert quiz
if (!$insert_quiz)
{
// There was an error creating record in the database
throw new CreateQuizException("Failed inserting quiz in to database");
}
else
{
// Success inserting quiz, return true
return true;
}
}
catch (InsertQuizException $insert_exception)
{
// Error inserting question
return false;
}
}
}
... and using this code, I use the class to create a new quiz in the database
class QuizMakerException extends Exception {}
try
{
// Create a blank new quiz maker object
$quiz_object = new QuizMaker();
// Set the quiz non-question variables
$quiz_object->quiz_name = $_POST['quiz_name'];
$quiz_object->quiz_intro = $_POST['quiz_intro'];
//... and so on ...
// Create the quiz record in the database if it is not already set
$quiz_object->CreateQuizRecord();
// Insert the quiz in to the database
$quiz_object->InsertQuestions();
}
catch (QuizMakerException $quiz_maker_error)
{
// I want to handle any errors from these functions here
}
For this piece of code, I want to call a QuizMakerException if any of the functions don't perform what I want them to (at the moment they return TRUE or FALSE).
What is the correct way to go about catching when any of the functions in this code does not perform what I want them to? At the moment they simply return TRUE or FALSE.
Do I really have to put lots of if/else statements between calling each function, I thought that was the whole point in exceptions, they simply halt the execution of further statements within the try/catch?
Do I throw a QuizMakerException from within the catch of my functions?
What is the right thing to do?
Help!
Well typically in the function which throws the exception, say your InsertQuestions method, you don't want to catch any exceptions, you want to throw them or let ones occurring to "bubble up". Then your "controller" code can make the determination of how to handle the exception.
If your goal here is to halt if CreateQuizRecord fails I would wrap CreateQuizRecord and InsertQuestions each in their own try block.
One advantage of exceptions is they can tell you more than a simple bool pass/fail. Either extending your base exception into things like "Invalid_Parameter" and testing for specific exceptions or -less ideally- inferring from properties of the exception. You can nest your catch blocks to handle exceptions individually.
Do I throw a QuizMakerException from within the catch of my functions?
Yes. Typically your code under // Code to insert quiz would itself return an exception. Say if the Model failed to insert it might be raising a database exception. In which case you can let that database exception bubble up, or do what you sort of doing now and catch it simply to in turn throw another exception (kinda dumbs down your exceptions in a way, doing that though).
Do I really have to put lots of if/else statements between calling each function, I thought that was the whole point in exceptions, they simply halt the execution of further statements within the try/catch?
I look at it like this, each call which throws an exception and is followed by a subsequent call which depends on this one not throwing any exceptions, should be wrapped in a try block. Assuming you want to handle it gracefully, if you simply want it to error out and halt just don't handle the exception. You'll get an error and stack trace. Sometimes that is desirable.
You might want to change the structure a bit. Your class QuizMaker can become:
<?php
// Define exceptions for use in class
public class CreateQuizException extends Exception {}
public class InsertQuizException extends Exception {}
class QuizMaker()
{
// Define the items in my quiz object
$quiz_id = null;
$quiz_name = null;
// Function to create a quiz record in the database
function CreateQuizInDatabase()
{
try
{
$create_quiz = // Code to insert quiz
if (!$create_quiz)
{
// There was an error creating record in the database
throw new CreateQuizException("Failed inserting record");
}
else
{
// Return true, the quiz was created successfully
return true;
}
}
catch (Exception $create_quiz_exception)
{
// There was an error creating the quiz in the database
throw new CreateQuizException(
"Failed inserting record " .
$create_quiz_exception->getMessage()
);
}
}
function InsertQuestions()
{
try
{
$insert_quiz = // Code to insert quiz
if (!$insert_quiz)
{
// There was an error creating record in the database
throw new InsertQuizException("Failed inserting quiz in to database");
}
else
{
// Success inserting quiz, return true
return true;
}
}
catch (Exception $insert_exception)
{
// Error inserting question
throw new InsertQuizException(
"Failed inserting quiz in to database " .
$create_quiz_exception->getMessage()
);
}
}
}
Effectively, if you cannot insert the record correctly, you throw an Insert exception. If anything goes wrong with that block of code, you catch it and throw again an Insert exception. Same goes with the Create function (or any other ones you might have).
In the main block of code you can have:
try
{
// Create a blank new quiz maker object
$quiz_object = new QuizMaker();
// Set the quiz non-question variables
$quiz_object->quiz_name = $_POST['quiz_name'];
$quiz_object->quiz_intro = $_POST['quiz_intro'];
//... and so on ...
// Create the quiz record in the database if it is not already set
$quiz_object->CreateQuizRecord();
// Insert the quiz in to the database
$quiz_object->InsertQuestions();
}
catch (InsertQuizException $insert_exception)
{
// Insert error
}
catch (CreateQuizException $create_quiz_exception)
{
// Create error
}
catch (Exception $quiz_maker_error)
{
// Any other error
}
If you don't want to have the multiple catch block there, just keep the catch(Exception) one and then check in it the type of each exception thrown to specify the actions taken from there.
HTH
The best thing to do is to not have to thhrow exceptions in the first place.
Exceptions are thrown when the program crashes and they are not made to handle wrong output.
If your function returns anything (even its the wrong thing) it doesn't need an exception.
To answer your question, if it's necessaryto use a lot of ifs/elses then you must use them.
Which would you recommend?
Return an error code, such as E_USER_ERROR from a function, and determine proper message higher up:
function currentScriptFilename()
{
if(!isset($_SERVER['SCRIPT_FILENAME']))
{
//This?
return E_USER_ERROR;
}
else
{
$url = $_SERVER['SCRIPT_FILENAME'];
$exploded = explode('/', $url);
return end($exploded);
}
}
Execute trigger_error() from the function, with a specific error message:
function currentScriptFilename()
{
if(!isset($_SERVER['SCRIPT_FILENAME']))
{
//Or this?
trigger_error('$_SERVER[\'SCRIPT_FILENAME\'] is not set.', E_USER_ERROR);
}
else
{
$url = $_SERVER['SCRIPT_FILENAME'];
$exploded = explode('/', $url);
return end($exploded);
}
}
I am not sure if I will regret having put a bunch of error messages in my functions further down the line, since I would like to use them for other projects.
Or, would you recommend something totally different?
Do not mix the matters.
Error notification and error handling are different tasks.
You have to use both methods simultaneously.
If you think that $_SERVER['SCRIPT_FILENAME'] availability is worth an error message, you can use trigger error. However PHP itself will throw a notice if you won't check it.
If you want to handle this error, just check this function's return value.
But I would not create a special function for this task.
So,
if (!$filename = basename($_SERVER['SCRIPT_FILENAME']) {
// do whatever you want to handle this error.
}
would be enough
Exceptions could be useful to handle errors, to know if we had any errors occurred.
A simple example:
try {
$filename = basename($_SERVER['SCRIPT_FILENAME'])
if (!$filename) throw new Exception("no filename");
$data = get_some_data_from_db() or throw new Exception("no data");
$template = new Template();
//Exception could be thrown inside of Template class as well.
}
catch (Exception $e) {
//if we had any errors
show_error_page();
}
$template->show();
3.Use exceptions.
If this is the route you are going, I'd rather recommend throwing Exceptions rather then returing an E_ERROR (E_USER_ERROR should be used), as this is just an integer, and possibly a totally valid return for your function.
Advantages:
- Throwing of an Exception cannot be interpreted as anything else then an error by mistake.
- You keep the possibility to add a descriptive error message, even though you don't handle the error at that point/
- You keep a backtrace in your Exception.
- You can catch specific exceptions at specific points, making the decision where in your project a specific type of error should be handled a lot easier.
If not using exceptions which you should be, use trigger_error().
If it is an error you'd like to deal with, try returning FALSE like a lot of the in built functions do.
If you do use exceptions, catch them like this
try {
whatever()
} catch (Exception $e) {
// handle however
}
I have come accross to this function below and I am wondering wether this is the right way of using the error handling of try/catch.
public function execute()
{
$lbReturn = false;
$lsQuery = $this->msLastQuery;
try
{
$lrResource = mysql_query($lsQuery);
if(!$lrResource)
{
throw new MysqlException("Unable to execute query: ".$lsQuery);
}
else
{
$this->mrQueryResource = $lrResource;
$lbReturn = true;
}
}
catch(MysqlException $errorMsg)
{
ErrorHandler::handleException($errorMsg);
}
return $lbReturn;
}
Codewise it is correct/works, However the power of try-catch is that when an Exception is thrown from deep down in one of the functions you're calling.
Because of the "stop execution mid-function and jump all the way back to the catch block".
In this case there are no deep-down exceptions therefore I would write it like this:
(Assuming there is a function "handleErrorMessage" in the ErrorHandler.)
public function execute() {
$lsQuery = $this->msLastQuery;
$lrResource = mysql_query($lsQuery);
if(!$lrResource) {
ErrorHandler::handleErrorMessage("Unable to execute query: ".$lsQuery);
return false;
}
$this->mrQueryResource = $lrResource;
return true;
}
Which I find more readable.
No. Throwing an exception in this case is simply a GOTO, but with a (slightly) prettier face.
Why have a call to ErrorHandler::handleException here anyway?
Just throw the exception, but never catch it. Then in the global initialization code for your app have a function with the following signature:
function catchAllExceptions(Exception $e)
Then call:
set_exception_handler('catchAllExceptions');
This will cause all uncaught excpetions to be passed as an argument to catchAllExceptions(). Handling all uncaught exceptions in one place like this is good, as you reduce code replication.
Well it is not really a good implementation since you throw the exception and you look for that exception in catch. So the answer of Visage is true.
You should use a global error handler instead of a tr-catch usage like in your code.
If you are not sure of the type of the error and occurance but want to continue the execution of the code although an exception had occured, then a try-catch block will help.