I have had a few nerve racking days trying to get a good error handling system in place within my site.
My error handling system is only focusing on unexpected errors e.g. mysqli errors
My site runs many mysqli queries meaning there is potential for errors to occur.
There is 3 parts to my system, and every one I am unsure on its place in best practices as well as efficiency.
Step 1 : catching the error
$query = "
SELECT * FROM `users_account_activations` WHERE `user_ip` =?
";
$statement = $databaseHandler->mysqli->prepare($query);
$statement->bind_param('s', $userIp);
$statement->execute();
$statement->store_result();
//record error, if any
$databaseHandler->mysqli->error ? error = true : error = false;
Is this a good way of catching the presents of an error somewhere in a prepared statement?
Will looking for errors at the end of the query not catch a error say in the prepare stage? or when the prepare stage fails all other fails (like falling dominoes)
Step 2 : recording the error
error_log("Could not process query...", 3, 'log/default.log');
Is this a suitible way of recording a error? as aposed to the systems that opens a file steam?
Step 3 : handling the after math
Now that there is a error I need to decide on how to move forward, my system intails exiting the script and going to a standard error page saying "opps! something went wrong".
header('location: errorpage.php'); exit();
is changing the header a good way of doing this? I am aware of the limitation of header regarding when you can use it.
Is there a better way for handing errors, this system is intended for unexpected errors. Expected errors are took care of and displayed to the users a different way.
Yes, you are right with your doubts - all the three steps are wrong.
First, for some very strange reason you are not recording the error message itself. How it is supposed to fix the error without error message? What's the use of logging something like "Could not process query..."?
Next, you have to check the result of every operation - so, prepare have to be checked too.
Next, it will be hard to spot the error without knowing where it happened. Some debug backtrace have to be included in the error message.
Next, there shouldn't be a Location header but just Status:500. So, instead of redirecting to error page, just include it.
Finally, all the aforementioned tasks have to be performed in a single place, namely - in the function registered as a custom error handler
So, the scenario have to be like this
in your bootstrap file create a code like this
set_error_handler("myErrorHandler");
function myErrorHandler($errno, $errstr, $errfile, $errline)
{
error_log("$errstr in $errfile:$errline");
header('HTTP/1.1 500 Internal Server Error', TRUE, 500);
readfile("500.html");
exit;
}
then, make your mysqli code like this
$stmt = $mysqli->prepare("qwefq") or trigger_error($mysqli->error);
and have all your errors logged
Related
If I use a bit of code like this:
$update_result = mysqli_query( $link , $sql_update_login ) or die ('Unable to execute query. '. mysqli_error($link));
Does it have to die or can you put a different query afterwards? Like a predetermined function that writes a log of the error to another table? Such as:
$update_result = mysqli_query( $link , $sql_update_login ) or function('$query, $error);
What are the other options after 'or'? I haven't found it in the documentation, any clues are appreciated.
Does it have to die
Quite contrary, it shouldn't or die() ever.
PHP is a language of bad heredity. Very bad heredity. And or die() with error message is one of the worst rudiments:
die throws the error message out, revealing some system internals to the potential attacker
such error message confuses casual users, because they don't understand what does it mean
Besides, die kills the script in the middle, leaving users without familiar interface to work with, so they'd likely just drop out
it kills the script irrecoverably. While exceptions can be caught and gracefully handled
die() gives you no hint of where the error has been occurred. And in a relatively big application it will be quite a pain to find.
So, never use die() with MySQL errors, even for the temporary debugging: there are better ways.
Instead of manually checking for the error, just configure mysqli to throw exceptions on error, by adding the following line to your connection code
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
and after that just write every mysqli command as is, without any or die or anything else:
$result = mysqli_query($link, $sql);
This code will throw an exception in case of error and thus you will always be informed of every problem without a single line of extra code.
A more detailed explanation on how to make your error reporting production ready, uniform and overall sensible while making your code much cleaner, you can find in my article on PHP error reporting.
or is just an operator (very similar to ||).
The or die() syntax works because or short-circuits, which means that if the first statement is true, True or X will always be true, so X isn't evaluated and your script doesn't die.
Yes, you can provide a different function after the (or).
I have tested the following:
mysqli_query($sel_db,'what!') or some_func(mysqli_error($sel_db));
function some_func($str) {
die("ERROR: ".$str);
}
It doesn't have to be die() specifically, but it needs to be something that'll make the script halt by calling exit() or die(), or something that throws an exception. Otherwise, the script will continue with the return value of that function (which is probably either null or some sort of junk) in $update_result, which will almost certainly cause problems.
I've just finished refactoring a bunch of MySQL and MySQLi forms to PDO.
Everything seems to be working.
Now on to error handling.
In the MySQL / MySQLi code I had been using if statements to catch errors. Like this:
if (!$database_connection) {
// error handling here
}
Plain and simple.
But I can't get a similar set-up to work with PDO.
Here's the scenario:
I have a connection to a database that looks something like this:
$data_source_name = "mysql:host=$db_host;dbname=$db_name";
$database_connection = new PDO($data_source_name, $db_username, $db_password);
The execution looks something like this:
$stmt= $database_connection->prepare($sql);
$stmt->execute([$name, $email]);
I'm trying to set up a condition like the one described above:
if ( database connection fails ) {
// error handling
}
But this code doesn't work.
if ( !$database_connection ) {
// error handling
} else {
$stmt= $database_connection->prepare($sql);
$stmt->execute([$name, $email]);
}
This if construct worked in MySQL (now deprecated) and works in MySQLi, but not PDO.
I was originally trying to make this work using try-catch, as recommended in many Stack posts. But after more research it appears that this function is inappropriate for PDO Exceptions.
Any guidance and suggestions appreciated. Thanks.
It's a very common fallacy, that one needs a dedicated error handling code for PDO or Mysqli (or whatever else module for that matter). Least it should be even more specific, such as "Mysqli connection" handler, as it seems with your old mysqli code.
If you think of it, you don't really care whether it was exactly a database error that prevented the code form being executed correctly. There can be any other problem as well.
Admittedly, one hardly can expect any other problem from such a simple code but still, the code may grow, become more modular, perform more tasks - and therefore error out in any other part as well. Like, writing database credentials in the every file is a bit of waste. So it's natural to put them in a file and then just include it in the every other script that requires a database interaction. So this file may get corrupted which will has the same effect as a database error. And will need to be fixed as well.
Or, if you're handling only the connection error, the problem can happen during the query execution as well (especially in your case, as the way the query is executed it will error out even if a customer will simply enter fish'h'chips for example).
What you really care for is whether the data has been stored correctly (and probably whether emails were sent as well) or not, no matter what could be the possible failure. This is being the exact reason, why I wrote in the article this warning against wrapping some specific part of code in a try-catch in order to report this particular error. As error reporting handler must be common for the entire code.
Admittedly, the simplest exception handling method is simply wrapping the entire code in a try catch block where the most generic exception type, namely Throwable, must be checked for. Not very reliable but simplest.
The key here is to wrap the entire code, not just some random part of it. But one shouldn't forget to set the exception mode for PDO, in order let the query execution errors to be caught in this block as well.
<?php
try {
require 'pdo.php'
...
$sql = "INSERT INTO other_choices (order,foods) VALUES (?,?)";
...
$stmt= $db_connection->prepare($sql);
$stmt->execute([$order, $foods]);
// send emails, etc
} catch (Throwable $e) {
// do your handling here
}
Note that I substituted actual variables in the query with question marks, which is being correct way of using prepared statements, that otherwise become useless and render all your transition from mysqli fruitless (especially given that mysqli supports prepared statements as well).
Unfortunately, PHP has two kinds of errors - exceptions and errors proper. And try-catch can catch only the former. In order to handle all kinds of errors, an error handler can be used. You can see a very basic example of one in my article on PHP error reporting.
The last note: sending an email every time an error occurs on the site is not the wisest move. Although in your case it could be justified, given PHP is only involved when a user submits a form, but on a regular site, where PHP is used to handle every page, it can lead to thousands emails. Or even in your case, spammers may target your forms and send thousands requests as well (which itself may cause some overflow error and therefore thousands emails in the inbox). Instead of sending emails manually, consider using a dedicated error monitoring software, such as Sentry. It will send only new errors, as well as aggregated error info.
new PDO raises an exception if the connection fails. Use an exception handler:
try {
$database_connection = new PDO($data_source_name, $db_username, $db_password);
} catch (PDOException $e) {
// error handling
}
EDIT: about the linked answer above, it's similar but different, since my goal is to debug the error in the error page.
Sometimes an unexpected error is hard to debug, since the error report is printed inside strange HTML elements, like an hidden div or a option element.
Is there not a way to automatically store error objects in a global variable and redirect the script to an error page, if any uncaught error is fired? And is there a way to do this for all errors, included the ones that normally doesn't quit the script, like warnings and notices?
You do this with a custom error handler. Turning errors into exceptions is a good way and allows you very fine grained control over error handling:
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
});
You may decide for which errors to throw exceptions and which to ignore; for example you may want to ignore or just log E_NOTICEs. With a global try..catch block or a custom exception handler you can now very easily decide what to do in case of errors.
Indeed there are ways to both redirect errors to pages, log them, track them, and what not. PHP is quite flexible. The good news is you don't have to homecook such methods, frameworks are available for that, but you can also survive without these as built in error handling facilities of PHP are sufficiently usable. If configured properly, PHP will abort on errors (or warnings, you decide), log them, and even return HTTP 500 Server Error code if plugged into a web server.
You may need to configure PHP properly. It is perfectly capable of a better error handling workflow. First of all, disable error printing, this is not how well behaved applications should report errors, and at worst, helps malicious users to break their way into your systems, using printed error output. You are not the only one viewing your webpages, you know, and not all users get confused seeing these, some wait for these. This is one of the directives you can use, editing the "php.ini" file, which configures PHP; it disables mixing error output with whatever else PHP outputs as part of content generation:
display_errors = "0"
You can also set it to "stderr", which is a good thing to do when debugging scripts using command line PHP invocation, as the output will be sent to another file channel, the so called standard error.
Take now heed of the following "php.ini" directive:
log_errors = "1"
The above will have PHP log errors either to a file or using web servers error logging facilities, depending on how PHP is invoked. On UNiX systems, the log file, listing the error and its details, will reside in "/var/log/www/", for instance.
Take a good read through the PHP documentation on error handling and reporting, starting perhaps at the following page:
http://www.php.net/manual/en/book.errorfunc.php
Don't forget to read on installation configuration. And I repeat again, NEVER have PHP display errors for a public PHP script! (and yes, I am aware that you are debugging, but I can't stress this point enough these days).
Thanks to #MladenB. and deceze, I solved my problem. This is how I coded the solution:
in a config.php file, to be included in your scripts (it's better to move the functions to a personal library file):
<?php
function my_error_handler($errno, $errstr, $errfile, $errline)
{
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
function my_exception_handler($e)
{
/**
* Exception handler that pass the error object to an error page.
* This is to avoid bad displaying or hiding of error reports.
*
* #param $e Exception The exception to manage
*/
if (session_status() !== PHP_SESSION_ACTIVE)
{
session_start();
}
session_register_shutdown();
$_SESSION['error'] = $e;
header('Location: error.php');
exit();
}
set_error_handler('my_error_handler');
set_exception_handler('my_exception_handler');
in error.php:
<?php
session_start();
session_register_shutdown();
$e = $_SESSION['error'];
echo '<h2>Stack trace</h2>';
echo var_dump($e->getTrace());
throw $e;
This question already has answers here:
PHP Error handling: die() Vs trigger_error() Vs throw Exception
(2 answers)
Closed 2 months ago.
For example, my usage would be:
$check = 'no';
if($check == 'yes') {
//do stuff
} else {
die('Error found');
}
Many developer's what i seen use:
if($check == 'yes') {
//do stuff
} else {
throw new Exception('Error found.');
}
Which one method is 'better' ?
Any benefit's throwing an exception instead of stoping executing script ?
Which one method is 'better' ?
This depends on your needs. It can't be said which one is better (and there are other ways of error handling as well you should put into consideration when you actually want to discuss error handling which this site is probably not the right place for).
Any benefit's throwing an exception instead of stoping executing script ?
An exception can be caught, a die can't be caught. If you want to test your code for example, dies are often a show-stopper.
Next to that an exception can carry more information and carry it more precisely. The message for example is more accessible with an exception than it is with a die. An exception keeps the file and line where it was thrown. For debugging there are stack traces and so on.
Exceptions are better (in design of large sites), because:
They don't stop script immediately (you've got a chance to inform user in nice 5xx page about internal server error)
If you decide to handle error differently in the future you can do so without modifying original code
Exceptions provide backtrace and ease up debugging
I'm not sure, but destructors shouldn't be called when die is used (exceptions provide option to execute them)
You may create lot of Exceptions types, each for different kind of error and handle them easily later
You should use die probably only in smaller scripts and pages where you don't need to style error for use or in case of fatal error (cannot include main library into index or something like that).
You can use both, throw new Exception if some exceptional situation occurs (database connection or query, page or file not found...), and then catch it where that suits you. Then maybe log error to file, send mail to administrator and then use die("Some textual message to user.");. If you don't want to use die() you can show user some 404 not found or 500 internal error page.
So I've just read Why to never use 'or die'.
I am more confused then ever. I am validating a complicated form and I go though many nested levels of if statements and what not and I am passing a variable to the form which is called $status which can only be 'new' or 'edit'. Then when the user submits the form to be validated again the form passes along the $status value as a hidden field ($_POST). I want to make sure that the user cannot accidentally change this so I want to catch the error should something other than 'new' or 'edit' pass though. (Although I would like to completely eliminate the possibility of the user affecting this variable in an ideal world.)
So I figured that I would use DIE() in an if statement
<nested ifs>
Select ($status){
Case 'edit':
break;
Case 'new':
break;
default:
//using example from article
trigger_error("An error that should not occur has occurred", E_USER_ERROR);
break;
}
</nested ifs>
I don't really understand how this is cleaner than die()? Essentially I would like to call another function which displays the user some options of what they can do at this juncture to fix the error, but I want the code to absolutely stop running as I don't want an if statement further down to continue parsing anything and generating an error when it finds something other than 'new' or 'edit'.
I'm not sure how clear I am so please feel free to ask me to elaborate on any unclear points.
(or better yet, can a hidden user field get hacked? How to prevent? :P)
trigger_error() triggers an error which is handled by an error handler.
With trigger_error() you can gracefully handle errors, for example:
set_error_handler('ErrorHandler');
function ErrorHandler($errno, $errmsg, $filename, $linenum, $vars)
{
print '<pre style="line-height: 2em;">';
printf("==> Error in `%s' line %s: %s\n\n", $filename, $linenum, $errmsg);
debug_print_backtrace();
print '</pre>';
exit($errno);
}
This is a simple example, but our company website displays a friendly error page and sends me an email that I'm an idiot and messed up somewhere :-)
The advantage over die() or exit() should be clear :-)
exit() can still be used when you need to exit. For example when you generate a template, output that, and want code execution to stop. Or when you send a header('Location: ...'); header and want to make sure the execution stops ... Just don't use it for handling unexpected situations (i.e. errors).
trigger_error() also gives you a better degree of control. You can send E_USER_NOTICE when you want execution to stop but display a notice, and E_USER_ERROR when you want execution to stop.
In addition, you can write more complex error handler functions where some IP's see a error, and the rest do not ... Kindda useful for development.
Be a but careful with overly complicated error handlers though, what happens is an error occurs inside an error handler ... ? You may have seen Inception :)
The article you linked to explains why you shouldn't use or die, as in:
$result = mysql_query($query) or die('A MySQL query occurred: ' . mysql_error());
Obviously this is a bad idea, as it could output sensitive information about your MySQL structure to a malicious user.
However, I think there is nothing wrong (and I think the writer of the article would agree with me) to use die in the way you are using it, although presenting the user with a list of options would, indeed, be the preferred way to handle this.