Please tell me if this is correct. In my error handler, I need to be able to detect when the # error-control operator was used to suppress errors, because some external libraries (sadly) use it a lot. Execution of the script should continue, just like when you don't use custom error handlers.
When the at-sign is used, PHP temporarily sets error_reporting to 0. So at the start of a script we set error_reporting to any value but zero - we can now do some beautiful IF/ELSE magic. To avoid any errors being shown at the frontend, we also set display_errors to 0, this will override error_reporting (but we can still use it's value for magic).
<?php
ini_set('display_errors',0);
error_reporting(E_ALL);
function error_handler($errno, $errstr, $errfile, $errline)
{
if (error_reporting()===0) return;
else die();
}
set_error_handler('error_handler');
//This issues an error, but the handler will return and execution continues.
//Remove the at-sign and the script will die()
#file();
echo 'Execution continued, hooray.';
?>
So.. Are there no catches here? Except the one where the external library overrides my error handling.. (any tips on that?)
Considering what your script does, and some user notes on the # operator manual page, it seems what you are doing is OK.
For instance, taras says :
I was confused as to what the # symbol
actually does, and after a few
experiments have concluded the
following:
the error handler that is set gets called regardless of what level the
error reporting is set on, or whether
the statement is preceeded with #
it is up to the error handler to impart some meaning on the different
error levels. You could make your
custom error handler echo all errors,
even if error reporting is set to
NONE.
so what does the # operator do? It temporarily sets the error reporting
level to 0 for that line. If that line
triggers an error, the error handler
will still be called, but it will be
called with an error level of 0
And the set_error_handler manual page seems to confirm that :
Of particular note is that this value will be 0 if the statement
that caused the error was prepended by the # error-control operator.
Here too, there are some user notes that can be useful ; for instance, this one (see the begining of the code)
Still, if what you want is to "disable" the effect of the # operator (not sure I understood the question correctly ; this might help you anyway), to be able to get the error messages while you are on your development environment, you can install the scream extension (pecl, manual)
Provided you configure it the right way, setting this in your php.ini (after installating/loading the extension, of course) :
scream.enabled = 1
This extension will simply disable the # operator.
And here's an example (quoting the manual) :
<?php
// Make sure errors will be shown
ini_set('display_errors', true);
error_reporting(E_ALL);
// Disable scream - this is the default and produce an error
ini_set('scream.enabled', false);
echo "Opening http://example.com/not-existing-file\n";
#fopen('http://example.com/not-existing-file', 'r');
// Now enable scream and try again
ini_set('scream.enabled', true);
echo "Opening http://example.com/not-existing-file\n";
#fopen('http://example.com/another-not-existing-file', 'r');
?>
And this will output :
Opening http://example.com/not-existing-file
Opening http://example.com/not-existing-file
Warning: fopen(http://example.com/another-not-existing-file): failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found in example.php on line 14
I am not sure I would use this extension on a production server (where I never want errors displayed), but it is very useful on a development machine, when using old code, on an application/library that uses # operator extensivly...
Related
I have an external include file in my php script which triggers a Warning when executing it in the browser. So I put an # character before it and very nice, now the Warning doesn't happen anymore. My problem is that if I now execute the script on the CLI the warning is not suppressed. How can I also suppress the warning for CLI?
This is a new VPS running PHP 7.3.
#include_once('externalsourcefile.php');
Result is:
WARNING : ""continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"?" at line xx (externalsourcefile.php)
UPDATE: the point is that die include file is an external source, which I can't edit to resolve the Warning by my own.
You must refer to https://www.php.net/manual/en/function.include-once.php#84108 , It will give you an idea how include_once works. You must remove # soon as possible, bad practice!
if(include_once('externalsourcefile.php') == false) {
} else {
}
# suppresses warnings for that statement, in your case the actual include_once, not the stuff that happens inside it.
I'm writing this as an answer, because it is too long to put in a comment. Saying that # should never be used is wrong. It's using it without thinking that is wrong.
Consider this simple code being run in a busy multi-process environment:
clearstatcache(true, $pathname);
if(is_dir($pathname) && $dh=opendir($pathname)) {
// readdir() loop here
}else{
// error handling here
}
This code has a race condition. It has happened for me numerous times that the directory has disappeared between the is_dir() return and the opendir() call, leading to warnings on the console:
PHP Warning: opendir(/the/path/in/use): failed to open dir: No such file or directory in /path/to/script.php on line 2
So how could this race be worked around without disabling warnings from opendir(), I ask you?
Disabling globally is IMHO worse than doing it on specific statements like the opendir() call above.
As already said, the error is not caused by the include_once statement itself but by the code running inside the included file (you can determine that by reading carefully the error message).
It should work, anyway. There must be something else going on.
I can think of three possibilities:
The CLI interpreter in your VPS is configured to use a custom error handler that doesn't support the error suppression operator:
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.
The interpreter is configured for debugging and Xdebug has been set up with the xdebug.scream directive:
If this setting is 1, then Xdebug will disable the # (shut-up) operator so that notices, warnings and errors are no longer hidden.
The browser interpreter is running an earlier PHP version then the CLI one. The "continue" targeting switch is equivalent to "break" warning is a backwards-incompatible change in PHP/7.3. It didn't trigger a warning before.
In either case, and as also already noted, the question itself illustrates that suppressing errors can lead to hard-to-diagnose bugs.
I have:
ini_set('display_errors', 0);
ini_set('display_startup_errors', 0);
It seems to work but when I use copy() function and try to copy to directory with denied access the error is displayed on the page itself. I don't need it. But I can't intercept copy()'s errors. I tried error_get_last() and try...catch (Exception $e) but still can't intercept it? How can I disable showing copy errors on page and how to intercept them in code?
If catching the copy error is all you need to do then:
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
// Log the error somewhere without spooking the users
return true;
});
copy(...);
restore_error_handler();
This way you can "silence" only this error and keep everything else working as before.
from runtime it is better (and more reliable) to use:
error_reporting(0);
see: http://php.net/manual/en/function.error-reporting.php for more info.
also you can suppress any errors regarding of reporting level by prefixing expression with #:
#copy(...)
see: http://php.net/manual/en/language.operators.errorcontrol.php for more info, also note what this cause a performance drop and causes expression to return boolean false if error occured, regardless of its original return type.
Worth noting:
If you are using nginx+php-fpm (or similar) ini values may be overriden in php-fpm's pool configuration.
If you are using apache+mod_php ini values may be overriden in .htaccess file.
Check that, before resolving to error_reporting(0), turning off error_reporting completely is not what you should do, as this may lead to broken logic (many popular libraries and frameworks rely on error reporting by setting handlers to traslate errors to exceptions).
In terms of portability use of error suppressing operator is preferred, dispite its affect on performance.
I had an old codesetup from some other developer and I am setting up the same on my server, there I saw a line
<?php #$Days = $_POST['Days']; ?>
This code runs well on my local setup but once I uploaded it on server this did not worked and returned a network error and all the code/HTML after this code also did not work.
Although, I debugged this issue and have removed this. Also, I know that to handle the errors we use the # symbol, and also I have read this question
My query is that what was the error in the above case, why was it not shown, if I want to check the error then what shall I do.
For error reporting I shall tell you that I already used the below code
<?php
ini_set("display_errors", "1");
error_reporting(E_ALL);
?>
So please tell me why was my code unable to get past this statement, as I have around 100's of such code blocks. Is there any setting in php which could help me to get over this.
The # is the error suppression operator in PHP, have a look at the documentation.
In your example, it is used before the variable name to avoid the E_NOTICE error there. If in the $_POST array, the 'Days' key is not set it will throw an E_NOTICE message, but # is used there to avoid that E_NOTICE.
The cause of the code not working on the server is probably due to the scream.enabled directive in your php.ini configuration being disabled.
Disabling scream should fix the issue.
Change the directive in your php.ini, like so:
scream.enabled = 0
If you want to disable it during run-time, then you can use ini_set as stated in the manual:
ini_set('scream.enabled', false);
Edit
Someone in the comments pointed out I haven't been thorough enough with my answer. I will try to amend my mistake in this here edit :).
The reason scream (and disabling the #) can / will break the code is due to the fact that the variable doesn't have a value. If the remainder of the code tries to use the variable it will throw an error.
Also, an E_NOTICE can throw an error if you attach an error handler to it.
A quote from another question:
The above code will throw an ErrorException any time an E_NOTICE or
E_WARNING is raised, effectively terminating script output (if the
exception isn't caught). Throwing exceptions on PHP errors is best
combined with a parallel exception handling strategy
(set_exception_handler) to gracefully terminate in production
environments.
I am repairing some old code that was made before me and I took about 2 hours to find a error because it was hidden by #.
Is that a way to deactivate this in PHP?
display_errors don't get errors hidden by #
You can define a custom error handler as described in the php documentation for error control operator
You can enable track_errors to save whatever error was generated by the error control operator (#) to a PHP variable. You can set track_errors in your php.ini file or use ini_set.
If the track_errors feature is enabled, any error message generated by
the expression will be saved in the variable $php_errormsg. This
variable will be overwritten on each error, so check early if you want
to use it.
Example
<?php
ini_set('track_errors', true);
#strpos();
echo $php_errormsg;
Will output:
strpos() expects at least 2 parameters, 0 given
Caveats
It is important to note that critical errors that cause the script to terminate that are suppressed by # will not be discoverable this way. In this case, if your script dies unexpectedly and without an error message, that should be a good indication to search for the # in your code.
Currently the "#" error-control operator prefix will even disable
error reporting for critical errors that will terminate script
execution. Among other things, this means that if you use "#" to
suppress errors from a certain function and either it isn't available
or has been mistyped, the script will die right there with no
indication as to why.
I work on a localhost. I have php 5.4.4. I Just read on php.net that since version 5.4 onwards E_STRICT became part of E_ALL.
So, i see in my php.ini file this.
error_reporting = E_ALL
So, i assume, it is the strictest Error reporting mode. So, i make a mistake intentionally to see if this works.
I set $_SESSION key to begin with a numerical value in my code. No error is reported. The program simply runs incorrectly.
I have also set: display_errors = On
So, i assume any error shall be reported in my browser, when the script is run. But, nothing happens.
One of the Fellow, Stackoverflow.com Members, was checking my code and found out the error, because his browser reported the error. But, nothing happens in mine.
You probably have your PHP configured to not output errors to screen, rather logging them to a log file.
That is, you have display_errors set to false, and log_errors set to true .
That is the common configuration for production websites, not echoing errors to screen (which could reveal important info for an attacker) but writing them to the web server log.
I suggest you install a global error handler, and do some formatting there, to get a nice result:
set_error_handler( 'error_handler' );
function error_handler( $errno, $errmsg, $filename, $linenum, $vars )
{
// error was suppressed with the #-operator
if ( 0 === error_reporting() )
return false;
printf( "Unrecoverable error in `%s' line %s: %s\n\n", $filename, $linenum, $errmsg );
exit( $errno );
}
The problem is a bit hard to track. If you want to see the actual error message with display errors, you have to keep the following in mind:
The display_errors directive works only as long as there is some display it could output to.
Normally the session is saved on disk at a time when your script has already finished and there is no output associated any longer to it.
Therefore you can not make the following notice visible with that setting:
Notice: Unknown: Skipping numeric key 0 in Unknown on line 0
The in Unknown on line 0 is a sign that this is not the normal PHP code. It's just when the PHP engine shuts down and saves the session data.
So you can never get this error message onto your display unless:
You track the error log or use the CLI environment which still has an output in shutdown phase (STDOUT)
You use session_write_close() to write the session while your script still runs:
Notice: session_write_close(): Skipping numeric key 0 in ... on line 12
Hope this helps and clarifies it fully.