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.
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.
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");
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
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;
}
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);
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);
Related
Just started using Monolog to log errors in my PHP project but I want to set the minimum error reporting setting to NOTICE and above. The code Im using right now
use Monolog\ErrorHandler;
$handler = new ErrorHandler($logger);
$handler->registerErrorHandler([], false);
$handler->registerExceptionHandler();
$handler->registerFatalHandler();
Which generates all errors including NOTICES. How can I set the equvalent to
error_reporting(E_ALL & ~E_NOTICE);
using Monolog
The ErrorHandler will catch all levels of trigger_error unless you silence them by using the # operator or actually setting your error_reporting by error_reporting(E_ALL & ~E_NOTICE);
If for some reason you want to keep error reporting but at the same time filter out which of those errors are caught by the monolog error handler, I'd go with extending the error handler and registering that instead. Something like this:
class MyErrorHandler extends Monolog\ErrorHandler{
public function handleError($code, $message, $file = '', $line = 0, $context = [])
{
if($code === E_NOTICE){
return;
}
parent::handleError($code, $message, $file, $line, $context);
}
}
use MyErrorHandler as ErrorHandler;
$handler = new ErrorHandler($logger);
$handler->registerErrorHandler([], false);
$handler->registerExceptionHandler();
$handler->registerFatalHandler();
Please note that I haven't actually tested this, but I find no reason it won't work
Monolog logging levels are unrelated to PHP logging levels. PHP logging levels as set by error_reporting() control what types of things the PHP engine itself reports inside its own output. Monolog logging levels emulate unix syslog levels and are intended to be driven by the application, not by PHP. I.e., error_reporting() handles messages generated by PHP, while Monolog handles messages generated by your app. For example, within your application, when you issue a log, you indicate its severity:
if ($some_not_important_condition) {
$logger->info('foo');
}
if ($some_important_condition) {
$logger->error('bar');
}
Thus, your app is always creating info level log entries, and it's up to the run-time configuration to decide which to ingore and/or where they go. For example, you might print to the screen in development or log to Nagios in production.
TCPDF uses a lot the PHP # operator to suppress error.
As my application use a custom error handler, it still get these "suppressed" errors.
How can I make it ignore #-suppressed errors?
I thought of finding out if the error comes from TCPDF using the backtrace but the error may come from a line not using # operator.
Such a line looks like (l. 6882) for instance:
if (($imsize = #getimagesize($file)) === FALSE) {
I've asked Nicola Asuni (TCPDF creator) about this specific error and he said: "The code is working fine and the error has been suppressed on purpose".
I use PHP function set_error_handler to handle errors.
And the following: error_reporting(E_ALL); on PHP 5.4
Check for error_reporting() inside the error handler (you should read the PHP Documentation, it explains your concrete case there)
See the example (adapted from PHP DOCS):
function myErrorHandler($errno, $errstr, $errfile, $errline ) {
if (!(error_reporting() & $errno)) {
// This error code is not included in error_reporting or was called with #
return;
}
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
set_error_handler('myErrorHandler');
#strpos();
I can't manage the following: I have an error handler which catches all E_WARNINGS but I only want to handle some of the warnings all the other I want to ignore and pass them to the default PHP error handler (which also takes all the other error types except the E_WARNINGS).
Is this even possible? Look at my simple error handler:
set_error_handler(function($errno, $errstr, $errfile, $errline, $errcontext) {
if($errfile != 'somefile_for_example.php') {
// I don't care about this error throw it somewhere else!
}
echo 'error in this file handles my custom error handler';
return true;
}, E_WARNING);
PHP documentation says at: http://php.net/manual/en/function.set-error-handler.php
It is important to remember that the standard PHP error handler is
completely bypassed for the error types specified by error_types
unless the callback function returns FALSE.
Maybe then this should work:
$old_error_handler = set_error_handler(
function($errno, $errstr, $errfile, $errline, $errcontext) {
if($errfile != 'somefile_for_example.php') {
return false;
}
echo 'error in this file handles my custom error handler';
return true;
}, E_WARNING);
-
[Edit] Another user (Philipp) commented that set_error_handler returns old handler, but only for another custom one, not for the default handler.
-
In any case, when programming error handlers, one must always be extra careful with programming errors, as they cannot be handled (maybe test the functions by themselves first).
There isn't an easy solution to do this, if you really want the error handler of php.
If you call set_error_handler, you get the current error handler as return value, but only, if set_error_handler was called already. An possible solution to avoid this is to restore the error handler(restore_error_handler) trigger your own error(trigger_error) and set your own error handler again. Caveat from this is, you lost the information about error line, file and context. In addition, you can only trigger user errors, which means, you have to map each error type to an user error type.
I would suggest, to handle all errors in your custom handler - there's no real benefit in such workarounds.
If I exclude E_WARNING errors from error_reporting() - error_reporting(E_ALL & ~E_WARNING) will my custom error handler registered with set_error_handler('error_handler') be called for PHP warning errors?
I'm asking this because there is the following code in Kohana framework:
public static function error_handler($code, $error, $file = NULL, $line = NULL)
{
if (error_reporting() & $code)
{
// This error is not suppressed by current error reporting settings
// Convert the error into an ErrorException
throw new ErrorException($error, $code, 0, $file, $line);
}
// Do not execute the PHP error handler
return TRUE;
}
which checks whether the triggering the function should be processed if (error_reporting() & $code) whereas I would expect the function error_hanlder to be never triggered for errors that shouldn't be processed.
I guess I've found the answer myself (from here):
It is important to remember that the standard PHP error handler is completely bypassed for the error types specified by error_types unless the callback function returns FALSE. error_reporting() settings will have no effect and your error handler will be called regardless - however you are still able to read the current value of error_reporting and act appropriately. Of particular note is that this value will be 0 if the statement that caused the error was prepended by the # error-control operator.
error_reporting() settings will have no effect and your error handler will be called regardless
I would like to write a test using simpleTest that would fail if the method I'm testing results in a PHP E_NOTICE "undefined index : foo".
I tried expectError() and expectException() without success. The simpleTest webpage indicate that simpleTest isn't able to catch compile time PHP errors, but E_NOTICE seems to be a run time error.
Is there a way to catch such an error and makes my test fail if so ?
That wasn't really easy but I finally managed to catch the E_NOTICE error I wanted. I needed to override the current error_handler to throw an exception that I will catch in a try{} statement.
function testGotUndefinedIndex() {
// Overriding the error handler
function errorHandlerCatchUndefinedIndex($errno, $errstr, $errfile, $errline ) {
// We are only interested in one kind of error
if ($errstr=='Undefined index: bar') {
//We throw an exception that will be catched in the test
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
return false;
}
set_error_handler("errorHandlerCatchUndefinedIndex");
try {
// triggering the error
$foo = array();
echo $foo['bar'];
} catch (ErrorException $e) {
// Very important : restoring the previous error handler
restore_error_handler();
// Manually asserting that the test fails
$this->fail();
return;
}
// Very important : restoring the previous error handler
restore_error_handler();
// Manually asserting that the test succeed
$this->pass();
}
This seems a little overly complicated having to redeclare the error handler to throw an exception just to catch it. The other hard part was correctly restoring the error_handler both when an exception was catched and no error occured, otherwise it just messes with SimpleTest error handling.
There really isn't a need to catch the notice error. One could also test the outcome of 'array_key_exists' and then proceed from there.
http://www.php.net/manual/en/function.array-key-exists.php
Test for false and have it fail.
You'll never catch it within the try-catch block, luckily we have set_error_handler():
<?php
function my_handle(){}
set_error_handler("my_handle");
echo $foo["bar"];
?>
You can do anything you want inside my_handle() function, or just leave it empty to silence the notice, although, it's not recommended. A normal handler should be like this:
function myErrorHandler($errno, $errstr, $errfile, $errline)
Many solutions to handling at sign E_NOTICE errors ignore all E_NOTICE errors. To ignore just errors due to use of at signs, do this in your set_error_handler callback function:
if (error_reporting()==0 && $errno==E_NOTICE)
return; // Ignore notices for at sign
An example of an important E_NOTICE that should not be ignored is this:
$a=$b;
because $b is undefined.