Is it OK to use # when extracting a possibly missing value from a PHP array? Example:
$value = #$array['possibly_missing_key'];
The intended behavior:
if (isset($array['possibly_missing_key'])) {
$value = $array['possibly_missing_key'];
} else {
$value = null;
}
I want to know, before spreading the usage pattern.
The # operator suppresses error messages, and using it potentially sets up your code for other errors and unexpected behavior that end up hard to track down. Thus it's most certainly an antipattern.
Thus, I would very much prefer the second bit. It makes it much clearer
that it may not be present in the array, and
what the default value is if it's not present
To make it more concise you can use the ternary conditional operator ?:, as seen in Mark Baker's answer. Slightly less code and more symbols but the meaning is well-recognized.
Actually the isset variation is the anti-pattern. If you just use isset($var)?$var:NULL with the intention to suppress the "error", then you've achieved nothing over using the proper syntax for suppressing errors. It has the same outcome, yet is less readable.
People are arguing for that because of perceived "cleanliness" and because using isset is a micro optimization. Avoiding # and using isset as syntactic salt replacement is just cargo cult programming.
Or
$value = (isset($array['possibly_missing_key'])) ? $array['possibly_missing_key']: null;
Ignoring warnings is definitely an antipattern; so yes, it's an anti-pattern (and I can guarantee that if you learn to suppress warnings, one of them will come back and bite you in the posterior, if not worse).
Also, while the second version is more verbose, it gives the uninitialized variable a known state (or can be used to handle the problem, if the variable is supposed to be filled).
The third option:
$value = (isset($array['key']) ? $array['key'] : null);
I know this doesn't directly answer the question; I would have put it as a comment, except it really needed to be formatted.
The idea here is that if you're trying to make your code shorter by using a one-liner instead of an if-else block, then you can still get it into a succinct one-liner using a ternary operator, giving you the best of both worlds.
The second block of code (or Mark Baker's alternative which will work exactly the same) is better. I'm not entirely sure about PHP, but in many other programming languages, to simply ignore a variable would almost definitely throw an error. At least with the second block you are initializing the variable to some value or memory location.
Error suppression should be more commonly used if you expect a function to throw an expected error in the end-product (however, much of the time this will not be the case).
Good luck!
Dennis M.
Related
I'm pretty sure that there is no difference between this
if ($value === true)
and this
if (true === $value)
For me it always seems confusing. I used to think that it's a 'bad unconventional style of new developers'. Furthermore, my boss told me never to do such a thing.
But today I was looking through Slim's source code, written by the guy who created PHP: The Right Way, and saw this (line 341).
if (true === $value) {
$c['settings'] = array_merge_recursive($c['settings'], $name);
}
I'm sure a guy like Josh Lockhart wouldn't do something like this if it was considered a bad practice. So what's with this order? Is it some kind of an old tradition?
This is colloquially referred to as 'Yoda Conditions'. The reasoning behind their usage is because sometimes people make mistakes. In the context of an if statement, this is one such mistake that is sometimes made:
if($value = 42)
Notice there is no double equals sign (==). This is a valid statement and is not a syntax error. It is possible that it would cause huge disruptions with the code that follows it, and it is very hard to identify quickly. Yoda conditions get around this by reversing the conditional expression:
if(42 = $value)
This is not a valid statement and is a syntax error, and those are generally reported with a line number included, so they're pretty easy to find.
"Syntax error you have. Line 73 do something else you must." -- Yoda's compiler, probably
Whether or not you actually think they're good or bad is up to personal preference and style. In some cases of popular frameworks (such as Wordpress), Yoda conditions are actually part of the official coding standards. The major critique against them is that, to some, they decrease readability of the code without providing any real major benefit.
As #MonkeyZeus pointed out in a comment to the question, sometimes people do actually mean to only use a single equals sign inside of an if condition:
if($iMustHaveAValue = functionCallThatCanFail()) {
// Rest assured, $iMustHaveAValue has a truthy value.
}
Since the value of an assignment operation is the value assigned, the value that is evaluated by the if condition in this case (and in the original case which was unintended) is whatever value happened to be assigned.
I have a PHP script that uses an array of options: $opts[]
$opts can contain 0 or more key value pairs. All values are boolean.
When checking for existence of a key, and then that the value is true, I have been doing the following:
if(isset($opts['small']) && $opts['small']) $classes .= 'smallBtn';
This works, but I feel it is a bit long winded.
After having a think about it, I have come up with the following alternative:
if(#$opts['small']) $classes .= "smallBtn";
This is much smaller, but relies on the # error suppression.
My question is, which is the better way to do this?
First is long winded, but explicit in what it is doing.
Second is shorter, but may be seen as bad coding practice?
UPDATE:
The 3rd option, and in my opinion the best, is using empty:
if(!empty($opts['small'])) $classes .= 'smallBtn';
From the manual:
No warning is generated if the variable does not exist. That means empty() is essentially the concise equivalent to !isset($var) || $var == false.
Using # to suppress errors is usually a bad thing to do since it makes debugging really difficult. For example, say you make a typo in variable name (note the double dollar sign):
if(#$$opts['small']) $classes .= "smallBtn";
This will be constantly false without throwing any errors.
If you want to shorten your code, maybe just use a function, something like:
function optionIsTrue($opts, $key) {
return isset($opts[$key]) && $opts[$key] === true;
}
if (optionIsTrue($opts, "small")) {
$classes .= 'smallBtn';
}
Personally, I would prefer the first approach with respect to Clean Code guidelines and Code Readability.
I wouldn't suppress any errors whereever possible.
The proper way is to do the long-winded check so that the error never occurs. Suppressing the error fixes the symptom but does not fix the error. The following excerpt from your question is the correct answer:
if(isset($opts['small']) && $opts['small']) $classes .= 'smallBtn';
You're right-on with wanting to make code shorter and easier to read. But I've seen a lot of PHP code done by other experts and the only '#' error suppression I have ever seen in production code is one I put there myself as a quick fix. After being chastised by colleagues, I promptly put in the proper long-winded check so the error never occurred.
I have made a comment but you couldn't see the diference, here it goes:
Well, according to PEAR Coding Standards you should simply, first one is the better aproach but for the best pratice, and for best understand after looking at it, just try to read this one:
if(isset($opts['small'])
&& $opts['small']
) {
$classes .= 'smallBtn';
}
And as much as i know, you never should suppress errors
Give me one good reason to do this
if( isset($_GET['key']) && ($_GET['key'] === '123') )
{...
instead of this
if( #$_GET['key'] === '123' )
{...
I'm asking for this very specific code case, and not in general!
Following reasons are not welcome:
"using # will slow down the application by some nanoseconds because the
error is created anyway (even if it's supressed)." Well I prefer slower
code but more readable.
"using # is bad habit." It might be true in general, but I don't belive in this case (moreover bad habits might
depend on the context, on PHP manual in function like fopen they
suggest to use # in certain circumstainces, see Errors/Exceptions
at http://www.php.net/manual/en/function.fopen.php)
The performance impact isn't actually the best argument against this example and you would have to measure the performance in your own application to decide whether this is a problem. It is more likely to cause a slow down if a large number of items being checked are not set or if you placed a check such as this within a loop.
The main problem associated with using the # operator is that it is likely to become a convention in your code, so while your example may seem innocuous, you may later find yourself or your team using:
if( #IsAvailable() ) {
And the error suppression starts to hide real errors that you didn't anticipate as well as those that you did - and you have no idea what happened as you get no exception information at all.
Think about how much you could be slowing your application down when your website / app starts getting tens / hundreds of thousands (or more) of requests a day. If you're suppressing errors as a standard, you probably have dozens for every request - suddenly, you're site is noticeably slower than you would want it to be.
In addition to this, you could end up suppressing errors that you actually want to be aware of while developing.
If you don't take performance issue as argument, then it is indeed OK. But it does not has to be in all cases, because # will supress all possible errors, even those, you did not think about. But in this case, it seems there are not possible other errors that the one you want to supress.
I agree with you that preceed isset() before reading value is very ugly and I don't like to write it either. But insert # before statement seems ugly to mee to. It can decrease readibility in longer code.
Good news is, that since PHP 7 we can use much nicer way, null-coalescence operator ?? which works like this:
if($_GET['key'] ?? '' === '123' ) {}
It is basically replacement for this:
$result = isset($value) ? $value : $anotherValue;
now you can use
$result = $value ?? $anotherValue;
So I'm working on cleanup of a horrible codebase, and I'm slowly moving to full error reporting.
It's an arduous process, with hundreds of notices along the lines of:
Notice: Undefined index: incoming in /path/to/code/somescript.php on line 18
due to uses of variables assuming undefined variables will just process as false, like:
if($_SESSION['incoming']){
// do something
}
The goal is to be able to know when a incorrectly undefined variable introduced, the ability to use strict error/notice checking, as the first stage in a refactoring process that -will- eventually include rewriting of the spots of code that rely on standard input arrays in this way. There are two ways that I know of to replace a variable that may or may not be defined
in a way that suppresses notices if it isn't yet defined.
It is rather clean to just replace instances of a variable like $_REQUEST['incoming'] that are only looking for truthy values with
#$_REQUEST['incoming'].
It is quite dirty to replace instances of a variable like $_REQUEST['incoming'] with the "standard" test, which is
(isset($_REQUEST['incoming'])? $_REQUEST['incoming'] : null)
And you're adding a ternary/inline if, which is problematic because you can actually nest parens differently in complex code and totaly change the behavior.
So.... ...is there any unacceptable aspect to use of the # error suppression symbol compared to using (isset($something)? $something : null) ?
Edit: To be as clear as possible, I'm not comparing "rewriting the code to be good" to "#", that's a stage later in this process due to the added complexity of real refactoring. I'm only comparing the two ways (there may be others) that I know of to replace $undefined_variable with a non-notice-throwing version, for now.
Another option, which seems to work well with lame code that uses "superglobals" all over the place, is to wrap the globals in dedicated array objects, with more or less sensible [] behaviour:
class _myArray implements ArrayAccess, Countable, IteratorAggregate
{
function __construct($a) {
$this->a = $a;
}
// do your SPL homework here: offsetExists, offsetSet etc
function offsetGet($k) {
return isset($this->a[$k]) ? $this->a[$k] : null;
// and maybe log it or whatever
}
}
and then
$_REQUEST = new _myArray($_REQUEST);
This way you get back control over "$REQUEST" and friends, and can watch how the rest of code uses them.
You need to decide on your own if you rate the # usage acceptable or not. This is hard to rate from a third party, as one needs to know the code for that.
However, it already looks like that you don't want any error suppression to have the code more accessible for you as the programmer who needs to work with it.
You can create a specification of it in the re-factoring of the code-base you're referring to and then apply it to the code-base.
It's your decision, use the language as a tool.
You can disable the error suppression operator as well by using an own callback function for errors and warnings or by using the scream extension or via xdebug's xdebug.scream setting.
You answered you question yourself. It suppress error, does not debug it.
In my opinion you should be using the isset() method to check your variables properly.
Suppressing the error does not make it go away, it just stops it from being displayed because it essentially says "set error_reporting(0) for this line", and if I remember correctly it would be slower than checking isset() too.
And if you don't like the ternary operator then you should use the full if else statement.
It might make your code longer but it is more readable.
I would never suppress errors on a development server, but I would naturally suppress errors on a live server. If you're developing on a live server, well, you shouldn't. That means to me that the # symbol is always unacceptable. There is no reason to suppress an error in development. You should see all errors including notices.
# also slows things down a bit, but I'm not sure if isset() is faster or slower.
If it is a pain to you to write isset() so many times, I'd just write a function like
function request($arg, $default = null) {
return isset($_REQUEST[$arg]) ? trim($_REQUEST[$arg]) : $default;
}
And just use request('var') instead.
Most so-called "PHP programmers" do not understand the whole idea of assigning variables at all.
Just because of lack of any programming education or background.
Well, it isn't going a big deal with usual php script, coded with considerable efforts and consists of some HTML/Mysql spaghetti and very few variables.
Another matter is somewhat bigger code, when writing going to be relatively easy but debugging turns up a nightmare. And you are learn to value EVERY bloody error message as you come to understanding that error messages are your FRIENDS, not some irritating and disturbing things, which better to be gagged off.
So, upon this understanding you're learn to leave no intentional errors in your code.
And define all your variables as well.
And thus make error messages your friends, telling you that something gone wrong, lelping to hunt down some hard-spotting error which caused by uninitialized variable.
Another funny consequence of lack of education is that 9 out of 10 "PHP programmers" cannot distinguish error suppression from turning displaying errors off and use former in place of latter.
I've actually discovered another caveat of the # beyond the ones mentioned here that I'll have to consider, which is that when dealing with functions, or object method calls, the # could prevent an error even through the error kills the script, as per here:
http://us3.php.net/manual/en/language.operators.errorcontrol.php
Which is a pretty powerful argument of a thing to avoid in the rare situation where an attempt to suppress a variable notice suppressed a function undefined error instead (and perhaps that potential to spill over into more serious errors is another unvoiced reason that people dislike #?).
In some conditions, may I use an # character instead of using the longer isset() function?
If not, why not?
I like to use this because in a lot cases I can save several quotation marks, brackets and dots.
I assume you're talking about the error suppression operator when you say # character, but that isn't a replacement for isset().
isset() is used to determine whether or not a given variable already exists within a program, to determine if it's safe to use that variable.
What I suspect you're doing is trying to use the variable regardless of its existance, but supressing any errors that may come from that. Using the # operator at the beginning of a line tells PHP to ignore any errors and not to report it.
The # operator is shorthand for "temporarily set error_reporting(0) for this expression". isset() is a completely different construct.
You shouldn't just use an #. The # suppresses warnings. It doesn't mean the code is correct, and warnings might still get added to your log file depending on your settings. It is much better to use isset to do the check.
As far as I know # is no substitution for isset(). Its an error suppression operator which prevents displaying errors in case they do exist in the script. Its also a pretty bad habit if used in PHP code.
It technically works, but there are a few reasons why I prefer the explicit isset solution when creating output, which I assume is what you're doing:
If I'm a new developer working on your old code, I recognize the isset idiom. I know what you're trying to do. With #, it's not so easy to figure out your intention.
Suppose you want to check if an object's property is set, like $user->name. If you just use error suppression to see if name is set, you will never be notified if $user is undefined. Instead, it's better to run isset($user->name) and be explicit, so that, if $user is undefined, you will be notified of the error.
Error suppression is a bad habit overall. Error notices are good, and you should make it as easy as possible to be notified of errors. Suppressing them when it's not necessary leads to problems in the future.
It depends on what you are trying to do. For instance, if you are performing a var_dump() or other debugging and know that sometimes your value will not be set I'd say in this situation it is ok.
var_dump(#$_REQUEST['sometimesIamSet']);
If you are using it in this case:
if(#$_REQUEST['something']){
// do something
}
else{
// do something else
}
I would strongly advise against it. You should write your code to do explicitly what you want to do.
if(isset($_REQUEST['something'])){
// Hurray I know exactly what is going on!
}
else{
// Not set!
}
The only instance in production I can think about using # is when you want to throw your own error. For example
$database_connection = #db_connect_function();
if($database_connection === false){
throw new Exception("DB connection could not be made");
}
Also, look at PaulPRO's answer. If what he is saying is indeed true, your log files could also be logging warnings that you don't know about. This would result in your log files being less helpful during debugging after release.
If for no other reason, don't use # as a substitute for isset because of this:
Look at this code:
echo (#$test) ?: 'default';
If $test is 'something' then you'll get 'something'.
If $test is empty, null or doesn't exist, then you'll get 'default';
Now here's where the problem comes in:
Suppose '0' or FALSE are valid answers?
If $test is '0' or FALSE then you'll get 'default' NOT '0' as you would want.
The long-format ternary is what you should use:
echo (isset($test)) ? $test : 'default';
Not much more coding, and more reliable when it comes to dealing with arguments that can evaluate as boolean false.
the # operator also makes your code run slower, as pointed out here:
http://php.net/manual/en/language.operators.errorcontrol.php
But as it's been pointed out, the code only runs measurably slower if an error occurs. In that case, code using isset instead of # operator is much faster, as explained here:
http://seanmonstar.com/post/909029460/php-error-suppression-performance