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.
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 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.
In PHP sometimes we prepend a function call with the # operator to suppress the warnings that can be eventually shown on the page.
Assuming that the application is set up to log error and warnings to a file, when a warning is "suppressed" its correspondent entry in the logfile will be created anyway?
It is my understanding, that it won't write to the log file - after reading http://php.net/manual/en/language.operators.errorcontrol.php
It effectively temporarilly sets the error reporting level to 0, and I believe if the error reporting level is 0, errors are not written to the log
PHP5.4
php.ini:
log_errors = On
error_log = /tmp/log.txt
error_reporting=E_ALL
and now that code as:
<?php
#asdfasdfxxxasdfas();
?>
the error can't logged in /tmp/log.txt
Because you are used Error Control Operators, from PHP Manual:
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.
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...