Detect (in custom error handler) if a PHP error was actually suppressed by @
Asked Answered
C

4

12

Context:

I have three environments for an app: dev (local), test/staging (prod server), production. The app knows which is which. Error reporting on both staging and production is 0, so errors are never shown. On dev I want to see errors immediately and I want to see them where they happen, so not in some log, but in the code's result.

However, I don't want to see the errors I have explicitly suppressed with @. I've been using fsockopen and that throws a warning when it can't connect. I accept the no-connection, but don't want to see the error. Not even on dev.

Apparantly all errors go though the custom error handler, even if they were suppressed in the code.

My error handler has only 4 arguments: errno, error, file, line. From those I can't see whether the error was originally suppressed or not. If I can see that there, I can choose whether to print the error (right now I always do, if env=dev).

Any ideas? Or maybe on how to completely ignore suppressed errors (so that they don't even reach the custom error handler)?

Cheesecake answered 14/9, 2011 at 19:30 Comment(3)
Obviously technically I'm talking about notices and warnings and not errors, because errors are fatal (except recoverable fatal errors, whatever those are).Cheesecake
How would I use exceptions? PHP chooses whether to throw an error or an exception (or a warning or a notice etc). (I'm not making all php errors -> exceptions with a custom error handler.) I wish PHP used only exceptions....Cheesecake
@stereofrog to what end? I'd still have to catch the suppressed errors in the error handler and not throw exceptions all the time... I don't see the point. Not very efficient either.Cheesecake
A
22

There's a hint to this in the set_error_handler manual page.

[the error_reporting()] value will be 0 if the statement that caused the error was prepended by the @ error-control operator

When you use the error control operator @, what is happening is this:

  1. error reporting is set to 0 (no errors) - error_reporting(0)
  2. the expression is evaluated
  3. error reporting is set back to the previous value (ie turned back on)

The slightly confusing quote above refers to the fact that error_reporting returns the current setting. If you have suppressed the error with the control operator, calling error_reporting() will return 0.

Therefore, if you know you have set it to non-zero (ie you are reporting some errors) and it returns zero, you know the error was suppressed.

If you detect a suppressed error and want to know what it was, you can find it in the variable $php_errormsg (if track_errors is set to true in php.ini).

Note that the error control operator causes a lot of overhead, as it changes the error reporting level twice each time it is used. It will slow down your script.

Accountable answered 14/9, 2011 at 19:39 Comment(5)
I was about to post something similar, but you beat me to it. :) @Rudie: Also see php.net/manual/en/language.operators.errorcontrol.php#98895Superannuation
Awesome =) I didn't think it would be that simple. Very good explanation indeed.Cheesecake
Glad you like it. Best of luck mate.Accountable
@adam: Was looking in your profile and hit this post! I was looking forward for that answer! Good!Oboe
I know this answer is super old, but checking for 0 (zero) no longer works in PHP 8. See my answer on this page for how to handle it in both PHP 7 and 8.Reinforce
R
6

For PHP 8.0

So in PHP 8.0, you can no longer tell if the error/warning/notice, etc, was suppressed (using @) simply by checking if error_reporting() == 0.

Instead, it is now a fairly long piece of bitwise operators with constants, described in a warning box here: https://www.php.net/manual/en/language.operators.errorcontrol.php

Fix for both PHP 7.x and 8.x

In your custom error handler, if you want to tell if your error was suppressed with the @ sign, use something like this:

$PHP_8_SUPPRESSED = E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR | E_PARSE;
$er = error_reporting();  
    
if ($er === 0 || $er === $PHP_8_SUPPRESSED) { 
  // do code because it was suppressed.  Ex: return FALSE, etc.
} 

At the time of this writing, $PHP_8_SUPPRESSED will equal integer value 4437, but it's probably best not to hard-code that, since future versions of PHP might change these constant values.

Quick side note: I really wish they had just created a new constant called "E_SUPPRESSED_ERROR" or something like that we could check. But sadly they did not!

Reinforce answered 5/11, 2022 at 1:18 Comment(0)
B
1

A proper way to handle errors in php

  • do not use error_reporting
  • install an errors-to-exceptions handler ( http://www.php.net/manual/en/class.errorexception.php , example 1) and convert all php "errors" to exceptions
  • in your main code, use try-catch where appropriate
  • install an exception handler to catch exceptions not caught in the main code. This handler is the only place where you output and/or log errors.
  • do not use @. In a rare case you want to ignore an error, use an empty catch block
  • use some stupid trick to catch "fatal" errors. Better yet, contact php group and try to convince them to make "fatal" errors handleable in scripts.
Bibliogony answered 15/9, 2011 at 18:38 Comment(1)
LOL remove fatal errors? You know why fatal errors are fatal? Because they're unrecoverable... If you con't catch the ErrorException, all kinds of errors/warnings/notices are fatal... You want a tiny notice somewhere in your app to stop the entire flow and end up in the global try/catch? That doesn't seem necessary. I'm not convinced. I like warnings and notices. Errors should be exceptions.Cheesecake
W
0

Add this too your handler

   // error was suppressed with the @-operator
    if (0 === error_reporting()) { return false;}
Woodborer answered 27/5 at 16:19 Comment(1)
Unfortunately this does not work in PHP 8. See my answer above for handing it in both PHP 7 and 8.Reinforce

© 2022 - 2024 — McMap. All rights reserved.