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.
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
Currently I have an old script that parses emails, as seen here:
// Accessing the mailbox
$mailbox = imap_open("{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX", $mailbox, $mailboxPassword);
// Retrieving only unread messages
$mail = imap_search($mailbox, 'UNSEEN');
// If no new messages found aborting the script
if(empty($mail)) die('No unread emails found!');
$total_found = 0;
$skipped = 0;
// Now we loop through messages
foreach ($mail as $key => $val) {
// process everything
}
This works fine other than some encoding issues with Russian (Cyrillic) characters and a few other issues. While I could hunt down all these issues individually, it seems like there are already great mail parsing classes out there. I found this, which I'd like to use as it sounds like this gets suggested often.
The example code provided is with the parser is below.
<?php
require_once('MimeMailParser.class.php');
$path = 'path/to/mail.txt';
$Parser = new MimeMailParser();
$Parser->setPath($path);
$to = $Parser->getHeader('to');
$from = $Parser->getHeader('from');
$subject = $Parser->getHeader('subject');
$text = $Parser->getMessageBody('text');
$html = $Parser->getMessageBody('html');
$attachments = $Parser->getAttachments();
?>
However it seems to need a reference to $path which is confusing me as the emails are not stored in a folder, there pulled from IMAP. Would I add $path = $mail; in the foreach block? If not, what format do I supply the email to the parser in? Do I have to use the same script I already have and save it to a folder?
All the emails are being retrieved from Gmail. I used IMAP but could use POP instead if IMAP wont work.
Based on the suggested answer i tried this code but its just looping through x unread emails and displaying blank data for everything, headers and body?
// Accessing the mailbox
$mailbox = imap_open("{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX", $mailbox, $mailboxPassword);
// Retrieving only unread messages
$mail = imap_search($mailbox, 'UNSEEN');
// If no new messages found aborting the script
if(empty($mail)) die('No unread emails found!');
$total_found = 0;
$skipped = 0;
// Now we loop through messages
foreach ($mail as $email) {
$Parser = new MimeMailParser();
$Parser->setText($mail);
echo "-----------------------------Start Of Email---------------------------------";
echo "<br /><br /><br /><br />";
$to = $Parser->getHeader('to');
echo "To: " . $to . "<br />";
$from = $Parser->getHeader('from');
echo "From: " . $from . "<br />";
$subject = $Parser->getHeader('subject');
echo "Subject: " . $subject . "<br /><br /><br />";
//$text = $Parser->getMessageBody('text');
$html = $Parser->getMessageBody('html');
echo "Body: " . "<br /><br />" . $html . "<br />";
//$attachments = $Parser->getAttachments();
echo "<br /><br /><br /><br />";
echo "-----------------------------End Of Email---------------------------------";
}
That class has another function to set the message content directly. Just call $Parser->setText($mail) where $mail is the message content in your IMAP foreach loop.
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 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