I am handling errors with the following script:
<?php # config.inc.php
// This script establishes email default settings.
// This script determines how errors are handled.
// Email Settings
$site['from_name'] = 'x'; // from email name
$site['from_email'] = 'x#x.com'; // from email address
// Just in case we need to relay to a different server,
// provide an option to use external mail server.
$site['smtp_mode'] = 'enabled'; // enabled or disabled
$site['smtp_host'] = 'mail.x.com';
$site['smtp_port'] = null;
$site['smtp_username'] = 'admin#x.com';
$site['smtp_password'] = 'x';
// Error handling:
// Flag variable for site status:
$live = TRUE;
ini_set('display_errors','On');
error_reporting(E_ALL);
// Error log email address:
$admin_email = 'x#x.com';
date_default_timezone_set('America/New_York');
// Create the error handler.
function my_error_handler ($e_number, $e_message, $e_file, $e_line, $e_vars) {
global $live, $admin_email;
// Build the error message.
$message = "An error occurred in script '$e_file' on line $e_line: \n<br />$e_message\n<br />";
// Add the date and time.
$message .= "Date/Time: " . date('n-j-Y H:i:s') . "\n<br />";
// Append $e_vars to the $message.
$message .= "<pre>" . print_r ($e_vars, 1) . "</pre>\n<br />";
if ($live) { // Don't show the specific error.
error_log ($message, 1, $admin_email); // Send email.
// Only print an error message if the error isn't a notice.
if ($e_number != E_NOTICE) {
echo '<div id="Error">A system error occurred. An administrator has been notified. We apologize for the inconvenience.</div><br />';
}
} else { // Development (print the error).
echo '<div id="Error">' . $message . '</div><br />';
}
} // End of my_error_handler() definition.
// Use my error handler.
set_error_handler ('my_error_handler');
?>
If I include this script in a script with an error, the error does not get displayed. If I comment out the call to set_error_handler(), the error does get displayed. What am I doing wrong that could be causing this behavior?
From the docs http://php.net/manual/en/function.set-error-handler.php
It is important to remember that the standard PHP error handler is
completely bypassed for the error types specified by error_types
unless the callback function returns FALSE.
Make your handler return FALSE
Related
Version 7.2.0 deprecated an highly useful bit of information for debugging in error_handler: the errcontext (the 5th parameter) values, which gave the value of variables in the scope of the error. Without that I can get only the file, line and error message, but that doesn't help if the error is a result of the actual values being processed. So the question is: "is there a way to get the values of the variables in a post 7.2.0 world?"
For background, I am using the Larry Ullman (famous php author) custom error handler, but I can no longer use the 5th parameter ($e_vars), below.
======
function my_error_handler ($e_number, $e_message, $e_file, $e_line, $e_vars) {
global $live, $email_errors;
$message = "An error occurred in script '$e_file' on line $e_line: \n<br />$e_message\n<br />";
$message .= "Date/Time: " . date('n-j-Y H:i:s') . "<br />";
$message .= "<pre>" . print_r ($e_vars, 1) . "</pre><br />";
if ($live) { // Don't show the specific error.
error_log ($message, 1, $email_errors,"From:errors#domain.com"); // Send email.
// Only print an error message if the error isn't a notice.
if ($e_number != E_NOTICE) {
echo '<div id="Error">A system error occurred. We apologize for the inconvenience.</div><br />';
}
} else { // Development (print the error).
echo '<div id="Error">' . $message . '</div><br />';
}
}
set_error_handler ('my_error_handler');
======
I have code that causes 2 errors, 1 from the php function pg_query_params because I've input an invalid query, and the Exception that I throw when the result of that function is false:
if (!$res = pg_query_params($this->sql, $this->args)) {
// note pg_last_error seems to often not return anything
$msg = pg_last_error() . " " . $this->sql . PHP_EOL . " Args: " . var_export($this->args, true);
throw new \Exception("Query Execution Failure: $msg");
}
Then I have error handler code which logs the errors and is supposed to echo them. Both errors are logged, but only the last (the thrown exception) is echoed. I'd like both echoed, as the first contains helpful debugging info. I don't understand why both aren't, as I've done some debugging and echo is called for both errors. Is it something related to output buffering or a concurrency issue?
Here is a shortened version of my error handler code. The throwableHandler method is registered with set_exception_handler() and the phpErrorHandler method with set_error_handler(). I haven't included generateMessageBodyCommon() but it simply adds error info to the message body:
private function handleError(string $messageBody, int $errno)
{
// echo
if ($this->echoErrors) {
$messageBody .= 'inside echo'; // this goes into the log file for both errors
echo nl2br($messageBody, false);
}
// log
#error_log($messageBody, 3, $this->logPath);
}
public function throwableHandler(\Throwable $e)
{
$message = $this->generateMessageBodyCommon($e->getCode(), $e->getMessage(), $e->getFile(), $e->getLine());
$message .= PHP_EOL . "Stack Trace:" . PHP_EOL . $e->getTraceAsString();
$this->handleError($message, $e->getCode(), $exitPage);
}
public function phpErrorHandler(int $errno, string $errstr, string $errfile = null, string $errline = null)
{
$message = $this->generateMessageBodyCommon($errno, $errstr, $errfile, $errline) . PHP_EOL . "Stack Trace:". PHP_EOL . $this->getDebugBacktraceString();
$this->handleError($message, $errno, false);
}
Be careful with '#' -> #error_log($messageBody, 3, $this->logPath);
PHP supports one error control operator: the at sign (#). When prepended to an expression in PHP, any error messages that might be generated by that expression will be ignored.
http://php.net/manual/en/language.operators.errorcontrol.php
Error level :
error_reporting(E_ALL);
http://www.php.net/manual/en/function.error-reporting.php
error_reporting — Sets which PHP errors are reported
I am getting the below error in one of the file in Production, where the function is defined twice. I tried to recreate the issue, getting in a different file.
Fatal error: Cannot redeclare foo() (previously declared in
/home/content/45/8989001/html/test/test.php:5) in
/home/content/45/8989001/html/test/test.php on line 10
To suppress this error, its advised to make an entry to php.ini file, but I don't have access to it as its shared hosting.
Alternatively, its suggested to make an entry to existing php file inside the <?php ?> tags. I did the below change.
error_reporting(0); // This is not working, still error is displayed
error_reporting(E_NONE); // This is not working, still error is displayed
ini_set('display_errors', 0); // This is not working, still error is displayed
My complete code,
<?php
function foo() {
return "foo";
}
function foo() {
return "foo";
}
// error_reporting(0); // This is not working, still error is displayed
// error_reporting(E_NONE); // This is not working, still error is displayed
ini_set ( 'display_errors', 0 ); // This is not working, still error is displayed
echo "hello";
?>
Problem: How to suppress this error in Production, instead log to some file. or at-least suppress the error?
Note: The error is fixed in prod, but how to suppress it to avoid user seeing error in some other file next time
Update1:
After the below change too, the error is the same.
<?php
ini_set ( 'display_errors', 0 );
function foo() {
return "foo";
}
function foo() {
return "foo";
}
// error_reporting(0); // This is not working, still error is displayed
// error_reporting(E_NONE); // This is not working, still error is displayed
// ini_set ( 'display_errors', 0 ); // This is not working, still error is displayed
echo "hello";
?>
Update2:
<?php
register_shutdown_function( "fatal_handler" );
function fatal_handler() {
echo "inside fatal_handler";
$errfile = "test";
$errstr = "shutdown this test";
$errno = E_CORE_ERROR;
$errline = 0;
$error = error_get_last();
echo $error;
if( $error !== NULL) {
$errno = $error["type"];
$errfile = $error["file"];
$errline = $error["line"];
$errstr = $error["message"];
$newline = "\r\n";
define ( 'senderName', 'Error' );
define ( 'senderEmail', 'admin#abc.com' );
$headers = "From: " . senderName . " <" . senderEmail . "\r\n";
$subject_admin = 'Error-Fix it';
$body_admin = "Dear Admin, $newline$newline An error occured $newline" . "error number : " . $errno . $newline . " error file : $errfile" . $newline . "error line :" . $errline . $newline . "error string : $errstr" . $newline;
$body_footer = " **** This is an an auto-generated mail. kindly do not reply to this mail. Thank You. ****" . $newline . $newline;
$body_admin = $body_admin . $newline . $newline . $newline . $body_footer;
$to_admin1 = 'mymail#gmail.com';
mail ( $to_admin1, $subject_admin, $body_admin, $headers );
//error_mail(format_error( $errno, $errstr, $errfile, $errline));
}
}
function foo() {
return "foo";
}
function foo() {
return "foo";
}
// error_reporting(0); // This is not working, still error is displayed
// error_reporting(E_NONE); // This is not working, still error is displayed
// ini_set ( 'display_errors', 0 ); // This is not working, still error is displayed
echo "hello";
?>
update 3
Still getting same error
<?php
register_shutdown_function ( "fatal_handler" );
function fatal_handler() {
echo 'YAY IT WORKED';
}
function foo() {
return "foo";
}
function foo() {
return "foo";
}
// error_reporting(0); // This is not working, still error is displayed
// error_reporting(E_NONE); // This is not working, still error is displayed
// ini_set ( 'display_errors', 0 ); // This is not working, still error is displayed
echo "hello";
?>
Update4
<?php
//error_reporting(0);
function fatalErrorHandler() {
echo 'YAY IT WORKED';
}
# Registering shutdown function
register_shutdown_function('fatalErrorHandler');
// let force a Fatal error -- function does not exist
functiontest();
echo "hello";
?>
<!--
output:
Fatal error: Call to undefined function functiontest() in ...test2.php on line 12
YAY IT WORKED -->
<?php
error_reporting(0);
function fatalErrorHandler() {
echo 'YAY IT WORKED';
}
# Registering shutdown function
register_shutdown_function('fatalErrorHandler');
// let force a Fatal error -- function does not exist
functiontest();
echo "hello";
?>
<!-- output:
YAY IT WORKED -->
Final update:
This resolved my intermediate question
register_shutdown_function is not getting called
If I were you I would call my host and ask them to make the changes to my phpini. They may have special things setup.
Now you should never just hide errors. You send yourself an email and fix right away. The error you are trying to surpress is fatal and the script will no longer run, so hiding it only results in a blank page, the user stkill cannot continue.
It is a fatal error and you cannot recover from it. The way to hide fatal error properly in my opinion is to create your own error handler function.
And you would use something like this to catch the fatal errors.
http://php.net/manual/en/function.register-shutdown-function.php
The function needs to be on every page in order to catth the error. I have a error.php that I require in my db class, which is on every page.
//This function gets called every time your script shutsdown
register_shutdown_function( "fatal_handler" );
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"];
//send error email
error_mail(format_error( $errno, $errstr, $errfile, $errline));
}
}
UPDATE
**OP said the function is not being called. **
The above should work.
Here is basically what I use in production, it should tottally work.
ON_SCREEN_ERRORS and SEND_ERROR_EMAIL are my own config constants for development.
//Set error handler function (See function below)
set_error_handler("StandardErrorHandler");
/**
* Will take an error string and if ON_SCREEN_SQL_ERRORS is set to on, it will display the error on the screen with the SQL in a readable format and
* if SEND_SQL_ERROR_EMAILS is set to on, it will dendthe error email with the SQL in a readable format.
*
* PHP will pass these parameters automatically on error.
*
* #param string $errno The error type code.
* #param string $errstr The error string.
* #param string $errfile The file that the error occured in.
* #param string $errline The line number that the error occured.
*/
function StandardErrorHandler($errno, $errstr, $errfile, $errline) {
$Err = $errstr.'<br />'.GetErrorType($errno).'on line '.$errline.' in '.$errfile.'<br />';
if (ON_SCREEN_ERRORS===TRUE)
{
err($Err);
}
if ($errno =='256' and SEND_ERROR_EMAILS === TRUE)
{
$Path = "http://". $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
// Custom error function
gfErrEmail($Err, $Path, 'SQL Error');
}
}
//Set the Fatal Error Handler function (See function below)
register_shutdown_function("FatalErrorHandler");
/**
* This function gets called on script shutdown, it will check if the last error is a fatal error. You cannot catch fatal errors,
* but using this function we will be notified about it and be able to fix it.
* If error is fatal, and if ON_SCREEN_FATAL_ERRORS is set to ON, this function will display the fatal error on the screen.
* If error is fatal, and if SEND_FATAL_ERROR_EMAILS is set to ON, this function will send error email.
*
*/
function FatalErrorHandler() {
$error = error_get_last();
if($error !== NULL) {
//check if error is of fatal, compliler or other non recoverable error types
if ($error["type"] =='1' || $error["type"] =='4' || $error["type"] =='16' || $error["type"] =='64' || $error["type"] =='4096')
{
$errno = GetErrorType($error["type"]);
$errfile = $error["file"];
$errline = $error["line"];
$errstr = $error["message"];
$Err = '<strong>'.$errno.'<br/></strong>'.$errstr.'<br />'.$errno.' on line '.$errline.' in '.$errfile.'<br />';
if (ON_SCREEN_ERRORS===TRUE)
{
err($Err);
}
if (SEND_ERROR_EMAILS === TRUE)
{
$Path = 'http://'. $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
//Custom function
gfErrEmail($Err, $Path, $errno);
}
}
}
}
/**
* This function receives the error code and returns the specified string.
* The return strings are what the error message will display.
*
* #return string The error title
*/
function GetErrorType($Type)
{
switch($Type)
{
case 1:
// 'E_ERROR';
return 'Fatal Error ';
case 2:
// 'E_WARNING';
return 'Warning ';
case 4:
// 'E_PARSE';
return 'Compile Time Parse Error ';
case 8:
// 'E_NOTICE';
return 'Notice ';
case 16:
// 'E_CORE_ERROR';
return 'Fatal Start up Error ';
case 32:
// 'E_CORE_WARNING';
return 'Start Up Warning ';
case 64:
//'E_COMPILE_ERROR';
return 'Fatal Compile Time Error ';
case 128:
// 'E_COMPILE_WARNING';
return 'Compile Time Warning ';
case 256 :
// 'E_USER_ERROR' - USED FOR SQL ERRORS - DO NOT USE THIS ERROR CODE to TRIGGER_ERROR()
return 'SQL Error ';
case 512:
// 'E_USER_WARNING';
return 'Warning - Thrown using trigger_error() ';
case 1024:
// 'E_USER_NOTICE';
return 'Notice - Thrown using trigger_error() ';
case 2048:
// 'E_STRICT';
return 'Strict Error (PHP suggest changes to your code which will ensure the best interoperability and forward compatibility of your code.) ';
case 4096:
// 'E_RECOVERABLE_ERROR';
return 'Catchable Fatal Error (This error can be caught, use a Try Catch) ';
case 8192:
// 'E_DEPRECATED';
return 'Warns you of depreciated code that will not work in future versions of PHP. ';
case 16384:
// 'E_USER_DEPRECATED';
return 'Programmer Triggered Error - Thrown using trigger_error() ';
}
return "Error Type Undefined ";
}
I am aware of the set_error_handler function.. But I am wondering if it's possible to set a custom error handler for certain user defined functions? Example.
function ArrayToString ($Array){
if (!is_array($Array)){
trigger_error("Not Array", Not_Array); // I know this is invalid syntax but an example
}
return implode (" ",$Array);
}
Then something like:
set_handler_Not_Array(){
//Format with some CSS
echo "The Argument supplied within ".$errline." <br> Inside: ".$errfile." Is not a valid Array";
exit;
}
Is a solution like this possible? Or would I do I have to resort to the current error handling that is in use:
function Error_Handler($errno, $errstr,$error_file,$error_line)
{
$MySQLCheck = "mysql_connect()"; // String to search for
$MySQL = strpos($errstr, $MySQLCheck); // Looks inside the $errstr for the string $MySQLCheck
if ($MySQL !== false) // if Contains the $MySQLCheck
{
unset($errstr); // Unsets the current error message (which usually states username#host
$errstr = "Can Not Connect To Database"; // Changes the error message to something thatdoes not give away username or host.
}
$UndefinedIndex = "Undefined index:"; // String to search for
$Index = strpos($errstr, $UndefinedIndex); // Searches $errstr for the String listed above
if ($Index !== false) // If contains the $UndefinedIndex
{
$errstrbkup = $errstr; // Saves $errstr to another variable before the $errstr is changes
$str = str_replace("Undefined index:", "", $errstrbkup); // replaces "Undefined Index:" with nothing
$errstr = "A Button Does not Seem To Be Handled Correctly! <br> <b>Button Name:</b> $str "; // Changes the $errstr to a new message
}
$UndefinedIndex = "Undefined variable:"; // String to search for
$Variable = strpos($errstr, $UndefinedIndex); // Searches $errstr for the String listed above
if ($Variable !== false) // If contains the $UndefinedIndex
{
$errstrbkup = $errstr; // Saves $errstr to another variable before the $errstr is changes
$str = str_replace("Undefined variable:", "", $errstrbkup); // replaces "Undefined Index:" with nothing
$errstr = "A Variable Does Not Seem To Be Handled. Check $str"; // Changes the $errstr to a new message
}
$ErrorFile = str_replace("/home/u394942373/public_html/", "", $error_file); // Removes the /var/www from showing in the error
// remove the full path up to the file root if detected
echo
"Opps, It Seems An error Has Been Encountered <br>
Technical Information Listed Below: <br>
<br><br><br>
<b>Error Message:</b> $errstr <br>
<b>File Causing Error:</b> $ErrorFile<br>
<b>Line:</b> $error_line <br>
A Member Of The Technical Department Has Been Notified And Will Fix This Error When Available.
";
die();
}
//set error handler
set_error_handler("Error_Handler");
The reason for this, is because my line manager wishes to have a custom error handler for certain types of error messages... Or for every new error thrown would we still have the be modifying the current error handler
I am using the following error handling function, which emails the error to $admin_email if the site is live ($live==TRUE). My host now requires SMTP authentication. Am I correct in assuming I must remove the call to error_log() and send mail using either the PEAR mail package or PHPMailer?
// Create the error handler.
function my_error_handler ($e_number, $e_message, $e_file, $e_line, $e_vars) {
global $live, $admin_email;
// Build the error message.
$message = "An error occurred in script '$e_file' on line $e_line: \n<br />$e_message\n<br />";
// Add the date and time.
$message .= "Date/Time: " . date('n-j-Y H:i:s') . "\n<br />";
// Append $e_vars to the $message.
$message .= "<pre>" . print_r ($e_vars, 1) . "</pre>\n<br />";
if ($live) { // Don't show the specific error.
echo('<p>sending email</p>');
error_log ($message, 1, $admin_email); // Send email.
// Only print an error message if the error isn't a notice.
if ($e_number != E_NOTICE) {
echo '<div id="Error">A system error occurred. An administrator has been notified. We apologize for the inconvenience.</div><br />';
}
} else { // Development (print the error).
echo '<div id="Error">' . $message . '</div><br />';
}
return FALSE;
} // End of my_error_handler() definition.
The mail function does not use authorization. I think on windows you can set php to use smtp by default in the php.ini file.