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.
Related
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 develop in Vagrant VM with Ubuntu 12.04 and php 5.5.7. and faced problem of incorrect error reporting handling in this case: #-sign works incorrectly.
When using custom error handlers and globally turned on error reporting, #-calls (such as #unlink('...')) should trigger custom handler but calling error_reporting() from it should return 0 in this case:
http://ca3.php.net/manual/en/language.operators.errorcontrol.php
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 #.
But in fact it returns -1!
Googling have not resulted in anything.
Have anyone any idea, how to make it work correctly? And what can cause this?
UPD
The question is not about using or not using of #-sign. Even if I don't use it, vendors of third-party packages use it. And don't tell me that this packages are bad because of it, you will be wrong. In some situations, when using # wisely, it can fix issues caused by bad design of old native php functions that produce warnings in places where they should not. But this is a hollywar and doesn't matter in question context.
The question is about making it work correctly in given environment and localization of issue source
UPD2
Problem was found, see accepted answer
From php.net, WARNING message
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.
Is equialent:
error_reporting(0);
unlink('...'); // and error reporting always return 0
error_reporting(E_ALL);
If you want to process the unlink, you must to check that file exists before unlink it
$file = 'path/to/file.txt';
if(file_exists($file))
{
unlink($file);
}
P.S. Don't use "#" - it is very bad code style and bad solution by performance. See the test with # and without it
If you're wondering what the performance impact of using the #
operator is, consider this example. Here, the second script (using
the # operator) takes 1.75x as long to execute...almost double the
time of the first script.
So while yes, there is some overhead, per iteration, we see that the #
operator added only .005 ms per call. Not reason enough, imho, to
avoid using the # operator.
Without "#"
for ($i = 0; $i < 1000000; $i++) { $undefined; }
real 0m7.617s
user 0m6.788s
sys 0m0.792s
And with "#"
for ($i = 0; $i < 1000000; $i++) { #$undefined; }
real 0m13.333s
user 0m12.437s
sys 0m0.836s
Well, I've found the problem...
Xdebug is installed, and the problem is there.
Xdebug docs say:
xdebug.scream
Type: boolean, Default value: 0, Introduced in Xdebug >= 2.1
If this setting is 1, then Xdebug will disable the # (shut-up) operator so that notices, warnings and errors are no longer hidden.
But by some reason, after installing it via apt-get install -y php5-xdebug it sets scream to 1 in /etc/php5/mods-available/xdebug.ini
The control operator is used to make all warnings/errors silent, no matter what the consequences are. I would like to use this crazy tool, but I guess I've got some strange server configuration and - even though I add # to a function, it still throws warnings/errors:
$ php -a
Interactive shell
php > $f = #file('juzio');
PHP Warning: file(juzio): failed to open stream: No such file or directory in php shell code on line 1
PHP Stack trace:
PHP 1. {main}() php shell code:0
PHP 2. file() php shell code:1
I've been trying to find a setting that is responsibe for this, but found nothing so far. Anybody knows why # isn't working for me? Just in case, I'm running PHP 5.3.6-13ubuntu3.10 with Suhosin-Patch. I've got also xdebug installed (in case it matters).
edit: please don't write about error_reporting. My question is about # operator. Thanks.
The scream.enabled directive in your php.ini configuration file will disable the effects of error suppression operator (#):
Quoting the manual:
The scream extension gives the possibility to disable the silencing error control operator so all errors are being reported. This feature is controlled by an ini setting.
See the example from the documentation to understand how it affects error reporting.
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);
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...