Why and how would you use Exceptions in this sampl

2019-01-16 16:36发布

问题:

I've been wondering why would I use Exceptions in my PHP. Let's take a look at a simple example:

class Worker
{
 public function goToWork()
 {
  return $isInThatMood ?
  // Okay, I'll do it.
  true : 
  // In your dreams...
  false;
 }
}

$worker = new Worker;
if (!$worker->goToWork())
{
 if (date('l',time()) == 'Sunday')
  echo "Fine, you don't have to work on Sundays...";
 else
  echo "Get your a** back to work!";
}
else
 echo "Good.";

Is there a reason for me to use Exceptions for that kind of code? Why? How would the code be built?

And what about code that may produce errors:

class FileOutputter
{
 public function outputFile($file)
 {
  if (!file_exists($file))
   return false;
  return file_get_contents($file);
 }
}

Why would I use Exceptions in the above case? I have a feeling that Exceptions help you to recognize the type of the problem, which occured, true?

So, am I using Exceptions appropriately in this code:

class FileOutputter
{
 public function outputFile($file)
 {
  if (!file_exists($file))
   return throw new Exception("File not found.",123);
  try
  {
   $contents = file_get_contents($file);
  }
  catch (Exception $e)
  {
   return $e;
  }
  return $contents;
 }
}

Or is that poor? Now the underlying code can do this:

$fo = new FileOutputter;
try
{
 $fo->outputFile("File.extension");
}
catch (Exception $e)
{
 // Something happened, we could either display the error/problem directly
 echo $e->getMessage();
 // Or use the info to make alternative execution flows
 if ($e->getCode() == 123) // The one we specified earlier
  // Do something else now, create "an exception"
}

Or am I completely lost here?

回答1:

When should I use an exception?

You use an exception to indicate an exceptional condition; that is, something which prevents a method from fulfilling its contract, and which shouldn't have occurred at that level.

For example, you might have a method, Record::save(), which saves changes to a record into a database. If, for some reason, this can't be done (e.g. a database error occurs, or a data constraint is broken), then you could throw an exception to indicate failure.

How do I create custom exceptions?

Exceptions are usually named such that they indicate the nature of the error, for example, DatabaseException. You can subclass Exception to create custom-named exceptions in this manner, e.g.

class DatabaseException extends Exception {}

(Of course, you could take advantage of inheritance to give this exception some additional diagnostic information, such as connection details or a database error code, for example.)

When shouldn't I use an exception?

Consider a further example; a method which checks for file existence. This should probably not throw an exception if the file doesn't exist, since the purpose of the method was to perform said check. However, a method which opens a file and performs some processing could throw an exception, since the file was expected to exist, etc.

Initially, it's not always clear when something is and isn't exceptional. Like most things, experience will teach you, over time, when you should and shouldn't throw an exception.

Why use exceptions instead of returning special error codes, etc?

The useful thing about exceptions is that they immediately leap out of the current method and head up the call stack until they're caught and handled, which means you can move error-handling logic higher up, although ideally, not too high.

By using a clear mechanism to deal with failure cases, you automatically kick off the error handling code when something bad happens, which means you can avoid dealing with all sorts of magic sentinel values which have to be checked, or worse, a global error flag to distinguish between a bunch of different possibilities.



回答2:

I'm not a PHP programmer, but this looks similar to C#.

Typically you'd want to throw errors if it is a point of no return. Then you would be able to log something to show that the impossible happened.

If you can tell that the file doesn't exist then you could just say that. No real need in my mind to also throw an exception.

Now if the file was found and you are processing it, and say only half the file was uploaded and you had no way of telling that without an exception, then it'd be nice to have around.

I would say it's a good design practice to avoid the catch all exceptions "catch (Exception $e)" and design by contract instead, just like you seem to be doing in the prior example. I would at first be more specific with the type of exception being thrown and then work from there if needed. Otherwise, stay away from the try->catch and exceptions.



回答3:

In no way can you assume that a file exists just because you called file_exists()! The function file_exists doesn't open the file, so the file can potentially be deleted or renamed or moved at any time!

class FileOutputter
{
    public function outputFile($file)
    {
        if (!file_exists($file))
            return false;
        ///<--- file may be deleted right here without you knowing it
        return file_get_contents($file);//<-- then this will throw an error
            //if you don't catch it, you're program may halt.
    }
}

I believe this it better:

class FileOutputter
{
    public function outputFile($file)
    {
        try{
            if (!file_exists($file))
                return false;
            return file_get_contents($file);
        }catch(Exception e){
            check_what_went_wrong_and_go_to_plan_B();
        }
    }
}

(Edit: And it's probably even better to actually try to open the file from the beginning. If you succeed then you have a 'lock' on the file and it won't just disappear. If not, then catch the exception and see what went wrong.

Then again, you might feel that this level of reduncancy is just silly. In that case I don't think you need to worry about try/catch at all :)



回答4:

Just a note really, your code for file outputter is invalid, since file_get_contents($file) does not throw an exception. It will however raise a warning if the file doesn't exist, or can't be accessed for some reason. Additionally, you are returning an exception from outputFile, when you should probably be just letting the error propagate up the call stack.

However, you can register the error handler in php to throw exceptions when an error occurs:

function exception_error_handler($errno, $errstr, $errfile, $errline ) {
    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
set_error_handler("exception_error_handler");

That way, any standard function calls that cause an error will throw an exception.

So, I would change FileOutputter to this (with that snippet above added):

class FileOutputter
{
 public function outputFile($file)
 {
  if (!file_exists($file))
   throw new Exception("File not found.",123);

   return file_get_contents($file);  
 }
}

The calling code is then basically the same.

Basically rule is not to return an Exception when an error occurs - you can catch it if you want, and throw a custom exception, but let the exception go up the call stack until you want to use it.