I have a strange problem with regards to a custom error handler function that deals with fatal errors. The error on my website is an "Fatal error: Allowed memory size of 134217728 bytes exhausted" - which of course I will fix. But I want a custom error page to show if it happens again.
The following code echo's number 1, but not my "error-page.php" and does not echo number 2. So it feels like the script is erroring on the file_get_contents line - but it's not telling me why.
To make matters worse - it echo's fine on my development server! But I cannot think of a set up difference that would effect this.
To try and solve this I have echo'd out the full path for the file_get_contents and it is correct - and file_exists returns true - so I am utterly stumped!
Worth noting - error-page.php is just a static HTML page so no offending code in that! It won't even file_get_contents a test.txt.
error_reporting(-1);
set_error_handler("customErrorPage");
register_shutdown_function('shutdownFunction');
function shutdownFunction()
{
$last_error = error_get_last();
if ($last_error['type'] === E_ERROR) {
fatalErrorHandler(E_ERROR, $last_error['message'], $last_error['file'], $last_error['line'], "");
}
}
function fatalErrorHandler($error_level,$error_message,$error_file,$error_line,$error_context) {
http_response_code(500);
$errorEmail = "<strong>Error on page:</strong> http://" . $_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"] . "<br />\n";
$errorEmail .= "<strong>Message:</strong> " . $error_message . "<br />\n";
$errorEmail .= "<strong>IP:</strong> " . getRealIpAddr() . "<br />\n";
$errorEmail .= "<strong>Line:</strong> " . $error_line . "<br />\n";
$errorEmail .= "<strong>File:</strong> " . $error_file . "<br />\n";
if ((strpos($_SERVER['SERVER_NAME'], ".com") > 0)) {
$to = "james#email.com";
$subject = "FATAL Error on " . $_SERVER["SERVER_NAME"] . " - " . $error_message;
$headers = "MIME-Version: 1.0'" . "\r\n";
$headers .= "Content-type: text/html; charset=iso-8859-1". "\r\n";
$headers .= "From: " . WEBSITE_COMPANY . " <errors#" . WEBSITE_DOMAIN . ">" . "\r\n";
mail($to, $subject, $errorEmail, $headers);
echo "1";
echo file_get_contents($_SERVER["DOCUMENT_ROOT"] . "includes/error-page.php");
echo "2";
die;
} else {
echo $errorEmail;
die;
}
}
This is what I see on my screen when running the script:
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried
to allocate 14907 bytes) in
/home/sure-wise/htdocs/objects/myaccount.php on line 2666 1
Related
I've been using error_get_last() in combination with register_shutdown_function() to create an error report for some of my scripts. It's been working fine for the most part.
Every now and then, though, the error report will show an error from a different script.
I'm not quite sure how that can happen. Can anyone explain?
I'm wondering if error_get_last() in one script should be able to show errors from different scripts and if that's the case, under what circumstances would that happen?
My only guess is that the error is saved for a short time so that a script that finishes execution at almost the same time might catch another script's errors. Could that be it?
This is the code I use and have included in many different PHP files:
function send_error_report($extra) {
global $conn;
$base_url = ( isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']=='on' ? 'https' : 'http' ) . '://' . $_SERVER['HTTP_HOST'];
$url = $base_url . $_SERVER["REQUEST_URI"];
$error = error_get_last();
if (isset($error)) {
$error_str = "Type: " . $error['type'] . " | Message: " . $error['message'] . " | File: " . $error['file'] . " | Line: " . $error['line'];
$sql = "INSERT INTO `error_report` (`script`, `error`) VALUES ('$url', '" . $error_str . " - " . $extra . "');";
$conn->query($sql);
}
}
register_shutdown_function('send_error_report', '');
I am using Bennett Stone php wrapper class for mysqli operations. class is working fine without any issues. There is a function inside it (shared below) which sends an email to administrator for any kind of mysqli errors.
Issue
There are 100's of scripts using this class, I am not able to find out which script thrown any particular error.
What i want
I want to get the full path of error originating script inside class from where query originated. for example, test.php contains query & post execution if any error comes out then i want complete path of test.php inside error handling function along with error details (shared below).
What i can do
Class modification I can do by my own
I am getting details like this:
Error at 2016-08-23 05:44:18:
Query: SELECT email FROM users WHERE
Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
Error handling function
public function log_db_errors( $error, $query )
{
$message = '<p>Error at '. date('Y-m-d H:i:s').':</p>';
$message .= '<p>Query: '. htmlentities( $query ).'<br />';
$message .= 'Error: ' . $error;
$message .= '</p>';
if( defined( 'SEND_ERRORS_TO' ) )
{
$headers = 'MIME-Version: 1.0' . "\r\n";
$headers .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n";
$headers .= 'To: Admin <'.SEND_ERRORS_TO.'>' . "\r\n";
$headers .= 'From: systems<noreply#'.$_SERVER['SERVER_NAME'].'.com>' . "\r\n";
mail( SEND_ERRORS_TO, 'Database Error', $message, $headers );
}
else
{
trigger_error( $message );
}
if( !defined( 'DISPLAY_DEBUG' ) || ( defined( 'DISPLAY_DEBUG' ) && DISPLAY_DEBUG ) )
{
echo $message;
}
}
Put these lines somewhere above all your codes:
ini_set('display_errors', 0);
ini_set('log_errors', 1);
error_reporting(E_ALL);
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
and you will have every error logged with a stack trace. While all this bushy "log_db_errors" stuff will be thrown away.
the problem solved.
NB. This wrapper class is just terrible. Utilizes almost every bad practice ever exists.
What you can do is catching exception and edit error message, using some magic php const in it : PHP: Magic constants
Example, in your class, around the query :
try {
// your query
} catch (Exception $e) {
// launch your log_db_errors() function, with some of const, like __CLASS__, __LINE__, etc :
$error = '[' . __FILE__ . '][' . __CLASS__ . '::' . __FUNCTION__ . '][' . __LINE__ . '] : ' . $e->getMessage();
// ...
}
I'm trying to add a basic new-grabber for a site I'm making and cannot figure out for the life of me what is causing this error. The file I'm grabbing is a plain-text file, completely accessible.
I've seen it posted before and the OP is calling something like:
$var = $data[str] instead of $var = $data['src']
But I am not calling anything with "code" in the name.
I receive this error upon running my code:
HTTP request failed. Error 8 on line 123: Use of undefined constant
code - assumed 'code' in file /usr/local/lib/php/head.php
Here is my entire file below:
<?
$e_news = file_get_contents("http://cinemattson.com/templates/flickfeed/news.txt");
if (!$e_news === true) {
$error = error_get_last();
echo "HTTP request failed. Error " . $error['type'] . " on line " . $error['line'] . ": " . $error['message'] . " in file " . $error['file'] . "<br>";
} else {
echo "Everything went better than expected";
}
if ($e_news === true) {
$news = explode("|", $e_news);?>
<h4>News - <? echo (!empty($news) ? $news[1] : "v0.0.1");?> <small><? echo (!empty($news) ? $news[0] : "5/22/2016");?></small></h4>
<p><? echo (!empty($news) ? $news[2] : "Loading news failed, or there is currently no news.");?></p>
<?
} else {
echo "<h4>News failed to load</h4>";
}
?>
Do you guys know what I'm missing or doing wrong here?
As already suggested by #John Stirling, "the issue is elsewhere".
More precisely the reported Error 8 on line 123... etc is related to an error that happened previously, elsewhere.
And your current code is responsible to make this error appear now because you wrote:
$e_news = file_get_contents("http://cinemattson.com/templates/flickfeed/news.txt");
if (!$e_news === true) {
$error = error_get_last();
This way, the following happens:
Each time file_get_contents() is successfull, $e_news gets its content.
Then $e_news === true is FALSE (even if this content is empty, because you used ===), and if (!$e_news === true) is always TRUE.
So there is no error now, and your error_get_last() gets the trace of the last error that previously happened, elsewhere...
In fact, for your code to work as expected you should rather do something like this:
$e_news = file_get_contents("http://cinemattson.com/templates/flickfeed/news.txt");
if ($e_news === false) {
$error = error_get_last();
echo "HTTP request failed. Error " . $error['type'] . " on line " . $error['line'] . ": " . $error['message'] . " in file " . $error['file'] . "<br>";
echo "<h4>News failed to load</h4>";
} else {
echo "Everything went better than expected";
$news = explode("|", $e_news);?>
<h4>News - <? echo (!empty($news) ? $news[1] : "v0.0.1");?> <small><? echo (!empty($news) ? $news[0] : "5/22/2016");?></small></h4>
<p><? echo (!empty($news) ? $news[2] : "Loading news failed, or there is currently no news.");?></p>
<?
}
I've written the following custom Exception handler:
namespace System\Exception;
class Handler extends \Exception {
public static function getException($e = null) {
if (ENVIRONMENT === 0 && is_object($e)) {
$message = "<p>";
$message .= "Exception: " . $e->getMessage();
$message .= "<br />File: " . $e->getFile();
$message .= "<br />Line: " . $e->getLine();
$message .= "<br />Trace: " . $e->getTrace();
$message .= "<br />Trace as string: " . $e->getTraceAsString();
$message .= "</p>";
} else {
$message = '<h1>Exception</h1>';
$message .= '<p>There was a problem.</p>';
}
#require_once('header.php');
echo $message;
#require_once('footer.php');
exit();
}
public static function getError($errno = 0, $errstr = null, $errfile = null, $errline = 0) {
if (ENVIRONMENT === 0) {
$message = "<p>";
$message .= "Error: " . $errstr;
$message .= "<br />File: " . $errfile;
$message .= "<br />Line: " . $errline;
$message .= "<br />Number: " . $errno;
$message .= "</p>";
} else {
$message = '<h1>Error</h1>';
$message .= '<p>There was a problem.</p>';
}
#require_once('header.php');
echo $message;
#require_once('footer.php');
exit();
}
public static function getShutdown() {
$last_error = error_get_last();
if ($last_error['type'] === E_ERROR) {
self::getError(E_ERROR, $last_error['message'], $last_error['file'], $last_error['line']);
}
}
}
and have indicated that I want to use this class and its methods for processing all exceptions and errors generated by the system in the following way:
set_exception_handler(array("System\Exception\Handler", "getException"));
set_error_handler(array("System\Exception\Handler", "getError"), -1 & ~E_NOTICE & ~E_USER_NOTICE);
register_shutdown_function(array("System\Exception\Handler", "getShutdown"));
I have also indicated that I don't want errors to be displayed on the screen and wand to report all of the errors:
ini_set('display_errors', 'Off');
error_reporting(-1);
My question now is - do I still need to use the try { } catch () { } statement in order to catch any exceptions and errors? I know that the above is most probably not a bullet proof, but seem to be working so far without any try / catch statement by processing all uncaught exceptions and errors.
Also - is there any disadvantage by using custom exception handler and letting it catch all uncaught exceptions rather than doing this via try {} catch (i.e. performance / security etc.)?
You don't have to, but you cannot recover - using try/catch gives you the advantage of reacting to particular exception (for example file not found in some_custom_session_handling() might be a good place to use try/catch and log out such user without session file).
So the advantage is that you have prettier messages. The downside is that you treat exceptions always the same. It's not bad in itself, and should not degradate performance or security, but it misses the point of using exceptions in the first place.
However, it does not exclude using try/catch where you might want them, so I'd say it's a good failover solution, but should be avoided as a try/catch replacement
As jderda said, be using your aproach you are missing the point of exceptions: to check for any errors in the upper levels of your code and react to them - halt or treat the exception and move on. Your aproach is fine when you want to, for example, log all uncaught exceptions
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.