Can I try / catch warnings?

Keywords: PHP DNS

I need to catch some warnings thrown from PHP native functions and process them.

Especially:

array dns_get_record  ( string $hostname  [, int $type= DNS_ANY  [, array &$authns  [, array &$addtl  ]]] )

When a DNS query fails, it will raise a warning.

try / catch does not work because warnings are no exception.

I now have two choices:

  1. Set_error_handler seems to have some set_error_handler because I have to use it to filter every warning on the page (is this true?);

  2. Adjust the error report/display so that these warnings do not appear on the screen, then check the return value; if false, no record of the host name can be found.

What are the best practices here?

#1st floor

A really effective solution is to set up a simple error handler using the E_WARNING parameter, as follows:

set_error_handler("warning_handler", E_WARNING);
dns_get_record(...)
restore_error_handler();

function warning_handler($errno, $errstr) { 
// do something
}

#2nd floor

You should probably try to completely eliminate the warning, but if that's not possible, you can add @ (i.e. @dns_get_record(...) before the call, and then use any information you can get to determine if the warning occurred or not.

#3rd floor

Setting and Restoring Error Handlers

One possibility is to set up your own error handler before invoking it and later use restore_error_handler() to restore the previous error handler.

set_error_handler(function() { /* ignore errors */ });
dns_get_record();
restore_error_handler();

You can build on this idea and write reusable error handlers to record errors for you.

set_error_handler([$logger, 'onSilencedError']);
dns_get_record();
restore_error_handler();

Turn errors into exceptions

You can use the set_error_handler() and ErrorException classes to convert all php errors to exceptions.

set_error_handler(function($errno, $errstr, $errfile, $errline, $errcontext) {
    // error was suppressed with the @-operator
    if (0 === error_reporting()) {
        return false;
    }

    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
});

try {
    dns_get_record();
} catch (ErrorException $e) {
    // ...
}

An important thing to note when using your own error handler is that it will bypass the error_reporting setting and pass all errors (notifications, warnings, etc.) to your error handler.You can set a second parameter on set_error_handler() to define the type of error you want to receive, or you can access the current setting using... = error_reporting() in the error handler.

Prohibit Warning

Another possibility is to disable the call using the @ operator and then check the return value of dns_get_record().However, I recommend not to do this because errors/warnings are triggered rather than suppressed.

#4th floor

If dns_get_record() fails, it returns FALSE, so you can use @ to suppress the warning and check the return value.

#5th floor

I want to try/capture warnings, but keep the usual warning/error log records (for example, at/var/log/apache2/error.log); the handler must return false for this.However, since the "throw new..." statement essentially interrupts execution, the "wrap in function" technique, which is also discussed below, must be executed:

Is there a static method that raises an exception in php

Or, in short:

  function throwErrorException($errstr = null,$code = null, $errno = null, $errfile = null, $errline = null) {
    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
  }
  function warning_handler($errno, $errstr, $errfile, $errline, array $errcontext) {
    return false && throwErrorException($errstr, 0, $errno, $errfile, $errline);
    # error_log("AAA"); # will never run after throw
    /* Do execute PHP internal error handler */
    # return false; # will never run after throw
  }
  ...
  set_error_handler('warning_handler', E_WARNING);
  ...
  try {
    mkdir($path, 0777, true);
  } catch (Exception $e) {
    echo $e->getMessage();
    // ...
  }

Edit: On close inspection, it is found that it does not work: "return false & & throwErrorException..." will basically not throw an exception, but will simply log in to the error log; deleting the "false &&" section (such as the section in "return throwErrorException...") will cause the exception throw to work correctly, but will not log in error_log afterwards...but I'll keep this message because I haven't done so yet and haven't seen this behavior documented elsewhere.

Posted by triphis on Wed, 11 Mar 2020 21:10:30 -0700