How can I get all PHP exceptions, both ones generated by mysqli and normal php ones to be sent to me by email?
The problem environment
I am using a shared server at my ISP and don’t have access to any admin type PHP configuration files. On the server I have just a hundred or so lines of PHP that performs a query against a MySQL database and if any rows get returned, it executes a few SQL UPDATE commands. This is run every day as a cron job. There is no associated web site (I don’t even have a web site on that server). The server hosts a db used for remote connections and the php simply performs some maintenance on it it.
Since there is no ‘user’ and no ‘web page’ to give feedback to I want all errors and exceptions emailed to me.
Things I have tried / researched
I found out that
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
will convert mysqli errors that would normally result simply in FALSE being returned into an exception that will be written to the error log. That helps.
… that I can email text using eg
$email = "my message"; Error_log($email, 1, "myemailaddress@myserver.com", "From: defaultemail@hostingserver.com");
and that I can get the details of the error by writing my own error handler eg
function the_error_handler($number, $message, $file, $line, $vars) //putting $vars in shows all the variables the server knows about { $email = " An error ($number) occurred on line $line in $file. $message "; Error_log($email, 1, "myemailaddress@myserver.com", "From: defaultemail@hostingserver.com"); };
and setting it using
set_error_handler('the_error_handler');
minimum, reproducible example
In the code below I have put all this together and deliberately introduced two errors (trying to echo a variable that doesn’t exist and trying to execute a query on a table that doesn’t exist.
<?php function the_error_handler($number, $message, $file, $line, $vars) //putting $vars in shows all the variables the server knows about(inluding passwords) { $email = " An error ($number) occurred on line $line in $file. $message "; Error_log($email, 1, "myemailaddress@myserver.com", "From: defaultemail@hostingserver.com"); }; set_error_handler('the_error_handler'); echo $NonExsitentVariable; //<--deliberate error mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); $servername = "65.78.165.199"; $username = "test_user"; //execute, select, update and create temp files rights $password = "U7frgh%^yT"; $dbname = "devlopment_db"; $the_sql = "SELECT * FROM non_existent_table;"; //<-- another deliberate error $conn = mysqli_connect($servername, $username, $password, $dbname); // Create connection $result = mysqli_query($conn, $the_sql); //run the query and get a pointer to the result $rows = mysqli_fetch_all($result, MYSQLI_ASSOC); //convert to an associative array of rows to send back ?>
Result
The result is that the non existent variable error gets emailed to me OK but the mysqli error just gets put into the error log on the server and not emailed.
How can I get both, or all errors, sent to me by email? (Obviously the real code is a bit more complex and would confuse the issue if posted in full here but the principle is the same if I can understand how to deal with this simpler example)
Background
I have been programming and lecturing in computing for around 35 years until retiring a while back but have never had the need (or wish!) to program in PHP. This is one of my first scripts and likely to be my last so if you are able to help please be reasonably explicit in what I need to do as I’m not too familiar with the PHP syntax or programming environment)
Some SO posts I’ve looked at
With PHP and mysqli, is it possible to catch all mysqli errors in one place across all PHP scripts?
PHP exception inside catch: how to handle it?
Advertisement
Answer
First of all I must warn you that on a live site that is open to public, emailing errors could flood your mailbox in case of some critical failure when every request to the site causes a distinct email to be sent. It happened not once to the companies I worked for. And sometimes it’s hard to find the actual cause in the waterfall of the errors.
Monitoring server logs is considered much safer and accurate solution. Using Grep on the error log proved to me much more efficient than browsing emails. But either way, the question here is how to handle all errors uniformly, while the action taken in the handler could be anything.
So just like it shown in my article on error reporting basics, errors and exceptions are different beasts and each has its own handler. Hence, to have the error processing uniform you have to convert errors to exceptions first, and then do the required processing in the exception handler, whatever it will be – logging, emailing or anything.
As such, you will need two functions featured in the article, the error handler that simply transfers errors into exceptions,
set_error_handler(function ($level, $message, $file = '', $line = 0) { throw new ErrorException($message, 0, $level, $file, $line); });
and the exception handler like this
set_exception_handler(function ($e) { error_log($e); http_response_code(500); if (ini_get('display_errors')) { echo $e; } else { error_log($e, 1, "myemailaddress@myserver.com", "From: defaultemail@hostingserver.com"); echo "<h1>500 Internal Server Error</h1> An internal server error has been occurred.<br> Please try again later."; } });
that will send you an email every time an error occurs and the code is run on a live site.
Also, you would probably like to add a fatal error handler which could be instrumental in handling the eponymous errors. The full example of all three handlers combined together can be seen in the article.
Regarding mysqli, you are absolutely right, the following line
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
will tell mysqli to throw exceptions, which will be processed by the handler defined above.