@ error suppression operator and set_error_handler
Asked Answered
A

7

25

I am following good programming practices and I am logging the PHP errors to file instead of displaying it to user. I use set_error_handler() for that.

Now the problem. For example, I have somewhere:

@file_exists('/some/file/that/is/outside/openbasedir.txt');

But despite the error suppression operator, the error message logs. I don't want that. I want suppressed errors not to pass to my error handler.

Apoplexy answered 11/9, 2011 at 19:46 Comment(14)
Avoid using @. Its a good sign, that you did something wrong and dont't want to fix it. However, the errorhandler-callback gets called, even if the statement itself is "silenced" via @.Henshaw
I think using @$a is acceptable instead of isset($a)?$a:''. The code readability overweights the "disadvantages".Apoplexy
@Rok: You'll notice that it also have some performance implications; @ hide errors, but they are still generated, formatted, error handlers are called, etc. Just structure your code so that you don't have to use isset() much.Belt
I completely agree with KingCrunch , hiding error is really BAD PRACTICE and you should stop doing itPolitico
They are not errors, they are NOTICEs.Apoplexy
And they are there for a reason. You should always display them in development and you can hide them in production (but in theory you should have nothing to hide), and this can be done via error_reporting(). The @ operator really has no place in good code.Delanie
That you think about testing your local variables with isset() tells me, that you definitely do something wrong: Intialize your local variables!Henshaw
NOTICES exist because they help to spot bugs, typos, etc. BTW you should never have to check for the existence of a variable (other than for array members).Belt
Thanks, I know all that you wrote. @KingCrunch, what about $_POST['sth'] ? How can I initialize that?Apoplexy
@Rok: Read arnauds comment: "Other than for array members". And for data coming from outside its even much more important to check it!Henshaw
I am eager to know, what kind of error file_exists() can throw? Don't you have it at hand, by chance?Jauch
Warning: file_exists(): open_basedir restriction in effect. File(/some/path) is not within the allowed path(s):, if you know how to avoid this warning without @ operator, please assist.Apoplexy
@ can occur in a third-party code. It is used in Yii2 for instance. So you can still have it even if you don't use it. @ being the bad practice is unrelated to the question.Cohleen
PHP's error handling is sadly a huge mess. I have moved away from PHP probably 2 years ago.Apoplexy
B
35

This answer applies at PHP 7:

The @ operator temporarily sets error_reporting to 0, so you can test the value of error_reporting in your error handler:

if (error_reporting() == 0)
    return;

Or even better, log only error types that are in error_reporting:

$error_reporting = error_reporting();
if ( !($error_reporting & $errno) )
    return;

Also take a look at the log_errors and error_log options, for automatically logging errors to a file or to syslog.

Belt answered 11/9, 2011 at 19:48 Comment(3)
So, basically you are saying that @ temporarily sets error_reporting to zero?Apoplexy
Didn't work for me (PHP7), but this did: if (error_reporting() == 0) { return; }Mingy
ini_get() didn't work for me on PHP 8, either. This did: if ( !(error_reporting() & $errno) )Anthonyanthophore
P
9

Solution that also works for PHP 7

According to the PHP docs:

If you have set a custom error handler function with set_error_handler() then it will still get called, but this custom error handler can (and should) call error_reporting() which will return 0 when the call that triggered the error was preceded by an @.

Source: http://php.net/manual/en/language.operators.errorcontrol.php

So you can use the following code in your error handler:

function exception_error_handler($errno, $errstr, $errfile, $errline ) {
    if (error_reporting() == 0) {
        /// @ sign temporary disabled error reporting
        return;
    }

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

set_error_handler("exception_error_handler");
Porty answered 28/12, 2018 at 11:18 Comment(2)
A follow up question: As the @ sign temporarily sets error reporting to 0, it seems there is no way from within the error handler to determine if the warning is triggered by code which has the @ prepended or if error reporting is disabled on the system. I would like to be able to do that, though, as I have a case where it makes sense to respect @ but not respect the general error reporting setting. Is there perhaps some workaround?Adscription
You could set a variable at the start of your code and use that variable to differentiate inside the // @ sign temporary disabled error reporting above. Another option is to use ini_get('error_reporting') to check the ini file, but that will only work if you adjusted the error reporting in configuration files.Porty
W
5

PHP8 behavior has changed and checking error_reporting() == 0 in the error handler no longer works. The way to check for errors suppressed with @ in PHP8 is as follows:

function errorHandler($err_severity, $err_msg, $err_file, $err_line)
{
    // Skip errors suppressed by @
    if (!(error_reporting() & $err_severity)) {
        return true;
    }

    // Error handling here
    return false;
}
Wasserman answered 21/11, 2022 at 21:49 Comment(0)
R
1

From manual:

Warning Prior to PHP 8.0.0, the error_reporting() called inside the custom error handler always returned 0 if the error was suppressed by the @ operator. As of PHP 8.0.0, it returns the value E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR | E_PARSE.

This means that solutions from other answers will not work :(

The @-operator became completely unusable imho.

To disable errors you will have to do the following:

error_reporting(0); // disable

try {
    $res = 10/0;
} catch (Throwable){
    $res = false;
}

error_reporting(-1); // enable
Rebec answered 17/5, 2022 at 9:26 Comment(3)
Well the error classes given with PHP 8.0.0 would have led to a fatal error stopping the script before PHP 8.0.0. Therefore suppressing them might have worked in sense of the operator suppressing the message but not the error. Its perhaps more the very specific example you give here for division by zero which turned into an error (and only was a warning beforehand) IIRC. Not sure about the completely unusable part there, maybe for division by zero errors only?Potiche
This helped me find the source of a bug after upgrading to PHP 8. Thanks.Witch
For PHP8 see my answer: https://mcmap.net/q/527425/-error-suppression-operator-and-set_error_handlerWasserman
M
1

PHP8 code 4437 for @

function myErrorHandler(int $errno, string  $errstr, ?string $errfile, ?int $errline) : void {
    if (error_reporting() !== 4437)
    {
      ......
    }
}

set_error_handler("myErrorHandler", E_ALL);
Maghutte answered 18/2, 2023 at 17:58 Comment(2)
Welcome to Stack Overflow! While this code may solve the question, including an explanation of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please edit your answer to add explanations and give an indication of what limitations and assumptions apply.Dipper
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Henze
S
0

You should actually avoid usage of @ operator. First of all, it is slow, and I would as far as to call it harmful.

What you should have instead is in php.ini file have two line:

error_repoting = E_ALL | E_STRICT
display_errors = Off

... or , if you do not have access to the php.ini file , then at the top of index.php (or any other bootstrap file) you should add :

error_reporting( E_ALL | E_STRICT );
ini_set('display_errors', 0);
Selfconsistent answered 11/9, 2011 at 19:57 Comment(3)
I didn't downvote but just saying to avoid @ usage doesn't answer the question. PHP sometime throws exceptions, which can be caught, sometime displays stupid notices, which cannot be caught. In that case, turning off errors for the whole application is not a reasonable solution. Silencing them on the spot is not ideal either but slightly better (especially for cases like file_exists or file_get_contents which already return false in case of error, so you don't need the notice). Ideally, PHP wouldn't be broken in the way it reports errors, but since it is we need to work with that.Saturable
You should avoid usage of @ operator, but sometimes you need to use libraries which use them.Porty
@SimonBackx there is no lack of libraries on the packagist site. You probably can find a better one. It's no 2011 anymore :DSoileau
J
0

Starting with PHP 8 - They changed error_reporting() to return the non-zero value "E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR | E_PARSE" which is 4437 when handling error control operators. This is a bit unorthodox, but keep in mind that E_WARNING and other values are not set. I consider that a "magic" constant.

See Error Control Operators for description how error control operators are handled, and Set Error Handler first example how to ignore errors that are not enabled. Here is sample code to handle both error control operators and errors that are not enabled. It is important to check for error control operator first, then filter out the errors that are not enabled.

function errorHandler($errno, $errstr, $errfile, $errline)
{
  // catch error control operator and return
  if (error_reporting() ===
    E_ERROR |
    E_CORE_ERROR |
    E_COMPILE_ERROR |
    E_USER_ERROR |
    E_RECOVERABLE_ERROR |
    E_PARSE
  ) {
    return false;
  }

  // catch errors not being flagged
  if (!(error_reporting() & $errno)) {
    return false;
  }

  ...rest of error handler logic...
}
Jodyjoe answered 28/6 at 19:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.