I'm familiar with some of the basics, but what I would like to know more about is when and why error handling (including throwing exceptions) should be used in PHP, especially on a live site or web app. Is it something that can be overused and if so, what does overuse look like? Are there cases where it shouldn't be used? Also, what are some of the common security concerns in regard to error handling?
问题:
回答1:
One thing to add to what was said already is that it's paramount that you record any errors in your web application into a log. This way, as Jeff "Coding Horror" Atwood suggests, you'll know when your users are experiencing trouble with your app (instead of "asking them what's wrong").
To do this, I recommend the following type of infrastructure:
- Create a "crash" table in your database and a set of wrapper classes for reporting errors. I'd recommend setting categories for the crashes ("blocking", "security", "PHP error/warning" (vs exception), etc).
- In all of your error handling code, make sure to record the error. Doing this consistently depends on how well you built the API (above step) - it should be trivial to record crashes if done right.
Extra credit: sometimes, your crashes will be database-level crashes: i.e. DB server down, etc. If that's the case, your error logging infrastructure (above) will fail (you can't log the crash to the DB because the log tries to write to the DB). In that case, I would write failover logic in your Crash wrapper class to either
- send an email to the admin, AND/OR
- record the details of the crash to a plain text file
All of this sounds like an overkill, but believe me, this makes a difference in whether your application is accepted as a "stable" or "flaky". That difference comes from the fact that all apps start as flaky/crashing all the time, but those developers that know about all issues with their app have a chance to actually fix it.
回答2:
Roughly speaking, errors are a legacy in PHP, while exceptions are the modern way to treat errors. The simplest thing then, is to set up an error-handler, that throws an exception. That way all errors are converted to exceptions, and then you can simply deal with one error-handling scheme. The following code will convert errors to exceptions for you:
function exceptions_error_handler($severity, $message, $filename, $lineno) {
if (error_reporting() == 0) {
return;
}
if (error_reporting() & $severity) {
throw new ErrorException($message, 0, $severity, $filename, $lineno);
}
}
set_error_handler('exceptions_error_handler');
error_reporting(E_ALL ^ E_STRICT);
There are a few cases though, where code is specifically designed to work with errors. For example, the schemaValidate
method of DomDocument
raises warnings, when validating a document. If you convert errors to exceptions, it will stop validating after the first failure. Some times this is what you want, but when validating a document, you might actually want all failures. In this case, you can temporarily install an error-handler, that collects the errors. Here's a small snippet, I've used for that purpose:
class errorhandler_LoggingCaller {
protected $errors = array();
function call($callback, $arguments = array()) {
set_error_handler(array($this, "onError"));
$orig_error_reporting = error_reporting(E_ALL);
try {
$result = call_user_func_array($callback, $arguments);
} catch (Exception $ex) {
restore_error_handler();
error_reporting($orig_error_reporting);
throw $ex;
}
restore_error_handler();
error_reporting($orig_error_reporting);
return $result;
}
function onError($severity, $message, $file = null, $line = null) {
$this->errors[] = $message;
}
function getErrors() {
return $this->errors;
}
function hasErrors() {
return count($this->errors) > 0;
}
}
And a use case:
$doc = new DomDocument();
$doc->load($xml_filename);
$validation = new errorhandler_LoggingCaller();
$validation->call(
array($doc, 'schemaValidate'),
array($xsd_filename));
if ($validation->hasErrors()) {
var_dump($validation->getErrors());
}
回答3:
Unhanded errors stop the script, that alone is a pretty good reason to handle them.
Generally you can use a Try-Catch block to deal with errors
try
{
// Code that may error
}
catch (Exception $e)
{
// Do other stuff if there's an error
}
If you want to stop the error or warning message appearing on the page then you can prefix the call with an @ sign like so.
@mysql_query($query);
With queries however it's generally a good idea to do something like this so you have a better idea of what's going on.
@mysql_query($query)
or die('Invalid query: ' . mysql_error() . '<br />Line: ' . __LINE__ . '<br />File: ' . __FILE__ . '<br /><br />');
回答4:
You should use Error Handling in cases where you don't have explicit control over the data your script is working on. I tend to use it frequently for example in places like form validation. Knowing how to spot error prone places in code takes some practice: Some common ones are after function calls that return a value, or when dealing with results from a database query. You should never assume the return from a function will be what your expecting, and you should be sure to code in anticipation. You don't have to use try/catch blocks, though they are useful. A lot of times you can get by with a simple if/else check.
Error handling goes hand in hand with secure coding practices, as there are a lot of "errors" that don't cause your script to simply crash. while not being strictly about error handling per se, addedbytes has a good 4 article series on some of the basics of secure PHP programming which you can find HERE. There are a lot of other questions here on stackoverflow on topics such as mysql_real_escape_string and Regular Expressions which can be very powerful in confirming the content of user entered data.
回答5:
The best practice IMHO is to use the following approach: 1. create an error/exception handler 2. start it upon the app start up 3. handle all your errors from inside there
<?php
class Debug {
public static setAsErrorHandler() {
set_error_handler(array(__CLASS__, '__error_handler'));
}
public static function __error_handler($errcode, $errmsg, $errfile, $errline) {
if (IN DEV) {
print on screen
}
else if (IN PRO) {
log and mail
}
}
}
Debug::setAsErrorHandler();
?>
回答6:
Rather than outputing the mysql_error you might store it in a log. that way you can track the error (and you don't depend on users to report it) and you can go in and remove the problem.
The best error handling is the kind that is transparent to the user, let your code sort out the problem, no need to involve that user fellow.
回答7:
besides handling errors right away in your code you can also make use of
http://us.php.net/manual/en/function.set-exception-handler.php
and
http://us.php.net/manual/en/function.set-error-handler.php
I find setting your own exception handler particularly useful. When an exception occurs you can perform different operations depending on what type of exception it is.
ex: when a mysql_connet
call returns FALSE
I throw a new DBConnectionException(mysql_error())
and handle it a "special" way: log the error, the DB connection info (host, username, password) etc and maybe even email the dev team notifying them that something may be really wrong with the DB
I use this to compliment standard error handling. i wouldnt recommend overusing this approach
回答8:
Error suppression with @ is very slow.
回答9:
You can also use Google Forms to catch and analyse exceptions, without having to maintain a database or publicly accessible server. There is a tutorial here that explains the process.