What is the best way to handle errors on front end websites? Similar to twitter, reddit, etc that have a nice 'something went wrong' page. I want to:
detect errors and be notified, even if its just to a log file
show a custom error page such as the fail whale or similar, so its
obvious something has gone wrong instead of showing nothing, or a horrible looking error.
Ideally anything that prevents a user from seeing the site, like fatal errors, custom exceptions, mysql being down, random 500 errors etc, should show the custom error page.
Fatal Errors
PHP : Custom error handler - handling parse & fatal errors - works ok, but if this happens at the end of a script, and it has already output something, this wont work
Non Fatal Errors
http://davidwalsh.name/custom-error-handling-php
MySQL Errors
Ideally need to cover everything from a simple query error, to the server not being there. Imagine its as simple as detecting the error in my database class and calling redirecting to / calling the error method
Server Errors
Is this something I should be doing in PHP OR Apache OR both?
Code at the moment
Currently I have the following from a mix of various other SO's, but fatal errors will just be reported after the rest of the page has loaded.
Anyone have any ideas for something that will work with all of the above?
<?
function xhandler($number,$string,$file,$line,$context)
{
//log to text file?
//log to xml file?
//store in database?
//whatever you want to do!
echo "<h1>here be dragons!</h1>";
echo "$number,$string,$file,$line,$context";
exit();
}
function fatal_handler() {
$errfile = "unknown file";
$errstr = "shutdown";
$errno = E_CORE_ERROR;
$errline = 0;
$error = error_get_last();
if( $error !== NULL) {
$errno = $error["type"];
$errfile = $error["file"];
$errline = $error["line"];
$errstr = $error["message"];
}
echo "<h1>here be dragons!</h1>";
print_r($error);
exit();
}
register_shutdown_function( "fatal_handler" );
set_error_handler('xhandler',E_ALL);
include('show_the_content');
function_that_doesnt_exist();
echo $unterminated_line_of_code
?>
You can't catch fatal errors. They are fatal, script execution stops immediately when they're encountered. These errors should never appear on production sites.
Notices and warnings should be suppressed from showing in HTML on production environment (live sites).
What these sites usually do is handle HTTP errors with PHP. You need to redirect these errors to a PHP script which will handle them. For example, in Apache you do it with the ErrorDocument directive. You can put these directives (one for each HTTP error code) in the server configuration or in the .htaccess file in the web site's document root.
In the PHP script, you can filter different HTTP errors with $_SERVER['REDIRECT_STATUS'], if you need to. When you receive a request with the desired HTTP error, you can handle it any way you wish.
Related
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;
First of all Im using PHP 5.4.3.
The problem I have is that whenever an error occurs in a PHP file (such as parse error), it will display an error message in HTML and the HTTP status of the page will be 200.
What I want to know is how can I set the HTTP status to 500 if any errors occur and to display no errors at all.
Keep in mind that I do not want to display HTTP status 500 for every page, only for a few.
You can use register shutdown function. Source: PHP : Custom error handler - handling parse & fatal errors
ini_set('display_errors', 0);
function shutdown() {
if (!is_null(error_get_last())) {
http_response_code(500);
}
}
register_shutdown_function('shutdown');
Note, E_PARSE errors will only be caught in files that are included/required after the shutdown function is registered.
myninjaname's answer is nearly right - however you don't know what's happened with the output buffer at exit. Also, your code will always run to completion. I'd go with using output buffering and a custom error_handler in addition to handling the situation immediately, guaranteeing the response to the browser this also makes it easier to trap and log information relevant to the error so you can fix any bugs:
<?php
ob_start();
set_error_handler('whoops');
function whoops($errno, $errstr, $errfile, $errline, $errcontext)
{
header($_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error', true, 500);
$logged="$errno: $errstr\nin $errfile at $errline\n";
foreach($errcontext as $scoped=>$val) {
$logged.=" $scoped = $val\n";
}
$logged.="Output = " . ob_get_clean() . "\n";
// NB still buffering...
debug_print_backtrace();
$logged.="stack trace = " . ob_get_clean() . "\n";
print "whoops";
ob_end_flush();
write_log_file($logged);
exit;
}
You can really handle parse errors - they occur when you've written and deployed bad code - you should know how to prevent this happening on a production system. Adding more code to try to solve the problem is an oxymoron.
You should also disable displaying of errors (not reporting) by setting display_errors=0 in your ini file.
update
I tried to catch any errors that occur with a custom error handler, but for some reason people say that it doesn't catch parse errors (and a few others too).
Don't you know how to test this for yourself? IIRC a lot depends on the context (a shutdown function can catch some parse errors in included content) and the version of PHP. But as above, if you have got to the point where such an error occurs on a production system, it's because you've failed to do your job properly.
Turn off error reporting with error_reporting(0);
Send your own HTTP status code with header("HTTP/1.0 500 Internal Server Error");
Reference
error_reporting()
header()
you may simply set the response code:
http_response_code(500);
I have a website built using PHP and Mysqli and I'm at the part where I should think about my error handling.
Even if I think that my code is perfect errors will appear when I release the website to the public. I found this answer that shows how I can hide the errors for the users but the developer can still see them. Though I don't know if this is really the best approach for my site. I don't want the user to see ugly error messages produced my PHP itself but that I could design my own error message depending on the error.
How should I manage these errors? Should I save them all in a database?
How do I know which errors could occurr?
PHP has in-built function to catch various types of errors:
set_error_handler
You should use this function to capture the errors across all your pages, you can write custom code whether to insert errors to database, or to write into separate error log file, or to notify immediately through email to developers, you can decide.
I would start by using
try
{
//your code here
}
catch(Exception $ex)
{
echo $ex->getMessage();
}
When doing database queries. The error handling can be loggin it to a file or something like that.
That way you catch what's happening and set yourself what needs to be done....
error_reporting(E_ALL);
ini_set('display_errors','On');
ini_set('error_log', 'error.log');
ini_set('log_errors', 'On');
These functions will show errors if any also it will list errors in error.log.
If you want to hide the errors from appearing on site then you can set value from "on" to "off".
If you want to hide it only from users and not for developers then you can set "ini_set('display_errors','off');" so these will not visible to users but developers can resolve it from error.log
How should I manage these errors?
You should record them and analyse the logs to resolve them (or at least ensure your site is secure).
Should I save them all in a database?
No - you're going to lose visibility of database connectivity issues. The right way is via the syslog functionality on the local machine.
How do I know which errors could occurr?
? All of them.
Handling errors is one of the most important aspects of an application. The users expects it to work, but when an error occurs their may loose confidence into your application, no matter who good it is. I learned it the hard way.
We use a class similar to the following:
class ErrorHandler
{
public static function registerHandlers()
{
$inst = new ErrorHandler;
set_error_handler(array(&$inst, 'errorHandler'), E_ALL);
set_exception_handler(array(&$inst, 'exceptionHandler'));
register_shutdown_function(array(&$inst, 'shutdownHandler'));
}
public function shutdownHandler()
{
if (($error = error_get_last()))
{
$this->_clearOutputBuffers();
// Handle error
}
}
public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext)
{
$this->_clearOutputBuffers();
// Handle error
}
public function exceptionHandler(Exception $exception)
{
$this->_clearOutputBuffers();
// Handle error
}
private function _getErrorCode($sMessage, $sFile, $nLine, $nCode)
{
$errorCode = sprintf("%u", crc32($sMessage.$sFile.$nLine.$nCode));
}
private function _clearOutputBuffers()
{
if (count(ob_list_handlers()) > 0)
{
ob_clean();
}
}
}
This class is able to catch most errors and works surprisingly well for debugging purposes as well. When ever an error is caught we write all the information to a file that we can reference later. Further we separate our environments between development and production and have separate error screens for it.
For the development environment we use an error screen that displays the extract of the file a stack trace and variables.
For the production environment we display an error screen containing the error number returned from _getErrorCode. If a customer wants to contact us about the error, all he has to do is tell us the number and we can instantly look it up and have all the data in front of us.
I have attached a screenshot of our development error screen.
i call an php pgm per cronjob at different times.
the pgm includes many php-files.
each file sends or gets data from partners.
How can i handle errors in one includes pgm.
at the time, one ftp-connection in an included pgm fails so the complete script crushes.
how can i handle this ?
You should wrap code, which is possible to crash, into try/catch construction. This will throw exeption, but the script will continue to work. More here.
Need to know more about you code inorder to give you definite answer.
In general php errors isn't catchable unless you define your own error handler from which you throw exceptions your self. Using the code below makes most runtime errors catchable (as long as they arent considered fatal)
error_reporing(E_ALL);
set_error_handler(function($errno, $errstr, $errfile, $errline) {
if($errno == E_STRICT || $errno == E_DEPRECATED) {
return true;
}
throw new RuntimeException('Triggered error (code '.$errno.') with message "'.$errstr.'"');
});
Btw, You could also define your own exception handler to display triggered errors with a full stack trace when an exception isn't catched.
Notice! I would not suggest that you add this code to a production website without rigorous testing first, making sure everything still works as expected.
Edit:
I have no idea what your code looks like, but I guess you can do something like:
require 'error-handler.php'; // where you have your error handler (the code seen above)
$files_to_include = array(
'some-file.php',
'some-other-file.php',
...
);
foreach($files_to_include as $file) {
try {
include $file;
}
catch(Exception $e) {
echo "$file failed\nMessage: ".$e->getMessage()."\nTrace:\n".$e->getTraceAsString();
}
}
Everyone says that "Enabling errors to be shown" in an active site is bad (due to some security issues).
Now, we have to consider 2 cases:
The site is in debug mode
The site is not in debug mode
Now, for case #1:
We want to see the errors. How?
ini_set('error_reporting', E_ALL);
ini_set('display_errors', 1);
Nothing more simple. Also we can customize an error handler for all errors except Parse and Fatal.
Instead, if the case is #2:
We would like to be able to deactivate the messages:
ini_set('error_reporting', 0);
ini_set('display_errors', 0);
And it's ok. But what about showing users a friendly message such as "Hei man, something is really f**ked up. I don't assure you we are working to fix it, since we are very lazy.".
You should enable errors again and just use the function set_error_handler() and hope that no parse or fatal errors occur. But my first question is:
Question 1: Is that possible to avoid error reporting and have a custom offline page that is loaded when something goes wrong? I mean, is it possible to have ini_set('error_reporting', 0); and ini_set('display_errors', 0); and still be able to tell PHP to load a custom Error page?
And now another:
Question 2: I developed a class that with the power of set_error_handler() logs errors occurred into the database. In this way I can keep track of hack attempts and other cool stuff. (And yes, i'm always sure the DB is accessible since my application shuts down if we cannot connect to the DB). Is this worth something?
Some time ago I created small system that redirects you to error page when fatal error occurs / uncaught exception was thrown. It was possible with assumption, that every request is handled by one file and ends in this file, so by reaching end of this file I'm sure that everything went OK. With this condition I've set up function to redirect on error page and registered it as shutdown function - so it will be called at the end of all requests. Now in this function I check conditions for clean shutdown and if hey are met, I do nothing and output is flushed to the browser, otherwise buffer is cleaned and only header redirecting to error page is sent.
Simplified version of this code:
<?php
function redirect_on_error(){
if(!defined('EVERYTHING_WENT_OK')){
ob_end_clean();
header('Location: error.html');
}
}
register_shutdown_function('redirect_on_error');
ob_start();
include 'some/working/code.php';
echo "Now I'm going to call undefined function or throw something bad";
undefined_function();
throw new Exception('In case undefined function is defined.');
define('EVERYTHING_WENT_OK', TRUE);
exit;
Question 1: Is that possible to avoid error reporting and have a custom offline page that is loaded when something goes wrong?
Unfortunately, I don't think so, at least for fatal errors. However, recent versions of PHP always send a 500 response when that occurs, so, depending on webserver, you may be able to rewrite the response if such thing happens. If your actual server running PHP is behind a reverse proxy, this becomes trivial with Apache.
Question 2: I developed a class that with the power of set_error_handler() logs errors occurred into the database.
Sure, it's always good to log the errors. You already seem to be aware of the limitations of logging errors into the database.