0

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?

Good Way to Email Exceptions

PHP exceptions error messages

PHP exception inside catch: how to handle it?

user2834566
  • 775
  • 9
  • 22
  • 1
    "returned into an *exception*" but set_*error*_handler. So it works as expected. See [the missing step](https://phpdelusions.net/articles/error_reporting#converting_errors_to_exceptions) – Your Common Sense Jun 15 '20 at 16:45
  • 2
    An easy way would be to use some tried and tested library, like Monolog. That can log all kinds of issues (notices/errors/exceptions etc) and have handlers that can send them as email, log file and more. – M. Eriksson Jun 15 '20 at 16:53
  • @Your Common Sense. Yes I read that. It was what prompted me to use `mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);` to get an exception from mysqli, I thought that I would then be able to use the other exception handling code to send details by email, but not so – user2834566 Jun 15 '20 at 18:08
  • but there is *no exception handling* code in your question. And I gave you a link to a page that doesn't tell you to use mysqli_report, but does show how you can handle both errors and exceptions – Your Common Sense Jun 15 '20 at 18:13

1 Answers1

0

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.

Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
  • Thank you but can you please tell me (a) what does what does `http_response_code(500);` do? and (b) where does echo display its results? Like I said, there is no website, no index.html and the url of the server is known only to me. That's also why I don't think I need be worried about email flooding (might be wrong though) The only thing I can see that might cause an exception is the state of the database when the php runs and that only happen when the cron job fires at 3am every day. – user2834566 Jun 16 '20 at 06:51
  • I replaced my error handler with both of yours and this time the unidentified variable message went to both the error log and was also sent to me by email but the SQL error wasn't reported at all. (previously the variable message was emailed and the sql error went to the log) – user2834566 Jun 16 '20 at 07:02
  • Would it be better if everything goes to the error log and then another cron job sent me the error log by email if its size is ever greater than zero (since my db maintenance PHP is the only thing that can cause an exception, there being no other php around), in which case is it possible to have the code continue after one exception in case there is another one? - In practice I cannot see there ever being an error, as opposed to an exception as its just running the same sql every time. – user2834566 Jun 16 '20 at 07:14
  • Shame on me I completely missed the cron job part. In case of a cron job the solution is ridiculously simple: you just add a `mailto:myemailaddress@myserver.com` to your crontab, and any output from the job sill be emailed to you. Hence you don't need any error handlers at all. Just make sure that errors are displayed, with `ini_set('display_errors',1);` and you are done. Assuming your script doesn't normally output anything, there will be output only in case of error, and it will be emailed to you by the cron daemon. Sounds a deal? – Your Common Sense Jun 16 '20 at 07:42
  • That's simpler. What is a crontab? I've seen it before but not explained. I set up the cron job using cpanel and just needed to enter the command to run the initial php function. (Also,the end of your article has a confusing line "Here, we define a common _error_ handling function MyExceptionHandler() which encapsulates all the error handling logic. Then, we set up the _exception_ handler that calls our function when an uncaught exception appears." But it looks like the reverse, the _exception_ handler is called MyExceptionHandler and it is called by your _error_ handler) Have I misunderstood? – user2834566 Jun 16 '20 at 08:09
  • OK,found out what a crontab is (I should have remembered as I taught Unix about 30 years ago, maybe it wasn't called crontab then!) But I won't have access to that file as, like I said, I'm on a shared server and really only have access to cpanel and to certain server folders, mainly to do with making web sites. I'll accept your answer as, although I still can't get it to email all errors I can tweak it to email the mysqli ones which I am really interested in. – user2834566 Jun 16 '20 at 09:15